⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 usart_main.c

📁 用avr 单片机atmega128的UART0,AVR-GCC编译的源码对AVR学习有用
💻 C
字号:
/***********************************************
****      AVR  USART串口使用范例              ***
****  	                                     ***
**** 作者:  HJJourAVR                        ***
**** 编译器:WINAVR20050214                   ***
****                                         ***
****  	www.OurAVR.com	 2005.9.10            ***
***********************************************/
/*
本程序简单的示范了如何使用ATMEGA16的USART
	USART的设置
	波特率的计算
	发送采用查询方式
	接收采用中断方式

除非有特殊格式要求,否则不建议使用 printf函数库,该函数会耗用3~6KB程序空间
这里的应用比较简单,所以自己编写了put_c/put_s函数。

出于简化程序考虑,各种数据没有对外输出,学习时建议使用JTAG ICE硬件仿真器
*/

#include <avr/io.h>
#include <avr/delay.h>
#include <avr/signal.h>
#include <avr/interrupt.h>


/*
注: 内部函数_delay_ms() 最高延时  262.144mS@1MHz
    为了使 _delay_ms()函数的延时正确,须在makefile中设定F_CPU为实际的系统时钟频
    本范例为7.3728MHz外部石英晶体振荡器 即 F_CPU=7372800
    因为7.3728MHz能生成多种标准的通讯波特率。
    
    如果使用其他系统时钟频率,注意 波特率误差不要超过 +/-1%.
    做USART通讯时,除非你掌握了校准技术,否则请不要使用内部/外部RC振荡器
*/


//管脚定义
#define PIN_RXD			0 	//PD0   RXD
#define PIN_TXD			1 	//PD1   TXD

#define LED0			0 	//PB0
#define LED1			1 	//PB1
#define LED2			3 	//PB3

//常量定义
#define BAUDRATE        9600	//波特率
//#define F_CPU			7372800  //这个已经在makefile里面定义了


//宏定义
#define LED0_ON()		PORTB|= (1<<LED0)   //输出高电平,灯亮
#define LED0_OFF()		PORTB&=~(1<<LED0)	//输出低电平,灯灭
#define LED1_ON()		PORTB|= (1<<LED1)
#define LED1_OFF()		PORTB&=~(1<<LED1)
#define LED2_ON()		PORTB|= (1<<LED2)
#define LED2_OFF()		PORTB&=~(1<<LED2)
//51系列的高电平输出能力很弱,低电平也仅能点亮LED.所以常见输出低电平才灯亮的接法。
//AVR芯片的高低驱动能力都很强,甚至能推动8字数码管的公共极,怎么接都没问题。



//全局变量
//如果变量会在中断服务程序中被修改,须加volatile限定
volatile unsigned char FLAG;				//按键标志
volatile unsigned char PC_COMMAND;		//PC发出的当前命令
volatile unsigned char RX_BUFFER[16];	//存放接收数据的数组
volatile unsigned char RX_index;			//存放接收数据的个数

//仿真时在watch窗口,监控这些变量。

void put_c(unsigned char c) //发送采用查询方式
{
	while( !(UCSRA & (1<<UDRE)) );
	UDR=c;
}

void put_s(unsigned char *ptr)
{
	while (*ptr)
	{
		put_c(*ptr++);
	}
	put_c(0x0D);
	put_c(0x0A);  //结尾发送回车换行
}


SIGNAL(SIG_USART_RECV) //串口接收中断服务程序
{
	PC_COMMAND=UDR;
	switch(PC_COMMAND)
	{
		case '0':	//0x30 ASCII '0'
			LED0_ON();
			put_s("用户输入0#指令");
			break;
		case '1':
			LED1_ON();
			put_s("用户输入1#指令");
			break;
		case '2':
			LED0_OFF();
			LED1_OFF();
			FLAG=!FLAG;
			put_s("用户输入2#指令");
			break;
		default:
			put_s("用户输入的指令无效!");
			break;
	}
	/*
	  注意,使用put_s函数发送数据需要一定的时间,如果输入数据的速度过高将会导致数据丢失
	  所以,一般建议中断服务程序的处理时间尽量的短,只做采集数据和设标志位,命令的处理交由主程序来完成
	  这里只是示范简单的命令处理
	*/
	
	RX_BUFFER[RX_index]=PC_COMMAND;		//保存数据到数组里面
	RX_index++;
	if (RX_index>=16) RX_index=0;		//防止数组溢出
		
}

void init_USART(void)//USART 初始化
{

    //USART 9600 8, n,1  PC上位机软件(超级终端等)也要设成同样的设置才能通讯
    UCSRC = (1<<URSEL) | 0x06;
    //异步,8位数据,无奇偶校验,一个停止位,无倍速
    /*
    UBRRH与UCSRC共用I/O 地址。因此访问该地址时需注意以下问题。
    写访问
    当在该地址执行写访问时, USART 寄存器选择位(URSEL)控制被写入的寄存器。
    若URSEL为0,对UBRRH值更新;若URSEL为1,对UCSRC设置更新
    
    读访问
    对UBRRH 或UCSRC 寄存器的读访问则较为复杂。但在大多数应用中,基本不需要读这些寄存器
    
    
    没有UBRR这个16位寄存器,因为UBRRL(0x09)/UBRRH(0x20)的地址不连续,而且UBRRH跟UCSRC共用地址
    */
    
    //U2X=0时的公式计算
    UBRRL= (F_CPU/BAUDRATE/16-1)%256;
    UBRRH= (F_CPU/BAUDRATE/16-1)/256;
    //U2X=1时的公式计算
    //UBRRL= (F_CPU/BAUDRATE/8-1)%256;
    //UBRRH= (F_CPU/BAUDRATE/8-1)/256;
    //也可根据数据手册的[波特率设置的例子]查得
    //UBRRL = 0x2F; //set baud rate lo
    //UBRRH = 0x00; //set baud rate hi
    UCSRA = 0x00;
    UCSRB = (1<<RXCIE)|(1<<RXEN)|(1<<TXEN);
    //使能接收中断,使能接收,使能发送
}

void pro_coammand(void) //多字节命令的处理程序
{
	unsigned char i;
	if (RX_index>=10) 
	{
    	UCSRB&= ~(1<<RXCIE);	//关断USART接收中断
		put_c(0x0D);
		put_c(0x0A);  //发送回车换行
		put_s("Hello! 你之前输入的命令列表是:");
		for (i=0;i<RX_index;i++) put_c(RX_BUFFER[i]);
		put_c(0x0D);
		put_c(0x0A);
		put_c(0x0D);
		put_c(0x0A);  //发送回车换行
		RX_index=0;				//清零
    	UCSRB|= (1<<RXCIE);	//打开USART接收中断
	}
}

int main(void)
{

    //上电默认DDRx=0x00,PORTx=0x00 输入,无上拉电阻
    PORTA =0xFF;											//不用的管脚使能内部上拉电阻。
    PORTC =0xFF;
    DDRB  =  (1<<LED2)|(1<<LED1)|(1<<LED0);				//输出
    PORTB =~((1<<LED2)|(1<<LED1)|(1<<LED0));				//低电平,灯灭
    DDRD  =(1<<PIN_TXD);								//TXD为输出
    PORTD =0xFF;

    FLAG=0;
	init_USART();
	put_s("你好!");
	put_s("这是一个简单的串口实验程序");
	put_s("你可以在电脑上的超级终端程按下[0][1][2]按键,模拟用户板上的按键操作");
    sei();									//使能全局中断
    while (1)
    {
        while (FLAG==0) pro_coammand();
        LED2_ON();							//如果FLAG不加volatile限定(即has_volatile=0),程序将永远都运行不到这里。
        while (FLAG!=0) pro_coammand();
        LED2_OFF();
    }
}

/*
程序运行效果
     PC使用超级终端或SSCOM32串口调试程序,发送ASCII码的简单方法就是直接按下对应的按键
     
     例如  字符'0',即0x30 ,按下键盘上的[0]即可

     按下按键[0],LED0亮。
     按下按键[1],LED1亮。
     按下按键[2],LED0/1都熄灭, LED2是根据按键[2]的顺序来亮灭,是个乒乓键
     
*/

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -