CVAVR生成的典型USART收发的接口程序

发布者:Yuexin888最新更新时间:2017-01-06 来源: eefocus关键字:CVAVR  USART收发  接口程序 手机看文章 扫描二维码
随时随地手机看文章

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

//usart.h


//常量定义

#define BAUDRATE        9600    //波特率

//#define F_CPU            4000000  //晶振频率4.0MHz


#define RXB8 1

#define TXB8 0

#define PE 2    //M16

//#define UPE 2    //M128

#define OVR 3

#define FE 4

#define UDRE 5

#define RXC 7


//宏定义

#define FRAMING_ERROR (1<

#define PARITY_ERROR (1<

//#define PARITY_ERROR (1<

#define DATA_OVERRUN (1<

#define DATA_REGISTER_EMPTY (1<

#define RX_COMPLETE (1<


// USART Receiver buffer

// 全局变量,会在中断服务程序中被修改,须加volatile限定,不要就会出错啦

#define RX_BUFFER_SIZE 16         // 接收缓冲区大小,可根据需要修改

volatile char rx_buffer[RX_BUFFER_SIZE]; // 接收缓冲区,为char型变量组成的数组,该数组构成环形队列,个数为RX_BUFFER_SIZE 

volatile unsigned char rx_wr_index,rx_rd_index,rx_counter;

// This flag is set on USART Receiver buffer overflow

volatile char rx_buffer_overflow;   //接收缓冲区溢出标志


// USART Transmitter buffer

#define TX_BUFFER_SIZE 16

volatile char tx_buffer[TX_BUFFER_SIZE];

volatile unsigned char tx_wr_index,tx_rd_index,tx_counter;


// 函数声明

char get_c(void);

void put_c(char c);

void put_s(char *ptr);

void init_USART(void);

 

//usart.c


#include

#include

#include

#include "usart.h"


/*接收中断*/

ISR(USART_RXC_vect)

{

    char status,data;

    status=UCSRA;     //读取接收状态标志位,必须先读,当读了UDR后,UCSRA便自动清零了  

    data=UDR;         //读取USART数据寄存器,这句与上句位置不能颠倒的

    if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0)     //判断本接收到的数据是否有数据帧、校验或数据溢出错误(此处指USART的硬件接收溢出)

       {

           rx_buffer[rx_wr_index]=data;   // 将数据填充到接收缓冲队列中

           if (++rx_wr_index == RX_BUFFER_SIZE)    //写指针指向下一个单元,并判断是否到了队列的尾部,(不表示接受缓冲区是否满!)

               rx_wr_index=0;  //到了尾部,则指向头部(构成环状)

           if (++rx_counter == RX_BUFFER_SIZE)     //队列中收到字符加1,并判断是否队列已满 

          {

              rx_counter=0;   // 队列满了,队列中收到字符个数为0,表示队列中所有以前的数据作废,因为最后的数据已经把最前边的数据覆盖了

              rx_buffer_overflow=1;        //置缓冲区溢出标志。在主程序中必要的地方需要判断该标志,以证明读到数据的完整性

          };

       };

}

/*接收单个字符*/

char get_c(void)

{

    char data;

    while (rx_counter==0);             //接收数据队列中没有数据可以读取,等待......(注2)

    data=rx_buffer[rx_rd_index];       //读取缓冲队列中的数据

    if (++rx_rd_index == RX_BUFFER_SIZE) rx_rd_index=0;     //读取指针指向下一个未读的数据,如果指到了队列尾部,则指回到队列头步

    cli();    // 关中断!非常重要

    --rx_counter;  //队列中未读数据个数减1。因为该变量在接收中断中要改变的,为了防止冲突,所以改动前临时关闭中断。程序相当可靠了。

    sei();    // 开中断

    return data;

}


//发送中断

ISR(USART_TXC_vect)

{

    if (tx_counter)

    {

           --tx_counter;

           UDR=tx_buffer[tx_rd_index];

           if (++tx_rd_index == TX_BUFFER_SIZE) tx_rd_index=0;

    }; 

}

/*发送单个字符*/

void put_c(char c)

{

    while (tx_counter == TX_BUFFER_SIZE);   //发送数据队列中还有数据没有发送完,等待

    cli();

    if (tx_counter || ((UCSRA & DATA_REGISTER_EMPTY)==0))    //若发送数据队列有数据或者数据寄存器UDR非空时执行(因为队列先进先出的原因,所以,c要放进非空的发送数据队列里面)

       {

           tx_buffer[tx_wr_index]=c;

           if (++tx_wr_index == TX_BUFFER_SIZE) tx_wr_index=0;

           ++tx_counter;

       }

    else

           UDR=c;

    sei();

}

/*发送字符串*/

void put_s(char *ptr)

{

    while (*ptr)

    {

        put_c(*ptr++);

    }

    put_c(0x0D);

    put_c(0x0A);  //结尾发送回车换行

}


/*USART 初始化*/

void init_USART(void)

{


    //USART 9600 8, n,1  PC上位机软件(超级终端等)也要设成同样的设置才能通讯

    UCSRC = (1<

    UBRRL= (F_CPU/BAUDRATE/16-1)%256;

    UBRRH= (F_CPU/BAUDRATE/16-1)/256;

    UCSRA = 0x00;

    //接收使能,发送使能,接收中断使能,发送中断使能

    UCSRB=(1<

}


 

/***********************************************

**** 名  称:AVR USART(RS232)低层驱动+中间层软件示例        

****                                          

**** 作  者:zhiyu                       

**** 编译器:WINAVR20070525                   

****                                         

**** 参  考:http://www.ouravr.com/bbs/bbs_content.jsp?mother_form=bbs_content.jsp&bbs_id=1000&bbs_page_no=1&bbs_sn=147242

             《高档8位单片机ATmega128原理与开发应用指南(上)》--马潮  P320

             《嵌入式C编程与ATMEL AVR》-- 国外计算机经典教材 P141

**** 日  期:2007.07.19

****

**** 芯  片:M16L

**** 时钟源:外部4M晶振

****

**** 结  果:测试成功

**** 问  题:暂无

***********************************************/

//#include

//#include

#include

#include "usart.h"


int main(void)

{

    init_USART();

    sei();    //总中断允许

    

    put_s("Hello!");

    put_s("这是一个简单的高速的串口驱动程序");

    put_s("请你输入任意的字符,单片机将返回你输入的字符");

    while (1)

    {

        put_c(get_c());

    }

}

 

//Makefile,主要的几项,只是针对我这里的程序,要灵活运用哦


MCU = atmega16


F_CPU = 4000000


TARGET = main


SRC = $(TARGET).c usart.c   //多文件编译才会用到这一项,可以参考这个帖子:


http://www.mcublog.com/blog/user1/4266/archives/2006/6145.html


*****************************************************/

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

   l.它采用两个8字节的接收和发送缓冲器来提高MCU的效率.当主程序调用getchar()函数时,按顺序执行到while (rx_counter==0)处,接收数据队列里面就没有数据,如果再没有数据输入,那么就只能死在那里等待.如果有数据输入的话,中断很快就响应,数据就会迅速地填充接收数据队列,rx_counter!=0,这个死等待也就给瓦解了,让程序执行接下来的那句data=rx_buffer[rx_rd_index]了.最后return data;,返回输入的值;如当主程序调用Putchar()发送数据时,如果UART口不空闲,就将数据放入发送缓冲器中,MCU不必等待,可以继续执行其它的工作。而UART的硬件发送完一个数据后,产生中断,由中断服务程序负责将发送缓冲器中数据依次自动送出。

C语言书本里有其中一段:

      getchar()函数(字符输入函数)的作用是从终端(或系统隐含指定的输入设备)输入一个字符.getchar()函数没有参数.当你输入一个字符时候,比如'a'后,要按'Enter'键,字符才能送到内存!你一旦按了这个'Enter',上面的程序就会执行中断响应了,

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

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

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

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

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


/*=================================================

链接: http://www.ouravr.com/bbs/bbs_content.jsp?bbs_sn=147242&bbs_page_no=1&sub_kind_id=1430&bbs_id=1000

http://www2.ouravr.com/bbs/bbs_content.jsp? mother_form=bbs_content.jsp&bbs_id=1000&bbs_page_no=1&bbs_sn=528742

http://bbs.avrvi.com/simple/index.php?t3193.html

书籍: 高档8位单片机ATmega128原理与开发应用指南(上) P320

         嵌入式C编程与Atmel AVR P141 第三章 标准I/O和预处理函数 (网上有这本电子书的下载)

下面是马潮老师的说法:

在CVAVR系统提供的标准库函数stdio.h中,提供了getchar()函数,该函数是采用轮询方式从USART接收数据的,轮询方式不仅效率低,而且会丢失数据,不能实现多任务的并行处理。

CVAVR程序向导中给出的采用中断+缓冲的方式接受数据,同PC的串口接收数据的方法一样,充分利用了AVR的高速和RAM多的优点,体现出了如何才能充分发挥AVR的特点的程序设计思想,这种思路在32位系统中也是这样的。

使用AVR的话,对软件的设计能力要求更高了,否则根本不能发挥和体现AVR的特点。许多人有了一点C的基础,就认为采用C编写单片机程序没问题,很快就会掌握AVR了,对此我只能一笑了之。看看本站上众多的代码,再看看本贴的遭遇,能说什么呢?

还有,你可以参考一下这里的链接:http://www.iccavr.com/forum/dispbbs.asp?boardID=2&ID=2249&page=1这人朋友些得不错,主要是因为:

#define RX_BUFFER_SIZE0 8 //收件箱的长度


unsigned char rx_buffer0[RX_BUFFER_SIZE0];   //收信箱

unsigned char rx_wr_index0;   //收信箱写指针

unsigned char rx_rd_index0;   //收信箱读指针

unsigned char rx_counter0;    //收信箱存量

unsigned char rx_buffer_overflow0;  //收信箱满标志位


 

这样的比喻不是很好理解了吗,你要是脑子发散一点,会不会想到操作系统里面的"管道"的概念呢,其实现在我也没具体去看这东西,不过也会有些相通的地方吧.

回到本题: 

注1: 

如果在程序的开头部分加上语句 

#define _DEBUG_TERMINAL_IO_ 

那么程序在编译时仍使用系统自己的getchar()函数,这样在软件模拟仿真时,可以从模拟的终端读取数据,便于在软件模拟环境中调试整个系统,而需要正式运行时,则把该句注释掉。 

注2: 

此处在正式应用中应根据实际情况做适当的修改。否则当主程序调用getchar()时,如果缓冲队列中没有数据,同时对方也没有发数据的情况时,程序会在此死循环。 

比较简单的办法是将这句删掉,而在调用getchar()函数前先判断rx_counter的值,为0的话就不调用了。 

或改为: 

  signed int getchar(void)  

  {  

    signed int data;  

    if (rx_counter == 0) 

    { 

        data = -1;     

    } 

    else 

    { 

        data=rx_buffer[rx_rd_index];   //读取缓冲队列中的数据  

        if (++rx_rd_index == RX_BUFFER_SIZE) rx_rd_index=0;  //读取指针指向下一个未读的数据,如果指到了队列尾部,则指回到队列头步  

        #asm("cli")      // 关中断!非常重要                                               

        --rx_counter;    //队列中未读数据个数减1。因为该变量在接收中断中要改变的,为了防止冲突,所以改动前临时关闭中断。程序相当可靠了。  

        #asm("sei")      // 开中断 

    } 

    return data;  

 

注3: 

有兴趣希望深入实在学习的网友,可将CVAVR生成的USART发送代码仔细分析以下。它的发送代码非常完美,可以马上使用。 

思考分析:

#include  


#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<

#define PARITY_ERROR (1<

#define DATA_OVERRUN (1<

#define DATA_REGISTER_EMPTY (1<

#define RX_COMPLETE (1<


// USART Transmitter buffer 

#define TX_BUFFER_SIZE 8 

char tx_buffer[TX_BUFFER_SIZE]; 


#if TX_BUFFER_SIZE<256 

unsigned char tx_wr_index,tx_rd_index,tx_counter; 

#else 

unsigned int tx_wr_index,tx_rd_index,tx_counter; 

#endif 


// USART Transmitter interrupt service routine 

interrupt [USART_TXC] void usart_tx_isr(void) 

if (tx_counter) 

   { 

   --tx_counter; 

   UDR=tx_buffer[tx_rd_index]; 

   if (++tx_rd_index == TX_BUFFER_SIZE) tx_rd_index=0; 

   }; 


#ifndef _DEBUG_TERMINAL_IO_ 

// Write a character to the USART Transmitter buffer 

#define _ALTERNATE_PUTCHAR_ 

#pragma used+ 

void putchar(char c) 

while (tx_counter == TX_BUFFER_SIZE); 

#asm("cli") 

if (tx_counter || ((UCSRA & DATA_REGISTER_EMPTY)==0)) 

   { 

   tx_buffer[tx_wr_index]=c; 

   if (++tx_wr_index == TX_BUFFER_SIZE) tx_wr_index=0; 

   ++tx_counter; 

   } 

else 

   UDR=c; 

#asm("sei") 

#pragma used- 

#endif 


// Standard Input/Output functions 

#include  


// Declare your global variables here 


void main(void) 

// Declare your local variables here 


// Input/Output Ports initialization 

// Port A initialization 

// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In  

// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T  

PORTA=0x00; 

DDRA=0x00; 


// Port B initialization 

// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In  

// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T  

PORTB=0x00; 

DDRB=0x00; 


// Port C initialization 

// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In  

// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T  

PORTC=0x00; 

DDRC=0x00; 


// Port D initialization 

// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In  

// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T  

PORTD=0x00; 

DDRD=0x00; 


// Timer/Counter 0 initialization 

// Clock source: System Clock 

// Clock value: Timer 0 Stopped 

// Mode: Normal top=FFh 

// OC0 output: Disconnected 

TCCR0=0x00; 

TCNT0=0x00; 

OCR0=0x00; 


// Timer/Counter 1 initialization 

// Clock source: System Clock 

// Clock value: Timer 1 Stopped 

// Mode: Normal top=FFFFh 

// OC1A output: Discon. 

// OC1B output: Discon. 

// Noise Canceler: Off 

// Input Capture on Falling Edge 

// Timer 1 Overflow Interrupt: Off 

// Input Capture Interrupt: Off 

// Compare A Match Interrupt: Off 

// Compare B Match Interrupt: Off 

TCCR1A=0x00; 

TCCR1B=0x00; 

TCNT1H=0x00; 

TCNT1L=0x00; 

ICR1H=0x00; 

ICR1L=0x00; 

OCR1AH=0x00; 

OCR1AL=0x00; 

OCR1BH=0x00; 

OCR1BL=0x00; 


// Timer/Counter 2 initialization 

// Clock source: System Clock 

// Clock value: Timer 2 S



关键字:CVAVR  USART收发  接口程序 引用地址:CVAVR生成的典型USART收发的接口程序

上一篇:AVR单片机外部RAM访问
下一篇:串口接收的思路

推荐阅读最新更新时间:2024-03-16 15:29

从零开始51单片机教程 —— 26 单片机键盘接口程序设计
键盘是由若干按钮组成的开关矩阵,它是单片机系统中最常用的输入设备,用户能通过键盘向计算机输入指令、地址和数据。一般单片机系统中采和非编码键盘,非编码键盘是由软件来识别键盘上的闭合键,它具有结构简单,使用灵活等特点,因此被广泛应用于单片机系统。 按钮开关的抖动问题 组成键盘的按钮有触点式和非触点式两种,单片机中应用的一般是由机械触点组成的。在下图中,当开 键盘结构图 图1 图2 关S未被按下时,P1。0输入为高电平,S闭合后,P1。0输入为低电平。由于按钮是机械触点,当机械触点断开、闭合时,会有抖动动,P1。0输入端的波形如图2所示。这种抖动对于人来说是感觉不到的,但对计算机来说,则是完全
[单片机]
从零开始51单片机教程 —— 26 单片机键盘<font color='red'>接口</font><font color='red'>程序</font>设计
128X64 LCD接口51汇编程序
; 12864接口程序(MCS51模拟口线方式) ;************************************************** ;连线图: *LCM---8031* *LCM---8031* *LCM------------8031* *LCM----------8031* ; *DB0---P1.0* *DB4---P1.4* *RS-------------P3.0* *CS1----------P3.4* ; *DB1---P1.1* *DB5---P1.5* *RW-------------P3.1* *CS2----------P3.5* ; *DB2---P
[单片机]
基于VC的USB接口通信程序设计
   0 引言   随着信息技术的迅速发展,数据采集和处理技术广泛应用于雷达、通信、遥测、遥感等领域。而在早期的计算机系统上通常使用串口或并口来发送数据,每个接口都需要占用计算机内部很多的资源,传统的接口一般采用PCI总线或RS-232串行总线。PCI总线有较高的传输速率,可达132 Mbit/s,也可以即插即用,但是它们的扩充槽有限且插拔不方便;RS-232串行总线连接比较方便,但是传输速率太慢,不易用于高速传送数据和传送大量数据。USB(通用串行总线)集中了PCI和RS-232串行总线的优点,具有方便的即插即用和热插拔特性以及较高的传输速率,因此,将USB技术应用于数据采集是非常合适的,可以达到数据采集系统的高速度处理。目前
[工业控制]
基于VC的USB<font color='red'>接口</font>通信<font color='red'>程序</font>设计
公共闪存接口CFI在FlashMemory程序设计中的应用
    摘 要: 介绍了闪速存储器(Flash   Memory)的公共闪存接口(CFI)结构,以及系统软件如何利用CFI获取Flash   Memory的各种参数,实现对各种Flash  Memory的程序设计。     关键词: 闪速存储器 公共闪存接口CFI 命令用户接口CUI     自从Intel公司于1988年推出了可快速擦写的非易失性存储器Flash   Memory以来,快速擦写存储器Flash  Memory技术就得到了非常迅速的发展。这主要是由于Flash   Memory具有不需要存储电容器、集成度更高、制造成本低于DRAM、使用方便,读写灵活、访问速度快、断电
[缓冲存储]
RS-232接口与单片机串行通信程序
单片机的串行口是非常有用的,通过他我们可以把单片机系统的数据传回电脑处理或者接受电脑传过来的数据而进行相应的动作,下面我就给大家介绍上一下电脑的RS-232接口与单片机串行通信程序设计方法,www.51hei.com上还有很多这样的文章大家去搜索下。 RS-232简介: 在串行通讯时,要求通讯双方都采用一个标准接口,使不同的设备可以方便地连接起来进行通讯.RS-232-C接口(又称EIA RS-232-C)是目前最常用的一种串行通讯接口. ("RS-232-C"中的"-C"只不过表示RS-232的版本,所以与"RS-232"简称是一样的)它是在1970年由美国电子工业协会(EIA)联合贝尔系统,调制解调器厂家及计算机终
[单片机]
RS-232<font color='red'>接口</font>与单片机串行通信<font color='red'>程序</font>
STM32模拟SPI接口程序
做开发的时候经常需要用到模拟spi接口,这种写法不错的,网上很多类似的,我也拿来学习了。 #define MOSI_H GPIO_SetBits(GPIOB, GPIO_Pin_10) #define MOSI_L GPIO_ResetBits(GPIOB, GPIO_Pin_10) #define SCLK_H GPIO_SetBits(GPIOB, GPIO_Pin_13) #define SCLK_L GPIO_ResetBits(GPIOB, GPIO_Pin_13) #define MISO GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) unsigned char SPI
[单片机]
STM32F103系列实战之通用同步异步收发器(USART)
通用同步/异步收发器(USART) STM32F103xC、 STM32F103xD和STM32F103xE增强型系列产品中,内置了3个通用同步/异步收发器(USART1、 USART2和USART3),和2个通用异步收发器(UART4和UART5)。这5个接口提供异步通信、支持IrDA SIR ENDEC传输编解码、多处理器通信模式、单线半双工通信模式和LIN主/从功能。USART1接口通信速率可达4.5兆位/秒,其他接口的通信速率可达2.25兆位/秒。USART1、 USART2和USART3接口具有硬件的CTS和RTS信号管理、兼容ISO7816的智能卡模式和类SPI通信模式,除了UART5之外所有其他接口都可以使用DM
[单片机]
STM32F103系列实战之通用同步异步<font color='red'>收发</font>器(<font color='red'>USART</font>)
用GCC写的AT24C64接口程序
// AT24C64 support functions using ATMEGA's TWI // PIN -WP is hard-wired to GND // fuctions work better outside interrupt routines // by MXH, 2003/07/30 #include DStruct.h #include avr/twi.h // CONSTANTS DEFINITION FOR EEPROM #define EEADDR 0 #define EEWR 0 #define EERD 1 // TWINT *NOT* set after STOP
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
热门活动
换一批
更多
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

更多每日新闻
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved