STM32串口多机通信
最近在做多节点无线通信,用到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)
{........}
从而可在其中添加从机要给主机发送数据的程序。