STM32实验:串口接受和发送消息
在STM32里,USART负责进行串口通信。STM32可以通过串口和其他设备进行传输并行数据,是全双工、异步时钟控制,设备之间进行的是点对点的数据传输。对应的STM32引脚分别是RX(接收端)和TX(发送端)。STM32的USART串口资源有USART1、USART2、USART3。
串口有以下几个几个重要的参数:
1、波特率,串口通信的速率。
2、空闲,即没有信号传输的时候的电平位,一般为高电平。
3、起始位,标志一个数据帧的开始,固定为低电平。当数据开始发送时,产生一个下降沿。(空闲位为高电平,当条约到起始位的低电平,会产生一个下降沿触发信号)
4、数据位,发送数据帧,1为高电平,0为低电平。低位先行。
比如 发送数据帧0x0F 在数据帧里就是低位线性 即 1111 0000
5、校验位,用于数据验证,根据数据位的计算得来。有奇校验,偶校验和无校验。
如果是采用奇校验,在传送每一个字节的时候另外附加一位作为校验位,当实际数据中“1”的个数为偶数的时候,这个校验位就是“1”,否则这个校验位就是“0”,这样就可以保证传送数据满足奇校验的要求。在接收方收到数据时,将按照奇校验的要求检测数据中“1”的个数,如果是奇数,表示传送正确,否则表示传送错误。同理偶校验的过程和奇校验的过程一样,只是检测数据中“1”的个数为偶数。
如0100101偶校验码就是10100101,当实际数据中“1”的个数为偶数的时候,这个校验位就是“0”,否则这个校验位就是“1”,这样就可以保证传送数据满足偶校验的要求。
6、停止位,用于数据的间隔,固定为高电平。数据帧发送完成后,产生一个上升沿。(从数据传输的起始低电位变为空闲的高电位,产生一个上升沿触发信号)
下方就是一个字节数据的传输过程,从图中可以看出,串口发送的数据一般都是以数据帧的形式进行传输,每个数据帧都由起始位,数据位,停止位组成, 且停止位可变。
UART是通用异步收发器,USART是通用同步异步收发器,一般来说,在单片机上,名为UART的接口只能用于异步串行通信,而名为USART的接口既可用于同步串行通信,也可用于异步串行通信。
接下来介绍相应的代码文件。
首先是uart.c头文件:
#ifndef __USART_H
#define __USART_H
#include "./SYSTEM/sys/sys.h"
#include "./BSP//DELAY/delay.h"
#include "./BSP/LED/led.h"
extern UART_HandleTypeDef g_uart1_handle;
extern uint8_t g_rx_buffer[1];
extern uint8_t g_usart1_rx_flag;
void usart_init(uint32_t baudrate);
void USART1_IRQHandler(void);
void HAL_MspInit(UART_HandleTypeDef *haurt);
#endif
我们要使用extern关键字,声明该变量在.c文件里已经定义过了,在头文件引用,使之能在主函数中使用。
extern UART_HandleTypeDef g_uart1_handle;是对UART串口的初始化结构体。
uint8_t g_rx_buffer[1]是储存我们需要传输的字节。
uint8_t g_usart1_rx_flag检验我们是否发送了数据。
HAL_xx_MspInit:将外设底层的相关资源初始化完成,如时钟、使用到的引脚等。
接下来再编写uart.c文件:
#include "./BSP/UART/uart.h"
UART_HandleTypeDef g_uart1_handle;
uint8_t g_rx_buffer[1];
uint8_t g_usart1_rx_flag;
void usart_init(uint32_t baudrate){
g_uart1_handle.Instance = USART1;
g_uart1_handle.Init.Baudrate = baudrate;
g_uart1_handle.Init.WordLength = UART_WORDLENGTH_8B;
g_uart1_handle.Init.StopBits = UART_STOPBITS_1;
g_uart1_handle.Init.Parity = UART_PARITY_NONE;
g_uart1_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
g_uart1_handle.Init.Mode = UART_MODE_TX_RX;
HAL_UART_Init(&g_uart_handle);
HAL_UART_Receive_IT(&g_uart1_handle, (uint8_t*)g_rx_buffer, 1);
}
void HAL_UART_MspInit(UART_HandleTypeDef *haurt){
GPIO_InitTypeDef gpio_init_struct;
if(haurt->Instance == USART1){
__HAL_RCC_USART1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
gpio_init_struct.Pin = GPIO_PIN_9;
gpio_init_struct.Mode = GPIO_MODE_AF_PP;
gpio_init_struct.Pull = GPIO_PULLUP;
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &gpio_init_struct);
gpio_init_struct.Pin = GPIO_PIN_10;
HAL_GPIO_Init(GPIOA, &gpio_init_struct);
HAL_NVIC_SetPriority(USART1_IRQn, 3, 3);
HAL_NVIC_EnableIRQ();
}
}
void USART1_IRQHandler(void){
HAL_UART_IRQHandler(&g_uart1_handle);
}
UART_HandleTypeDef是usrt串口初始化的结构体,源码如下:
typedef struct __UART_HandleTypeDef
{
USART_TypeDef *Instance; /*!< UART registers base address */
UART_InitTypeDef Init; /*!< UART communication parameters */
uint8_t *pTxBuffPtr; /*!< Pointer to UART Tx transfer Buffer */
uint16_t TxXferSize; /*!< UART Tx Transfer size */
__IO uint16_t TxXferCount; /*!< UART Tx Transfer Counter */
uint8_t *pRxBuffPtr; /*!< Pointer to UART Rx transfer Buffer */
uint16_t RxXferSize; /*!< UART Rx Transfer size */
__IO uint16_t RxXferCount; /*!< UART Rx Transfer Counter */
DMA_HandleTypeDef *hdmatx; /*!< UART Tx DMA Handle parameters */
DMA_HandleTypeDef *hdmarx; /*!< UART Rx DMA Handle parameters */
HAL_LockTypeDef Lock; /*!< Locking object */
__IO HAL_UART_StateTypeDef gState; /*!< UART state information related to global Handle management
and also related to Tx operations.
This parameter can be a value of @ref HAL_UART_StateTypeDef */
__IO HAL_UART_StateTypeDef RxState; /*!< UART state information related to Rx operations.*/
} UART_HandleTypeDef;
我们主要关注这几个结构体成员:
1.Instance:选择的USART串口号;
2.Init.Baudrate:波特率,串口通信的速率,这里我们在初始化函数中会传入相应的波特率参数。
3.Init.WordLength = UART_WORDLENGTH_8B:传输数据大小,我们这里选用8位长的数据传输大小,也就是说我们一次只传输一个char大小的数据。
4.Init.StopBits=UART_STOPBITS_1:停止位,有无/1位/2位,这里我们采用一位停止位。
5.Init.Parity:奇偶校验位,这里我们不采用校验位。
6.Init.HwFlowCtl:设置硬件流控是否使能或禁能,这里我们关闭。
7.Init.Mode=UART_MODE_TX_RX:UART采用的模式,我们这里采用输入输出模式。
这里我们需要共用PA9,PA10两个串口,初始化GPIOA后再设置中断优先级。
HAL_UART_Receive_IT是串口接收中断函数。它用于在接收到数据时进行中断处理。在函数中包含了接收数据的读取和相关的数据处理。
最后我们再编写主函数main.c:
#include "./SYSTEM/sys/sys.h"
#include "./BSP/UART/uart.h"
int main(){
HAL_Init();
sys_stm32_clock_init(RCC_PLL_MUL9);
delay_init(72);
led_init();
usart_init(115200);
while(1){
if(g_usart1_rx_flag == 1){
HAL_UART_Transmit(&g_uart1_handle, (uint8_t*)g_rx_buffer, 1, 1000);
while(__HAL_UART_GET_FLAG(&g_uart1_handle, UART_FLAG_TC) != 1){
printf("Message received");
g_uart1_ex_flag = 0;
}
}
else{
delay_ms(50);
}
}
}
于是我们的实验就完成了