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

📄 uart(cvavr源程序).txt

📁 用于高速UART通讯的实现 CVAVR源程序
💻 TXT
字号:
URAT(RS232)低层驱动+中间层软件示例

 作者:马潮老师 / 整理:armok / 2005-01-17/ www.OurAVR.com

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

#include <mega128.h> 

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

#define FRAMING_ERROR (1<<FE) 
#define PARITY_ERROR (1<<UPE) 
#define DATA_OVERRUN (1<<OVR) 
#define DATA_REGISTER_EMPTY (1<<UDRE) 
#define RX_COMPLETE (1<<RXC) 

// USART0 Receiver buffer 
#define RX_BUFFER_SIZE0 8 
char rx_buffer0[RX_BUFFER_SIZE0]; 
unsigned char rx_wr_index0,rx_rd_index0,rx_counter0; 
// This flag is set on USART0 Receiver buffer overflow 
bit rx_buffer_overflow0; 

// USART0 Receiver interrupt service routine 
#pragma savereg- 
interrupt [USART0_RXC] void uart0_rx_isr(void) 
{ 
  char status,data; 
  #asm 
  push r26 
  push r27 
  push r30 
  push r31 
  in r26,sreg 
  push r26 
  #endasm 
  status=UCSR0A; 
  data=UDR0; 
  if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0) 
    { 
      rx_buffer0[rx_wr_index0]=data; 
      if (++rx_wr_index0 == RX_BUFFER_SIZE0) rx_wr_index0=0; 
      if (++rx_counter0 == RX_BUFFER_SIZE0) 
        { 
          rx_counter0=0; 
          rx_buffer_overflow0=1; 
        }; 
     }; 
  #asm 
  pop r26 
  out sreg,r26 
  pop r31 
  pop r30 
  pop r27 
  pop r26 
  #endasm 
} 


#pragma savereg+ 

#ifndef _DEBUG_TERMINAL_IO_ 
// Get a character from the USART0 Receiver buffer 
#define _ALTERNATE_GETCHAR_ 
#pragma used+ 
char getchar(void) 
{ 
  char data; 
  while (rx_counter0==0); 
  data=rx_buffer0[rx_rd_index0]; 
  if (++rx_rd_index0 == RX_BUFFER_SIZE0) rx_rd_index0=0; 
  #asm("cli") 
  --rx_counter0; 
  #asm("sei") 
  return data; 
}
#pragma used- 
#endif 

// USART0 Transmitter buffer 
#define TX_BUFFER_SIZE0 8 
char tx_buffer0[TX_BUFFER_SIZE0]; 
unsigned char tx_wr_index0,tx_rd_index0,tx_counter0; 

// USART0 Transmitter interrupt service routine 
#pragma savereg- 
interrupt [USART0_TXC] void uart0_tx_isr(void) 
{ 
  #asm 
  push r26 
  push r27 
  push r30 
  push r31 
  in r26,sreg 
  push r26 
  #edasm 
  if (tx_counter0) 
    { 
      --tx_counter0; 
      UDR0=tx_buffer0[tx_rd_index0]; 
      if (++tx_rd_index0 == TX_BUFFER_SIZE0) tx_rd_index0=0; 
    }; 
  #asm 
  pop r26 
  out sreg,r26 
  pop r31 
  pop r30 
  pop r27 
  pop r26 
  #endasm 
} 
#pragma savereg+ 

#ifndef _DEBUG_TERMINAL_IO_ 
// Write a character to the USART0 Transmitter buffer 
#define _ALTERNATE_PUTCHAR_ 
#pragma used+ 
void putchar(char c) 
{ 
  while (tx_counter0 == TX_BUFFER_SIZE0); 
  #asm("cli") 
  if (tx_counter0 || ((UCSR0A & DATA_REGISTER_EMPTY)==0)) 
    { 
      tx_buffer0[tx_wr_index0]=c; 
      if (++tx_wr_index0 == TX_BUFFER_SIZE0) tx_wr_index0=0; 
      ++tx_counter0; 
    } 
  else 
    UDR0=c; 
  #asm("sei") 
} 
#pragma used- 
#endif 

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

// Declare your global variables here 

void main(void) 
{ 

  // USART0 initialization 
  // Communication Parameters: 8 Data, 1 Stop, No Parity 
  // USART0 Receiver: On 
  // USART0 Transmitter: On 
  // USART0 Mode: Asynchronous 
  // USART0 Baud rate: 9600 
  UCSR0A=0x00; 
  UCSR0B=0xD8; 
  UCSR0C=0x06; 
  UBRR0H=0x00; 
  UBRR0L=0x67; 

  // Global enable interrupts 
  #asm("sei") 

  while (1) 
    { 
      // Place your code here 

    }; 
} 

这段由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的接收程序进行了改动和简化,使其更加符合在本系统中使用。 

#define UART_BEGIN_STX 0xBB 
#define UART_END_STX 0xEE 
#define RX_BUFFER_SIZE0 6 

char rx_buffer0[RX_BUFFER_SIZE0]; 
unsigned char rx_counter; 
bit Uart_RecvFlag 

// USART Receiver interrupt service routine 
#pragma savereg- 
interrupt [USART_RXC] void uart_rx_isr(void) 
{ 
  unsigned char status,data; 
  #asm 
  push r26 
  push r27 
  push r30 
  push r31 
  in r26,sreg 
  push r26 
  #endasm 

  status=UCSRA; 
  data=UDR; 
  if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0) 
    { 
      if (!Uart_RecvFlag) 
        { 
          rx_buffer[rx_counter] = data; 
          switch (rx_counter) 
            { 
              case 0: 
                if (data == UART_BEGIN_STX) rx_counter = 1; 
                break; 
              case 1: 
              case 2: 
              case 3: 
              case 4: 
                rx_counter++; 
                break; 
              case 5: 
                rx_counter = 0; 
                if (data == UART_END_STX) Uart_RecvFlag = 1; 
                break; 
            } 
          } 
      } 
      else 
        rx_counter = 0; 

  #asm 
  pop r26 
  out sreg,r26 
  pop r31 
  pop r30 
  pop r27 
  pop r26 
  #endasm 
} 
#pragma savereg+ 
………… 
void main(void) 
{ 
  while(1) 
    { 
      if (Uart_RecvFlag) 
        { 
          ………… //处理收到的数据包 
          Uart_RecvFlag = 0; //允许USART接受新的数据包 
        } 
      ………… //处理其它任务 
    } 
} 

  在这段代码中,接收中断服务程序直接对数据包的起始字符和结束字符进行判断,并完成对整个数据包的接收。当接收到正确的6个字符的数据包后,将“Uart_RecvFlag”标志置位,通知上层程序处理收到的数据。一旦“Uart_RecvFlag”标志置位后,中断服务程序将不再接收新的数据(放弃掉收到的字节),使得数据缓冲区不会溢出。 
上层程序的设计,应保证以200ms左右的间隔对“Uart_RecvFlag”标志位进行一次判断。一旦判断“Uart_RecvFlag”标志置位后,马上进行处理,回送应答数据。处理完后将“Uart_RecvFlag”标志清除,允许USART接收新的数据包。 
还可以考虑在数据包中增加“数据包编号”和“数据校验”2个字节,以进一步提高通信的可靠性。 


 

⌨️ 快捷键说明

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