?? usart_main.c
字號:
/***********************************************
**** AVR USART串口使用范例 ***
**** ***
**** 作者: HJJourAVR ***
**** 編譯器:WINAVR20050214 ***
**** ***
**** www.OurAVR.com 2005.9.10 ***
***********************************************/
/*
本程序簡單的示范了如何使用ATMEGA16的USART
USART的設置
波特率的計算
發送采用查詢方式
接收采用中斷方式
除非有特殊格式要求,否則不建議使用 printf函數庫,該函數會耗用3~6KB程序空間
這里的應用比較簡單,所以自己編寫了put_c/put_s函數。
出于簡化程序考慮,各種數據沒有對外輸出,學習時建議使用JTAG ICE硬件仿真器
*/
#include <avr/io.h>
#include <avr/delay.h>
#include <avr/signal.h>
#include <avr/interrupt.h>
/*
注: 內部函數_delay_ms() 最高延時 262.144mS@1MHz
為了使 _delay_ms()函數的延時正確,須在makefile中設定F_CPU為實際的系統時鐘頻
本范例為7.3728MHz外部石英晶體振蕩器 即 F_CPU=7372800
因為7.3728MHz能生成多種標準的通訊波特率。
如果使用其他系統時鐘頻率,注意 波特率誤差不要超過 +/-1%.
做USART通訊時,除非你掌握了校準技術,否則請不要使用內部/外部RC振蕩器
*/
//管腳定義
#define PIN_RXD 2 //PD2 RXD
#define PIN_TXD 3 //PD3 TXD
//#define LED0 0 //PB0
#define LED1 1 //PB1
#define LED2 2 //PB2
//常量定義
#define BAUDRATE 9600 //波特率
//#define F_CPU 7372800 //這個已經在makefile里面定義了
//宏定義
#define LED0_ON() PORTB|= (1<<LED1)|(1<<LED2) //輸出高電平,燈亮
#define LED0_OFF() PORTB&=~((1<<LED1)|(1<<LED2)) //輸出低電平,燈滅
#define LED1_ON() PORTB|= (1<<LED1)
#define LED1_OFF() PORTB&=~(1<<LED1)
#define LED2_ON() PORTB|= (1<<LED2)
#define LED2_OFF() PORTB&=~(1<<LED2)
//51系列的高電平輸出能力很弱,低電平也僅能點亮LED.所以常見輸出低電平才燈亮的接法。
//AVR芯片的高低驅動能力都很強,甚至能推動8字數碼管的公共極,怎么接都沒問題。
//全局變量
//如果變量會在中斷服務程序中被修改,須加volatile限定
volatile unsigned char FLAG; //按鍵標志
volatile unsigned char PC_COMMAND; //PC發出的當前命令
volatile unsigned char RX_BUFFER[16]; //存放接收數據的數組
volatile unsigned char RX_index; //存放接收數據的個數
//仿真時在watch窗口,監控這些變量。
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;
switch(PC_COMMAND)
{
case '0': //0x30 ASCII '0'
LED0_ON();
//put_s("用戶輸入0#指令");
break;
case '1':
LED1_ON();
//put_s("用戶輸入1#指令");
break;
case '2':
LED2_ON();
FLAG=!FLAG;
//put_s("用戶輸入2#指令");
break;
default:
LED0_OFF();
//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 初始化
{
//USART 9600 8, n,1 PC上位機軟件(超級終端等)也要設成同樣的設置才能通訊
UCSRC = (1<<URSEL) | 0x06;
//異步,8位數據,無奇偶校驗,一個停止位,無倍速
/*
UBRRH與UCSRC共用I/O 地址。因此訪問該地址時需注意以下問題。
寫訪問
當在該地址執行寫訪問時, USART 寄存器選擇位(URSEL)控制被寫入的寄存器。
若URSEL為0,對UBRRH值更新;若URSEL為1,對UCSRC設置更新
讀訪問
對UBRRH 或UCSRC 寄存器的讀訪問則較為復雜。但在大多數應用中,基本不需要讀這些寄存器
沒有UBRR這個16位寄存器,因為UBRRL(0x09)/UBRRH(0x20)的地址不連續,而且UBRRH跟UCSRC共用地址
*/
//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;
//也可根據數據手冊的[波特率設置的例子]查得
//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);
put_c(0x0D);
put_c(0x0A); //發送回車換行
RX_index=0; //清零
UCSRB|= (1<<RXCIE); //打開USART接收中斷
}
}*/
int main(void)
{
//上電默認DDRx=0x00,PORTx=0x00 輸入,無上拉電阻
PORTA =0xFF; //不用的管腳使能內部上拉電阻。
PORTC =0xFF;
DDRB = (1<<LED2)|(1<<LED1); //輸出
PORTB =~(1<<LED2)|(1<<LED1); //低電平,燈滅
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();
}
}
/*
程序運行效果
PC使用超級終端或SSCOM32串口調試程序,發送ASCII碼的簡單方法就是直接按下對應的按鍵
例如 字符'0',即0x30 ,按下鍵盤上的[0]即可
按下按鍵[0],LED0亮。
按下按鍵[1],LED1亮。
按下按鍵[2],LED0/1都熄滅, LED2是根據按鍵[2]的順序來亮滅,是個乒乓鍵
*/
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -