STM32串列埠USART
一、通用同步非同步收發器(USART)
通用同步非同步收發器(USART)提供了一種靈活的方法與使用工業標準NRZ非同步序列資料格式的 外部裝置之間進行全雙工資料交換。USART利用分數波特率發生器提供寬範圍的波特率選擇。 它支援同步單向通訊和半雙工單線通訊,也支援LIN(區域性互連網),智慧卡協議和IrDA(紅外資料 組織)SIR ENDEC規範,以及調變解調器(CTS/RTS)操作。它還允許多處理器通訊。 使用多緩衝器配置的DMA方式,可以實現高速資料通訊。
介面通過三個引腳與其他裝置連線在一起。任何USART雙向通訊至少需要兩個腳:接收資料輸入(RX)和傳送資料輸出(TX)。 RX:接收資料序列輸。通過過取樣技術來區別資料和噪音,從而恢復資料。 TX:傳送資料輸出。當傳送器被禁止時,輸出引腳恢復到它的I/O埠配置。當傳送器被啟用, 並且不傳送資料時,TX引腳處於高電平。在單線和智慧卡模式裡,此I/O口被同時用於資料的傳送和接收。
串列埠設定的一般步驟可以總結為如下幾個步驟:
1) 串列埠時鐘使能,GPIO 時鐘使能
2) 串列埠復位
3) GPIO 埠模式設定
4) 串列埠引數初始化
5) 開啟中斷並且初始化 NVIC(如果需要開啟中斷才需要這個步驟)
6) 使能串列埠
7) 編寫中斷處理函式
void uart_init(u32 bound){ //GPIO埠設定,bound:波特率 GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA時鐘 //USART1_TX GPIOA.9 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //複用推輓輸出 GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9 //USART1_RX GPIOA.10初始化 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空輸入 GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10 //Usart1 NVIC 配置 NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//搶佔優先順序3 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子優先順序3 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能 NVIC_Init(&NVIC_InitStructure); //根據指定的引數初始化VIC暫存器 //USART 初始化設定 USART_InitStructure.USART_BaudRate = bound;//串列埠波特率 USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字長為8位資料格式 USART_InitStructure.USART_StopBits = USART_StopBits_1;//一個停止位 USART_InitStructure.USART_Parity = USART_Parity_No;//無奇偶校驗位 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//無硬體資料流控制 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收發模式 USART_Init(USART1, &USART_InitStructure); //初始化串列埠1 USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//開啟串列埠接受中斷 USART_Cmd(USART1, ENABLE); //使能串列埠1 }
串列埠中斷服務函式
void USART1_IRQHandler(void) //串列埠1中斷服務程式
{
u8 Res;
#if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS為真,則需要支援OS.
OSIntEnter();
#endif
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中斷(接收到的資料必須是0x0d 0x0a結尾)
{
Res =USART_ReceiveData(USART1); //讀取接收到的資料
if((USART_RX_STA&0x8000)==0)//接收未完成,第15位
{
if(USART_RX_STA&0x4000)//接收到了0x0d,第14位
{
if(Res!=0x0a)USART_RX_STA=0;//接收錯誤,重新開始
else USART_RX_STA|=0x8000; //接收完成了
}
else //還沒收到0X0D
{
if(Res==0x0d)USART_RX_STA|=0x4000;
else
{
USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
USART_RX_STA++;
if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收資料錯誤,重新開始接收
}
}
}
}
#if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS為真,則需要支援OS.
OSIntExit();
#endif
}
#endif
主函式
int main(void)
{
u16 t;
u16 len;
u16 times=0;
delay_init(); //延時函式初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //設定NVIC中斷分組2:2位搶佔優先順序,2位響應優先順序
uart_init(115200); //串列埠初始化為115200
LED_Init(); //LED埠初始化
KEY_Init(); //初始化與按鍵連線的硬體介面
while(1)
{
if(USART_RX_STA&0x8000)
{
len=USART_RX_STA&0x3fff;//得到此次接收到的資料長度
printf("\r\n您傳送的訊息為:\r\n\r\n");
for(t=0;t<len;t++)
{
USART_SendData(USART1, USART_RX_BUF[t]);//向串列埠1傳送資料
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待發送結束
}
printf("\r\n\r\n");//插入換行
USART_RX_STA=0;
}
else
{
times++;
if(times%5000==0)
{
printf("\r\n精英STM32開發板 串列埠實驗\r\n");
printf("正點原子@ALIENTEK\r\n\r\n");
}
if(times%200==0)printf("請輸入資料,以回車鍵結束\n");
if(times%30==0)LED1=!LED1;//閃爍LED,提示系統正在執行.
delay_ms(10);
}
}
}
二、 STM32F1 的串列埠實現 485 通訊(半雙工)
485(一般稱作 RS485/EIA-485)是隸屬於 OSI 模型物理層的電氣特性規定為 2 線,半雙工, 多點通訊的標準。它的電氣特性和 RS-232 大不一樣。用纜線兩端的電壓差值來表示傳遞訊號。 RS485 僅僅規定了接受端和傳送端的電氣特性。它沒有規定或推薦任何資料協議。 RS485 的特點包括:
1) 介面電平低,不易損壞晶片。RS485 的電氣特性:邏輯“1”以兩線間的電壓差為+(2~6)V 表示;邏輯“0”以兩線間的電壓差為-(2~6)V 表示。介面訊號電平比 RS232 降低了, 不易損壞介面電路的晶片,且該電平與 TTL 電平相容,可方便與 TTL 電路連線。
2) 傳輸速率高。10 米時,RS485 的資料最高傳輸速率可達 35Mbps,在 1200m 時,傳輸 速度可達 100Kbps。
3) 抗干擾能力強。RS485 介面是採用平衡驅動器和差分接收器的組合,抗共模干擾能力 增強,即抗噪聲干擾性好。
4) 傳輸距離遠,支援節點多。RS485 匯流排最長可以傳輸 1200m 以上(速率≤100Kbps) 一般最大支援 32 個節點,如果使用特製的 485 晶片,可以達到 128 個或者 256 個節點, 最大的可以支援到 400 個節點。 RS485 推薦使用在點對點網路中,線型,匯流排型,不能是星型,環型網路。理想情況下 RS485 需要 2 個匹配電阻,其阻值要求等於傳輸電纜的特性阻抗(一般為 120Ω)。沒有特性阻抗的話, 當所有的裝置都靜止或者沒有能量的時候就會產生噪聲,而且線移需要雙端的電壓差。沒有終 接電阻的話,會使得較快速的傳送端產生多個數據信號的邊緣,導致資料傳輸出錯。
我們只需要配置好串列埠 2,就可以實現正常的 485 通訊了,串列埠 2 的配置和串列埠 1 基本類似,只是串列埠的時鐘來自 APB1,最大頻率為 36Mhz。
void RS485_Init(u32 bound)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOD, ENABLE);//使能GPIOA,D時鐘
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);//使能USART2時鐘
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; //PD7埠配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推輓輸出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //PA2
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //複用推輓
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;//PA3
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空輸入
GPIO_Init(GPIOA, &GPIO_InitStructure);
RCC_APB1PeriphResetCmd(RCC_APB1Periph_USART2,ENABLE);//復位串列埠2
RCC_APB1PeriphResetCmd(RCC_APB1Periph_USART2,DISABLE);//停止復位
#ifdef EN_USART2_RX //如果使能了接收
USART_InitStructure.USART_BaudRate = bound;//波特率設定
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8位資料長度
USART_InitStructure.USART_StopBits = USART_StopBits_1;//一個停止位
USART_InitStructure.USART_Parity = USART_Parity_No;///奇偶校驗位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//無硬體資料流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//收發模式
USART_Init(USART2, &USART_InitStructure); ; //初始化串列埠
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; //使能串列埠2中斷
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; //先佔優先順序2級
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //從優先順序2級
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中斷通道
NVIC_Init(&NVIC_InitStructure); //根據NVIC_InitStruct中指定的引數初始化外設NVIC暫存器
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//開啟中斷
USART_Cmd(USART2, ENABLE); //使能串列埠
#endif
RS485_TX_EN=0; //預設為接收模式
}
傳送資料
void RS485_Send_Data(u8 *buf,u8 len)
{
u8 t;
RS485_TX_EN=1; //設定為傳送模式
for(t=0;t<len;t++) //迴圈傳送資料
{
while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);
USART_SendData(USART2,buf[t]);
}
while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);
RS485_RX_CNT=0;
RS485_TX_EN=0; //設定為接收模式
}
接收資料
void RS485_Receive_Data(u8 *buf,u8 *len)
{
u8 rxlen=RS485_RX_CNT;
u8 i=0;
*len=0; //預設為0
delay_ms(10); //等待10ms,連續超過10ms沒有接收到一個數據,則認為接收結束
if(rxlen==RS485_RX_CNT&&rxlen)//接收到了資料,且接收完成了
{
for(i=0;i<rxlen;i++)
{
buf[i]=RS485_RX_BUF[i];
}
*len=RS485_RX_CNT; //記錄本次資料長度
RS485_RX_CNT=0; //清零
}
}
中斷函式
void USART2_IRQHandler(void)
{
u8 res;
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //接收到資料
{
res =USART_ReceiveData(USART2); //讀取接收到的資料
if(RS485_RX_CNT<64)
{
RS485_RX_BUF[RS485_RX_CNT]=res; //記錄接收到的值
RS485_RX_CNT++; //接收資料增加1
}
}
}
主函式
int main(void)
{
u8 key;
u8 i=0,t=0;
u8 cnt=0;
u8 rs485buf[5];
delay_init(); //延時函式初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//設定中斷優先順序分組為組2:2位搶佔優先順序,2位響應優先順序
uart_init(115200); //串列埠初始化為115200
LED_Init(); //初始化與LED連線的硬體介面
LCD_Init(); //初始化LCD
KEY_Init(); //按鍵初始化
RS485_Init(9600); //初始化RS485
POINT_COLOR=RED;//設定字型為紅色
LCD_ShowString(30,50,200,16,16,"ELITE STM32");
LCD_ShowString(30,70,200,16,16,"RS485 TEST");
LCD_ShowString(30,90,200,16,16,"[email protected]");
LCD_ShowString(30,110,200,16,16,"2015/1/15");
LCD_ShowString(30,130,200,16,16,"KEY0:Send"); //顯示提示資訊
POINT_COLOR=BLUE;//設定字型為藍色
LCD_ShowString(30,150,200,16,16,"Count:"); //顯示當前計數值
LCD_ShowString(30,170,200,16,16,"Send Data:"); //提示傳送的資料
LCD_ShowString(30,210,200,16,16,"Receive Data:"); //提示接收到的資料
while(1)
{
key=KEY_Scan(0);
if(key==KEY0_PRES)//KEY0按下,傳送一次資料
{
for(i=0;i<5;i++)
{
rs485buf[i]=cnt+i;//填充發送緩衝區
LCD_ShowxNum(30+i*32,190,rs485buf[i],3,16,0X80); //顯示資料
}
RS485_Send_Data(rs485buf,5);//傳送5個位元組
}
RS485_Receive_Data(rs485buf,&key);
if(key)//接收到有資料
{
if(key>5)key=5;//最大是5個數據.
for(i=0;i<key;i++)LCD_ShowxNum(30+i*32,230,rs485buf[i],3,16,0X80); //顯示資料
}
t++;
delay_ms(10);
if(t==20)
{
LED0=!LED0;//提示系統正在執行
t=0;
cnt++;
LCD_ShowxNum(30+48,150,cnt,3,16,0X80); //顯示資料
}
}
}