找回密码
 注册会员
搜索附件  
MCU资讯论坛 附件中心 单片机论坛 AVR单片机论坛 串口实验范例.rar

热门下载

附件中心&附件聚合2.0
For Discuz! X2.5 © hgcad.com

串口实验范例.rar

 

串口实验:
本程序简单的示范了如何使用ATMEGA16的USART
USART的设置
波特率的计算
发送采用查询方式
接收采用中断方式
除非有特殊格式要求,否则不建议使用 printf函数库,该函数会耗用3~6KB程序空间
这里的应用比较简单,所以自己编写了put_c/put_s函数。
出于简化程序考虑,各种数据没有对外输出,学习时建议使用JTAG ICE硬件仿真器


  1. #include <avr/io.h>
  2. #include <avr/delay.h>
  3. #include <avr/signal.h>
  4. #include <avr/interrupt.h>

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

  14. //管脚定义
  15. #define PIN_RXD   0  //PD0   RXD
  16. #define PIN_TXD   1  //PD1   TXD
  17. #define LED0   0  //PB0
  18. #define LED1   1  //PB1
  19. #define LED2   3  //PB3
  20. //常量定义
  21. #define BAUDRATE        9600 //波特率
  22. //#define F_CPU   7372800  //这个已经在makefile里面定义了

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

  32. //全局变量
  33. //如果变量会在中断服务程序中被修改,须加volatile限定
  34. volatile unsigned char FLAG;    //按键标志
  35. volatile unsigned char PC_COMMAND;  //PC发出的当前命令
  36. volatile unsigned char RX_BUFFER[16]; //存放接收数据的数组
  37. volatile unsigned char RX_index;   //存放接收数据的个数
  38. //仿真时在watch窗口,监控这些变量。
  39. void put_c(unsigned char c) //发送采用查询方式
  40. {
  41. while( !(UCSRA & (1<<UDRE)) );
  42. UDR=c;
  43. }
  44. void put_s(unsigned char *ptr)
  45. {
  46. while (*ptr)
  47. {
  48.   put_c(*ptr++);
  49. }
  50. put_c(0x0D);
  51. put_c(0x0A);  //结尾发送回车换行
  52. }

  53. SIGNAL(SIG_USART_RECV) //串口接收中断服务程序
  54. {
  55. PC_COMMAND=UDR;
  56. switch(PC_COMMAND)
  57. {
  58.   case '0': //0x30 ASCII '0'
  59.    LED0_ON();
  60.    put_s("用户输入0#指令");
  61.    break;
  62.   case '1':
  63.    LED1_ON();
  64.    put_s("用户输入1#指令");
  65.    break;
  66.   case '2':
  67.    LED0_OFF();
  68.    LED1_OFF();
  69.    FLAG=!FLAG;
  70.    put_s("用户输入2#指令");
  71.    break;
  72.   default:
  73.    put_s("用户输入的指令无效!");
  74.    break;
  75. }
  76. /*
  77.    注意,使用put_s函数发送数据需要一定的时间,如果输入数据的速度过高将会导致数据丢失
  78.    所以,一般建议中断服务程序的处理时间尽量的短,只做采集数据和设标志位,命令的处理交由主程序来完成
  79.    这里只是示范简单的命令处理
  80. */

  81. RX_BUFFER[RX_index]=PC_COMMAND;  //保存数据到数组里面
  82. RX_index++;
  83. if (RX_index>=16) RX_index=0;  //防止数组溢出
  84.   
  85. }
  86. void init_USART(void)//USART 初始化
  87. {
  88.     //USART 9600 8, n,1  PC上位机软件(超级终端等)也要设成同样的设置才能通讯
  89.     UCSRC = (1<<URSEL) | 0x06;
  90.     //异步,8位数据,无奇偶校验,一个停止位,无倍速
  91.     /*
  92.     UBRRH与UCSRC共用I/O 地址。因此访问该地址时需注意以下问题。
  93.     写访问
  94.     当在该地址执行写访问时, USART 寄存器选择位(URSEL)控制被写入的寄存器。
  95.     若URSEL为0,对UBRRH值更新;若URSEL为1,对UCSRC设置更新
  96.    
  97.     读访问
  98.     对UBRRH 或UCSRC 寄存器的读访问则较为复杂。但在大多数应用中,基本不需要读这些寄存器
  99.    
  100.    
  101.     没有UBRR这个16位寄存器,因为UBRRL(0x09)/UBRRH(0x20)的地址不连续,而且UBRRH跟UCSRC共用地址
  102.     */
  103.    
  104.     //U2X=0时的公式计算
  105.     UBRRL= (F_CPU/BAUDRATE/16-1)%256;
  106.     UBRRH= (F_CPU/BAUDRATE/16-1)/256;
  107.     //U2X=1时的公式计算
  108.     //UBRRL= (F_CPU/BAUDRATE/8-1)%256;
  109.     //UBRRH= (F_CPU/BAUDRATE/8-1)/256;
  110.     //也可根据数据手册的[波特率设置的例子]查得
  111.     //UBRRL = 0x2F; //set baud rate lo
  112.     //UBRRH = 0x00; //set baud rate hi
  113.     UCSRA = 0x00;
  114.     UCSRB = (1<<RXCIE)|(1<<RXEN)|(1<<TXEN);
  115.     //使能接收中断,使能接收,使能发送
  116. }
  117. void pro_coammand(void) //多字节命令的处理程序
  118. {
  119. unsigned char i;
  120. if (RX_index>=10)
  121. {
  122.      UCSRB&= ~(1<<RXCIE); //关断USART接收中断
  123.   put_c(0x0D);
  124.   put_c(0x0A);  //发送回车换行
  125.   put_s("Hello! 你之前输入的命令列表是:");
  126.   for (i=0;i<RX_index;i++) put_c(RX_BUFFER[i]);
  127.   put_c(0x0D);
  128.   put_c(0x0A);
  129.   put_c(0x0D);
  130.   put_c(0x0A);  //发送回车换行
  131.   RX_index=0;    //清零
  132.      UCSRB|= (1<<RXCIE); //打开USART接收中断
  133. }
  134. }
  135. int main(void)
  136. {
  137.     //上电默认DDRx=0x00,PORTx=0x00 输入,无上拉电阻
  138.     PORTA =0xFF;           //不用的管脚使能内部上拉电阻。
  139.     PORTC =0xFF;
  140.     DDRB  =  (1<<LED2)|(1<<LED1)|(1<<LED0);    //输出
  141.     PORTB =~((1<<LED2)|(1<<LED1)|(1<<LED0));    //低电平,灯灭
  142.     DDRD  =(1<<PIN_TXD);        //TXD为输出
  143.     PORTD =0xFF;
  144.     FLAG=0;
  145. init_USART();
  146. put_s("你好!");
  147. put_s("这是一个简单的串口实验程序");
  148. put_s("你可以在电脑上的超级终端程按下[0][1][2]按键,模拟用户板上的按键操作");
  149.     sei();         //使能全局中断
  150.     while (1)
  151.     {
  152.         while (FLAG==0) pro_coammand();
  153.         LED2_ON();       //如果FLAG不加volatile限定(即has_volatile=0),程序将永远都运行不到这里。
  154.         while (FLAG!=0) pro_coammand();
  155.         LED2_OFF();
  156.     }
  157. }
复制代码


程序运行效果
     PC使用超级终端或SSCOM32串口调试程序,发送ASCII码的简单方法就是直接按下对应的按键
     
     例如  字符'0',即0x30 ,按下键盘上的[0]即可
     按下按键[0],LED0亮。
     按下按键[1],LED1亮。
     按下按键[2],LED0/1都熄灭, LED2是根据按键[2]的顺序来亮灭,是个乒乓键

QQ|手机版|MCU资讯论坛 ( 京ICP备18035221号-2 )|网站地图

GMT+8, 2024-12-23 13:40 , Processed in 0.025810 second(s), 8 queries , Redis On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

返回顶部