打印
[Atmel]

AVR USART串口使用范例

[复制链接]
643|5
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
sssha|  楼主 | 2015-5-27 16:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
编译器:WinAVR-20050214
本程序简单的示范了如何使用ATMEGA16USART
USART的设置
波特率的计算
发送采用查询方式
接收采用中断方式
*/
#include <avr/io.h>
#include <avr/delay.h>
#include <avr/signal.h>
#include <avr/interrupt.h>
/*
注: 本范例为7.3728MHz外部石英晶体振荡器 F_CPU=7372800
    因为7.3728MHz能生成多种标准的通讯波特率  
    如果使用其他系统时钟频率,注意 波特率误差不要超过 +/-1%.
    USART通讯时,除非你掌握了校准技术,否则请不要使用内部/外部RC振荡器

相关帖子

沙发
sssha|  楼主 | 2015-5-27 16:25 | 只看该作者

//管脚定义
#define PIN_RXD                        0         //PD0   RXD
#define PIN_TXD                        1         //PD1   TXD
#define LED0                            0         //PA0
#define LED1                            1         //PA1
#define LED2                            3         //PA3

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

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

使用特权

评论回复
板凳
sssha|  楼主 | 2015-5-27 16:25 | 只看该作者
//全局变量     如果变量会在中断服务程序中被修改,须加volatile限定
volatile unsigned char FLAG;                                    //按键标志
volatile unsigned char PC_COMMAND;                //PC发出的当前命令
volatile unsigned char RX_BUFFER[16];            //存放接收数据的数组
volatile unsigned char RX_index;                            //存放接收数据的个数


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; //发送数据缓冲寄存器和接收数据缓冲寄存器共享地址,即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;

使用特权

评论回复
地板
sssha|  楼主 | 2015-5-27 16:25 | 只看该作者
        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初始化
// 9600 8, n,1  PC上位机软件(超级终端等)也要设成同样的设置才能通讯
{
UCSRC = (1<<URSEL) | 0x06;
//异步,8位数据,无奇偶校验,一个停止位,无倍速,10000110
       
    /*
        UBRRH与UCSRC共用I/O 地址。因此访问该地址时需注意以下问题:
当在该地址执行写访问时,USAR寄存器选择位(URSEL)控制被写入的寄存器,若          URSEL为0,对UBRRH值更新;若URSEL为1,则对UCSRC设置更新。对UBRRH 或UCSRC 寄存器的读访问则较为复杂。但在大多数应用中,基本不需要读这些寄存器。没有UBRR这个16位寄存器,因为UBRRL(0x09)/UBRRH(0x20)的地址不连续,而且UBRRH跟UCSRC共用地址
    */

使用特权

评论回复
5
sssha|  楼主 | 2015-5-27 16:26 | 只看该作者
//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;
    //也可根据数据手册的[波特率设置的例子]查得: UBRR = 47
    //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);  //发送回车换行
                RX_index=0;                                //清零
            UCSRB|= (1<<RXCIE);        //打开USART接收中断
        }
}

使用特权

评论回复
6
sssha|  楼主 | 2015-5-27 16:26 | 只看该作者
int main(void)               
{   
  //上电默认DDRx=0x00,PORTx=0x00 输入,无上拉电阻
    PORTB =0xFF;                                                           //不用的管脚使能内部上拉电阻
    PORTC =0xFF;
    DDRA =(1<<LED2)|(1<<LED1)|(1<<LED0);                 //输出
    PORTA =((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();
    }
}

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

57

主题

556

帖子

2

粉丝