之前买了一块评估板,也写了相应的评测文章,链接如下:
TKM32F499高性能MCU评估板试用之万事开头难,先点个灯来压压惊!
我们在上面这篇文章已经领会了TKM32F499的强大了,接下来进入主题,串口通信实验。
1、TKM32F499通用异步收发器(UART)数据结构及参数描述
在UART库的头文件里,UART由一个结构体进行维护:
typedef struct { //波特率 uint32_t UART_BaudRate; //数据长度 uint16_t UART_WordLength; //停止位 uint16_t UART_StopBits; //校验位 uint16_t UART_Parity; //模式 uint16_t UART_Mode; //硬件流控 uint16_t UART_HardwareFlowControl; } UART_InitTypeDef;
1.1 波特率
波特率是由波特率发生器产生的,这是一个专用16位的,UART波特率寄存器控制16 位自由运转的计数器的计数周期。提供期望的波特率和 Fosc(APB 时钟频率)
X = SPBRG 寄存器值 (1 to 65535)
1.2 数据长度
/** @defgroup UART_Word_Length * @{ */ #define UART_WordLength_5b ((uint16_t)0x0000) #define UART_WordLength_6b ((uint16_t)0x0010) #define UART_WordLength_7b ((uint16_t)0x0020) #define UART_WordLength_8b ((uint16_t)0x0030)
一般在工程应用中都是选择UART_WordLength_8b
1.3 停止位
/** @defgroup UART_Stop_Bits * @{ */ #define UART_StopBits_1 ((uint16_t)0x0000) #define UART_StopBits_2 ((uint16_t)0x0004) #define IS_UART_STOPBITS(STOPBITS) (((STOPBITS) == UART_StopBits_1) || \ ((STOPBITS) == UART_StopBits_2))
一般在工程应用中都是选择UART_StopBits_1
1.4 校验位
/** @defgroup UART_Parity * @{ */ #define UART_Parity_No ((uint16_t)0x0000) #define UART_Parity_Even ((uint16_t)0x0003) #define UART_Parity_Odd ((uint16_t)0x0001) #define IS_UART_PARITY(PARITY) (((PARITY) == UART_Parity_No) || \ ((PARITY) == UART_Parity_Even) || \ ((PARITY) == UART_Parity_Odd))
一般在工程应用中都是选择UART_Parity_No(无校验)
1.5 串口模式
/** @defgroup UART_Mode * @{ */ #define UART_Mode_Rx ((uint16_t)0x0008) #define UART_Mode_Tx ((uint16_t)0x0010) #define IS_UART_MODE(MODE) ((((MODE) & (uint16_t)0xFFE7) == 0x00) && ((MODE) != (uint16_t)0x00))
这个指的是当前串口为可接收还是可发送,可以同时拥有,也可以单个选择,具体根据项目需求制定。
1.6 硬件流控
/** @defgroup UART_Hardware_Flow_Control * @{ */ #define UART_HardwareFlowControl_None ((uint16_t)0x0000) #define IS_UART_HARDWARE_FLOW_CONTROL(CONTROL)\ (((CONTROL) == UART_HardwareFlowControl_None) || \ ((CONTROL) == UART_HardwareFlowControl_RTS) || \ ((CONTROL) == UART_HardwareFlowControl_CTS) || \ ((CONTROL) == UART_HardwareFlowControl_RTS_CTS))
一般工程应用中会直接将这个设置为UART_HardwareFlowControl_None 在HAL_uart.h库文件中提供了一系列操作接口:
void UART_DeInit(UART_TypeDef* UARTx); void UART_Init(UART_TypeDef* UARTx, UART_InitTypeDef* UART_InitStruct); void UART_StructInit(UART_InitTypeDef* UART_InitStruct); void UART_Cmd(UART_TypeDef* UARTx, FunctionalState NewState); void UART_ITConfig(UART_TypeDef* UARTx, uint16_t UART_IT, FunctionalState NewState); void UART_DMACmd(UART_TypeDef* UARTx, uint16_t UART_DMAReq, FunctionalState NewState); void UART_SendData(UART_TypeDef* UARTx, uint16_t Data); uint16_t UART_ReceiveData(UART_TypeDef* UARTx); FlagStatus UART_GetFlagStatus(UART_TypeDef* UARTx, uint16_t UART_FLAG); void UART_ClearFlag(UART_TypeDef* UARTx, uint16_t UART_FLAG); ITStatus UART_GetITStatus(UART_TypeDef* UARTx, uint16_t UART_IT); void UART_ClearITPendingBit(UART_TypeDef* UARTx, uint16_t UART_IT);
跟STM32操作类似,我们直接调就完了,不明白的地方直接看数据手册解决!
2、TKM32F499通用异步收发器(UART)使用
使用串口功能之前,还是一样,先确定我这边的需求,我这边需要接收一串以下格式的数据:
序号 信号值 差值\r\n
这个数据是我手上传感器发过来的数据,然后我用TKM32F499进行接收。
1、配置一路串口用于printf输出,这里选择UART4
电路原理图如下:
根据原理图写出以下初始化函数:
/*用于调试打印*/ void Uart4Init(int BaudRate) { UART_InitTypeDef UART_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOD, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出 GPIO_Init(GPIOD, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入 GPIO_Init(GPIOD, &GPIO_InitStructure); GPIO_PinAFConfig(GPIOD, GPIO_Pin_6 | GPIO_Pin_7, GPIO_AF_UART_2345); RCC_APB2PeriphClockCmd(RCC_APB2Periph_UART4, ENABLE); UART_InitStructure.UART_BaudRate = BaudRate; //波特率 UART_InitStructure.UART_WordLength = UART_WordLength_8b;//数据位 UART_InitStructure.UART_StopBits = UART_StopBits_1;//停止位 UART_InitStructure.UART_Parity = UART_Parity_No ; UART_InitStructure.UART_Mode = UART_Mode_Rx | UART_Mode_Tx;//输入输出模式 UART_InitStructure.UART_HardwareFlowControl = UART_HardwareFlowControl_None; UART_Init(UART4, &UART_InitStructure); UART_Cmd(UART4, ENABLE); //UART 模块使能 UART_ClearITPendingBit(UART4, 0xff); }
调试不用增加接收功能,所以不需要写接收回调函数,但需要写一个串口重定向,并把微库勾上才能使用printf。
编写重定向函数:
int fputc(int ch, FILE *f) { while((UART4->CSR & 0x1) == 0) {} UART4->TDR = (u8) ch; return ch; }
2、配置一路串口用于接收传感器数据(这里选择接UART1)
电路原理图如下:
实现代码逻辑:
/*用于接收传感器数据*/ void Uart1Init(int BaudRate) { UART_InitTypeDef UART_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //uart1_tx pa9 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 推挽复用输出 GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //uart1_rx pa10 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入 GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_PinAFConfig(GPIOA, GPIO_Pin_9 | GPIO_Pin_10, GPIO_AF_UART_1); //PA9、PA10复用为串口1 GPIO_PinAFConfig(GPIOA, GPIO_Pin_15, GPIO_AF_GPIO); //PA15复用为普通GPIO RCC_APB2PeriphClockCmd(RCC_APB2Periph_UART1, ENABLE); UART_InitStructure.UART_BaudRate = BaudRate; //波特率 UART_InitStructure.UART_WordLength = UART_WordLength_8b;//数据位 UART_InitStructure.UART_StopBits = UART_StopBits_1;//停止位 UART_InitStructure.UART_Parity = UART_Parity_No ; UART_InitStructure.UART_Mode = UART_Mode_Rx | UART_Mode_Tx;//输入输出模式 UART_InitStructure.UART_HardwareFlowControl = UART_HardwareFlowControl_None; UART_Init(UART1, &UART_InitStructure); UART_Cmd(UART1, ENABLE); //UART 模块使能 UART_ClearITPendingBit(UART1, 0xff); //这里需要进行接收,所以要打开接收中断 UART_ITConfig(UART1, UART_IT_RXIEN, ENABLE);//使能接收中断 NVIC_SetPriority(UART1_IRQn, 3); NVIC_EnableIRQ(UART1_IRQn); }
定义回调函数,编写接收逻辑,传感器用一个结构体进行维护:
#define SENSOR_BUFFER_SIZE 42 typedef struct { uint8_t BufferReady; uint16_t Sensor_rx_count ; uint8_t SensorRxBuffer[SENSOR_BUFFER_SIZE]; uint8_t SensorTxBuffer[SENSOR_BUFFER_SIZE]; } Sensor_HandleTypeDef; extern Sensor_HandleTypeDef Sensor;
接收回调函数处理:
//串口1接收中断处理函数,打印传感器数据 void UART1_IRQHandler(void) { u8 ucCh; /*当ISR位1为1时,表示接收到有效字节数据*/ if(UART1->ISR & (1 << 1)) { /*接收到了一个字节的数据*/ ucCh = UART1->RDR; if('\n' != ucCh) { Sensor.SensorRxBuffer[Sensor.Sensor_rx_count++] = ucCh ; } else { /*如果接收的是\n,则上一个接收的数据为'\r'结束*/ if('\r' == Sensor.SensorRxBuffer[Sensor.Sensor_rx_count - 1]) { /*添加结束符*/ Sensor.SensorRxBuffer[Sensor.Sensor_rx_count - 1] = 0x00 ; /*接收计数清0*/ Sensor.Sensor_rx_count = 0 ; Sensor.BufferReady = 1 ; } } /*清除中断接收标志*/ UART1->ICR |= 1 << 1; } }
接收这里主要用到串口的接收数据寄存器、中断状态寄存器、中断清除寄存器
接收数据在程序里就是一个RDR的寄存器。
其中在接收逻辑里需要去判断接收数据有效,最重要的是位1:
在每次收到一次有效数据,即发生了一次串口接收中断,我们接收完数据后需要及时对中断标志进行清除,这里最重要的是第一位:
这些寄存器都是通过一个结构体进行维护:
/** * @brief Universal Synchronous Asynchronous Receiver Transmitter */ typedef struct { __IO uint32_t TDR; __IO uint32_t RDR; __IO uint32_t CSR; __IO uint32_t ISR; __IO uint32_t IER; __IO uint32_t ICR; __IO uint32_t GCR; __IO uint32_t CCR; __IO uint32_t BRR; __IO uint32_t FRABRG; } UART_TypeDef;
具体使用方法请参考TKM32F499的芯片数据手册,当然如果不习惯用寄存器进行操作,也可以使用好炬润官方的HAL lib,调用相应的库函数。串口应用逻辑编写完毕以后,接下来我们在主函数的循环内判断BufferReady标志就可以了:
while(1) { if(1 == Sensor.BufferReady) { Sensor.BufferReady = 0; /*解析传感器数据*/ Sensor_Detect_Process(Sensor.SensorRxBuffer); printf("流水号:%d 信号值:%d 差值:%d\n",sensor_data.Sensor_Serial_Number ,sensor_data.Sensor_TP1_Singal_Value,sensor_data.Sensor_TP1_Devalue ); LCD_PutString(100, 60, (char *)Sensor.SensorRxBuffer, Red, Yellow, 1); status = !status; GPIO_WriteBit(GPIOA,GPIO_Pin_15,status); } }
最终效果,这里面还有我的其它逻辑:
如果对该评估板软件编程感兴趣的话,欢迎加我微信私聊交流~
TKM32F499评估板例程及资料下载
链接:https://pan.baidu.com/s/1xujEO4vJ7i7UUK7v_fGNgw 提取码:g1y2
或者后台回复TK499即可获取。
往期精彩
推荐三个我工作中经常使用的驱动大全wiki(建议收藏并转发让更多人知道!)