最近在做多節點無線通信,用到STM32 USART多機串行通信。
記錄下多機串行通信配置要點。
下面是我封裝的會用到的函數:
// .h #ifndef _MULTIUSART_H #define _MULTIUSART_H #include "sys.h" #include "stm32f10x_usart.h" /* USART_SR狀態寄存器 USART_DR數據寄存器 USART_BRR波特率寄存器 TXD-PA9 RXD-PA10 */ #define USART_REC_LEN 60 #define EN_USART1_RX 1 //使能(1)/禁止(0)串口1接收 extern u8 USART_RX_BUF[USART_REC_LEN]; //接收緩沖,最大USART_REC_LEN個字節.末字節為換行符 extern u16 USART_RX_STA; //接收狀態標記 extern void USART_InitEx(u32 bound); extern void USART_SendDataEx(USART_TypeDef* USARTx, uint16_t Data); extern void USART_SendAddr(USART_TypeDef* USARTx, uint16_t Addr); #endif
// .c #include "MultiUSART.h" //串口1中斷服務程序 //注意,讀取USARTx->SR能避免莫名其妙的錯誤 u8 USART_RX_BUF[USART_REC_LEN] = ""; //接收緩沖,最大USART_REC_LEN個字節. //接收狀態 //bit15, 接收完成標志 //bit14, 接收到0x0d //bit13~0, 接收到的有效字節數目 u16 USART_RX_STA=0; //接收狀態標記 void USART_InitEx(u32 bound) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA時鍾 USART_DeInit(USART1); //復位串口1 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //復用推挽輸出 GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空輸入 GPIO_Init(GPIOA, &GPIO_InitStructure); //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); //USART 初始化設置 USART_InitStructure.USART_BaudRate = bound; USART_InitStructure.USART_WordLength = USART_WordLength_9b; 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); //初始化串口 USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //開啟中斷 接收中斷 USART_Cmd(USART1, ENABLE); //使能串口 USART_SetAddress(USART1, 0x01); //設置USART1節點地址 //USART_WakeUpConfig(USART1, USART_WakeUp_AddressMark);//地址標記喚醒 //USART_ReceiverWakeUpCmd(USART1,ENABLE); } void USART_SendDataEx(USART_TypeDef* USARTx, uint16_t Data) { /* Check the parameters */ assert_param(IS_USART_ALL_PERIPH(USARTx)); assert_param(IS_USART_DATA(Data)); /* Transmit Data */ USARTx->DR = (Data & (uint16_t)0x01FF); //等待發送緩沖區空 //或 等待發送結束 //while((USART1->SR&0X40)==0); while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET){}; } void USART_SendAddr(USART_TypeDef* USARTx, uint16_t Addr) { /* Check the parameters */ assert_param(IS_USART_ALL_PERIPH(USARTx)); // 9位字長, 最高位為1,低四位為地址 //USARTx->DR = (1<<8) | Addr; Addr |= (uint16_t)0x0100; USARTx->DR = (Addr & (uint16_t)0x010F); } #if EN_USART1_RX void USART1_IRQHandler(void) //串口1中斷服務程序 { u8 Res; if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中斷(接收到的數據必須是0x0d 0x0a結尾) { Res =USART_ReceiveData(USART1);//(USART1->DR); //讀取接收到的數據 if((USART_RX_STA&0x8000)==0) //接收未完成 { if(USART_RX_STA&0x4000) //接收到了0x0d { 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; //接收數據錯誤,重新開始接收 } } } } } #endif
在主機端,可以寫個鍵盤控制發送地址,類似:
switch(KeyValue)
{
case 1: USART_SendAddr(USART1, 0x01); break; //從機1
case 2: USART_SendAddr(USART1, 0x02); break; //從機2
.............
然后即可自己設定通信協議分別處理從機發過來的數據了。
在從機端,由於要設為靜默模式:
USART_SetAddress(USART1, 0x01); //設置USART1節點地址 如從機1設為0x01
USART_WakeUpConfig(USART1, USART_WakeUp_AddressMark); //地址標記喚醒
USART_ReceiverWakeUpCmd(USART1,ENABLE); //使處於靜默模式
這幾句在串口初始化時要開啟。
當主機發出正確地址給從機時,下面if 語句便成立可執行。
//從機被喚醒, 接收中斷恢復正常
if((USART1->CR1 &((uint16_t)0x0002)) == 0)
{........}
從而可在其中添加從機要給主機發送數據的程序。