找回密码
 注册会员
更新自动建库工具PCB Footprint Expert 2024.04 Pro / Library Expert 破解版

URAT(RS232)低层驱动+中间层软件示例

[复制链接]
慧龙 发表于 2010-7-22 18:02:49 | 显示全部楼层 |阅读模式

本文包含原理图、PCB、源代码、封装库、中英文PDF等资源

您需要 登录 才可以下载或查看,没有账号?注册会员

×
一般教科书上提供的UART收发的程序往往是一段采用轮循(Polling)方式完成收发的简单代码。但对于高速的AVR来讲,采用这种方式大大降低了MUC的效率。在使用AVR时,应根据芯片本身的特点(片内大容量数据存储器RAM,更适合采用高级语言编写系统程序),编写高效可靠的UART收发接口(低层)程序。下面是一个典型的USART的接口程序。
  1. #include <mega128.h>

  2. #define RXB8 1
  3. #define TXB8 0
  4. #define UPE 2
  5. #define OVR 3
  6. #define FE 4
  7. #define UDRE 5
  8. #define RXC 7

  9. #define FRAMING_ERROR (1<<FE)
  10. #define PARITY_ERROR (1<<UPE)
  11. #define DATA_OVERRUN (1<<OVR)
  12. #define DATA_REGISTER_EMPTY (1<<UDRE)
  13. #define RX_COMPLETE (1<<RXC)

  14. // USART0 Receiver buffer
  15. #define RX_BUFFER_SIZE0 8
  16. char rx_buffer0[RX_BUFFER_SIZE0];
  17. unsigned char rx_wr_index0,rx_rd_index0,rx_counter0;
  18. // This flag is set on USART0 Receiver buffer overflow
  19. bit rx_buffer_overflow0;

  20. // USART0 Receiver interrupt service routine
  21. #pragma savereg-
  22. interrupt [USART0_RXC] void uart0_rx_isr(void)
  23. {
  24.   char status,data;
  25.   #asm
  26.   push r26
  27.   push r27
  28.   push r30
  29.   push r31
  30.   in r26,sreg
  31.   push r26
  32.   #endasm
  33.   status=UCSR0A;
  34.   data=UDR0;
  35.   if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0)
  36.     {
  37.       rx_buffer0[rx_wr_index0]=data;
  38.       if (++rx_wr_index0 == RX_BUFFER_SIZE0) rx_wr_index0=0;
  39.       if (++rx_counter0 == RX_BUFFER_SIZE0)
  40.         {
  41.           rx_counter0=0;
  42.           rx_buffer_overflow0=1;
  43.         };
  44.      };
  45.   #asm
  46.   pop r26
  47.   out sreg,r26
  48.   pop r31
  49.   pop r30
  50.   pop r27
  51.   pop r26
  52.   #endasm
  53. }


  54. #pragma savereg+

  55. #ifndef _DEBUG_TERMINAL_IO_
  56. // Get a character from the USART0 Receiver buffer
  57. #define _ALTERNATE_GETCHAR_
  58. #pragma used+
  59. char getchar(void)
  60. {
  61.   char data;
  62.   while (rx_counter0==0);
  63.   data=rx_buffer0[rx_rd_index0];
  64.   if (++rx_rd_index0 == RX_BUFFER_SIZE0) rx_rd_index0=0;
  65.   #asm("cli")
  66.   --rx_counter0;
  67.   #asm("sei")
  68.   return data;
  69. }
  70. #pragma used-
  71. #endif

  72. // USART0 Transmitter buffer
  73. #define TX_BUFFER_SIZE0 8
  74. char tx_buffer0[TX_BUFFER_SIZE0];
  75. unsigned char tx_wr_index0,tx_rd_index0,tx_counter0;

  76. // USART0 Transmitter interrupt service routine
  77. #pragma savereg-
  78. interrupt [USART0_TXC] void uart0_tx_isr(void)
  79. {
  80.   #asm
  81.   push r26
  82.   push r27
  83.   push r30
  84.   push r31
  85.   in r26,sreg
  86.   push r26
  87.   #edasm
  88.   if (tx_counter0)
  89.     {
  90.       --tx_counter0;
  91.       UDR0=tx_buffer0[tx_rd_index0];
  92.       if (++tx_rd_index0 == TX_BUFFER_SIZE0) tx_rd_index0=0;
  93.     };
  94.   #asm
  95.   pop r26
  96.   out sreg,r26
  97.   pop r31
  98.   pop r30
  99.   pop r27
  100.   pop r26
  101.   #endasm
  102. }
  103. #pragma savereg+

  104. #ifndef _DEBUG_TERMINAL_IO_
  105. // Write a character to the USART0 Transmitter buffer
  106. #define _ALTERNATE_PUTCHAR_
  107. #pragma used+
  108. void putchar(char c)
  109. {
  110.   while (tx_counter0 == TX_BUFFER_SIZE0);
  111.   #asm("cli")
  112.   if (tx_counter0 || ((UCSR0A & DATA_REGISTER_EMPTY)==0))
  113.     {
  114.       tx_buffer0[tx_wr_index0]=c;
  115.       if (++tx_wr_index0 == TX_BUFFER_SIZE0) tx_wr_index0=0;
  116.       ++tx_counter0;
  117.     }
  118.   else
  119.     UDR0=c;
  120.   #asm("sei")
  121. }
  122. #pragma used-
  123. #endif

  124. // Standard Input/Output functions
  125. #include <stdio.h>

  126. // Declare your global variables here

  127. void main(void)
  128. {

  129.   // USART0 initialization
  130.   // Communication Parameters: 8 Data, 1 Stop, No Parity
  131.   // USART0 Receiver: On
  132.   // USART0 Transmitter: On
  133.   // USART0 Mode: Asynchronous
  134.   // USART0 Baud rate: 9600
  135.   UCSR0A=0x00;
  136.   UCSR0B=0xD8;
  137.   UCSR0C=0x06;
  138.   UBRR0H=0x00;
  139.   UBRR0L=0x67;

  140.   // Global enable interrupts
  141.   #asm("sei")

  142.   while (1)
  143.     {
  144.       // Place your code here

  145.     };
  146. }

复制代码
这段由CVAVR程序生成器产生的UART接口代码是一个非常好的、高效可靠,并且值得认真学习和体会的。其特点如下:

l.它采用两个8字节的接收和发送缓冲器来提高MCU的效率,如当主程序调用Putchar()发送数据时,如果UART口不空闲,就将数据放入发送缓冲器中,MCU不必等待,可以继续执行其它的工作。而UART的硬件发送完一个数据后,产生中断,由中断服务程序负责将发送缓冲器中数据依次送出。

2.数据缓冲器结构是一个线性的循环队列,由读、写和队列计数器3个指针控制,用于判断队列是否空、溢出,以及当前数据在队列中的位置。

3.用编译控制命令#pragma savereg-和#pragma savereg+,使得由CVAVR在生成的中断服务程序中不进行中断保护(CVAVR生成中断保护会将比较多的寄存器压入堆栈中),而在中断中嵌入汇编,只将5个在本中断中必须要保护的寄存器压栈。这样提高了UART中断处理的速度,也意味着提高了MCU的效率。

4.由于在接口程序Putchar()、Getchar()和中断服务程序中都要对数据缓冲器的读、写和队列计数器3个指针判断和操作,为了防止冲突,在Putchar()、Getchar()中对3个指针操作时临时将中断关闭,提高了程序的可靠性。


   建议读者能逐字逐句地仔细分析该段代码,真正理解和领会每一句语句(包括编译控制命令的作用)的作用,从中体会和学习如何编写效率高,可靠性好,结构优良的系统代码。这段程序使用的方法和技巧,对编写SPI、I2C的串行通信接口程序都是非常好的借鉴。

  作为现在的单片机和嵌入式系统的工程师,不仅要深入全面的掌握芯片和各种器件的性能,具备丰富的硬件设计能力;同时也必须提高软件的设计能力。要学习和掌握有关数据结构、操作系统、软件工程、网络协议等方面的知识,具有设计编写大的复杂系统程序的能力。

USART应用实例

  使用ATmega128实现一个工业设备的主控制板,它与由ATmega8管理的按键和LED显示构成的控制面板距离在2米左右,两者之间采用USART通信联系。考虑到在实际应用中,俩者之间交换的数据很少,通信速度也不需要很高,重要的是保证通信的可靠和抗干扰,因此在硬件设计上采用电流环的连接方式,见图5.4。

  在图中通信双方采用光隔和三极管,将USART的电平变化变成电流变化后传送连接,如同工业上使用的20mA电流环通信一样,大大提高了通信的抗干扰能力。

通信协议和规程的制定:

l.通信速率采用2400bps(速率太高时电流环的变化会跟不上)。

2. 用户数据包采用定长格式,每个数据包长度为6个字节,其中第1个字节是数据包起始字节0xBB,第6字节为数据包结束字节0xEE,其它为用户命令、数据和系统状态参数。

3.每次通信由A端发起,下发一个数据包;B端收到一个正确的数据包后,必须返回一个数据包应答。

4.A端下发一个数据包后,在300ms内没有正确收到应答包时(在2400bps时传送6个字节的时间约为30ms),将再次重发;3次重发均不能正确收到应答包则报警。

5.在系统正常工作时,A端每隔250ms下发一个数据包,B端如果在1s内没有正确收到一个下发的数据包,将进入安全保护程序。

  在这个应用实例中,USART接口的发送程序与前面给出的典型例程中的一样,而对USART的接收程序进行了改动和简化,使其更加符合在本系统中使用。
  1. #define UART_BEGIN_STX 0xBB
  2. #define UART_END_STX 0xEE
  3. #define RX_BUFFER_SIZE0 6

  4. char rx_buffer0[RX_BUFFER_SIZE0];
  5. unsigned char rx_counter;
  6. bit Uart_RecvFlag

  7. // USART Receiver interrupt service routine
  8. #pragma savereg-
  9. interrupt [USART_RXC] void uart_rx_isr(void)
  10. {
  11.   unsigned char status,data;
  12.   #asm
  13.   push r26
  14.   push r27
  15.   push r30
  16.   push r31
  17.   in r26,sreg
  18.   push r26
  19.   #endasm

  20.   status=UCSRA;
  21.   data=UDR;
  22.   if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0)
  23.     {
  24.       if (!Uart_RecvFlag)
  25.         {
  26.           rx_buffer[rx_counter] = data;
  27.           switch (rx_counter)
  28.             {
  29.               case 0:
  30.                 if (data == UART_BEGIN_STX) rx_counter = 1;
  31.                 break;
  32.               case 1:
  33.               case 2:
  34.               case 3:
  35.               case 4:
  36.                 rx_counter++;
  37.                 break;
  38.               case 5:
  39.                 rx_counter = 0;
  40.                 if (data == UART_END_STX) Uart_RecvFlag = 1;
  41.                 break;
  42.             }
  43.           }
  44.       }
  45.       else
  46.         rx_counter = 0;

  47.   #asm
  48.   pop r26
  49.   out sreg,r26
  50.   pop r31
  51.   pop r30
  52.   pop r27
  53.   pop r26
  54.   #endasm
  55. }
  56. #pragma savereg+
  57. …………
  58. void main(void)
  59. {
  60.   while(1)
  61.     {
  62.       if (Uart_RecvFlag)
  63.         {
  64.           ………… //处理收到的数据包
  65.           Uart_RecvFlag = 0; //允许USART接受新的数据包
  66.         }
  67.       ………… //处理其它任务
  68.     }
  69. }

复制代码
在这段代码中,接收中断服务程序直接对数据包的起始字符和结束字符进行判断,并完成对整个数据包的接收。当接收到正确的6个字符的数据包后,将“Uart_RecvFlag”标志置位,通知上层程序处理收到的数据。一旦“Uart_RecvFlag”标志置位后,中断服务程序将不再接收新的数据(放弃掉收到的字节),使得数据缓冲区不会溢出。
上层程序的设计,应保证以200ms左右的间隔对“Uart_RecvFlag”标志位进行一次判断。一旦判断“Uart_RecvFlag”标志置位后,马上进行处理,回送应答数据。处理完后将“Uart_RecvFlag”标志清除,允许USART接收新的数据包。
还可以考虑在数据包中增加“数据包编号”和“数据校验”2个字节,以进一步提高通信的可靠性。
xiajiagao 发表于 2011-4-6 20:34:27 | 显示全部楼层
学习一下!不错的程序
*滑块验证:
您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

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

GMT+8, 2024-12-23 14:05 , Processed in 0.055438 second(s), 12 queries , Redis On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表