STM32-NUCLEO-F411RE-USART_串口
使用STM32CubeMX生产初始化代码,底板硬件使用NUCLEO-F411RE开发板
串口基本原理
异步串行通信的特点
特点:数据传输以单个字符为单位,字符和字符之间的间隙任意,字符内部每一位持续的时间相同。收发双方没有专门的时钟信号,而是依靠事先约定的字符格式和通信速率来完成通信。
常用字符格式:1位起始位 8位数据位 无奇偶校验 1位停止位
波特率:每秒钟传送二进制数码的位数,以bit/s(bps)为单位 常用的波特率有:9600、19200、38400、57600和115200;
波特率为115200,表示每秒传输115200位,且每一位数据在数,据线上持续时间为Tbit=1/115200=8.68us。 波特率为9600时起始位长度为104.2us。
异步串行通信的两个关键点
异步串行通信的数据接收过程
串行内部寄存器结构框图
串行状态标志位
串囗初始化过程
一、配置时钟
开发板焊接了外部晶振,所以我 RCC(Reset and Cock Control) 配置选择了 Crystal/Ceramic Resonator(石英/陶瓷谐振器),配置完成后,右边的 Pinout view 里相关引脚就会被标绿。
外部高速时钟配置完成后,进入 Clock Configuration 选项,根据实际情况,将系统时钟配置为 96 MHz,配置步骤如下,最后按下回车,软件会自动调整分频和倍频参数。
二、配置调试模式
ST-Link 就是 Serial Wire 调试模式,一定要设置!!! 以前使用 M0 的芯片,不配置这个模式没出现问题,但现在这个型号,如果不配置 Serial Wire 模式,程序一旦通过 ST-Link 烧录到芯片中,芯片就再也不能被ST-Link 识别了。(后来我是通过 STMISP 工具烧录程序/擦除后才恢复正常的)
三、串口参数配置
串口为 USART1,按照下面步骤配置 USART1,将其设置为异步模式(Asynchronous),波特率 115200(默认),字长 8 Bits(默认),无校验(默认),停止位 1(默认),使能发送和接收(默认): 使能中断
四、生成 Keil 工程
设置 IDE 和 工程目录及名称: 将每种外设的代码存放到不同的 .c /.h 文件中,便于管理(不然都会被放到 main.c 中)。
代码部分
UART结构体定义
typedef struct __UART_HandleTypeDef
{
USART_TypeDef *Instance;
UART_InitTypeDef Init;
uint8_t *pTxBuffPtr;
uint16_t TxXferSize;
__IO uint16_t TxXferCount;
uint8_t *pRxBuffPtr;
uint16_t RxXferSize;
__IO uint16_t RxXferCount;
__IO HAL_UART_RxTypeTypeDef ReceptionType;
DMA_HandleTypeDef *hdmatx;
DMA_HandleTypeDef *hdmarx;
HAL_LockTypeDef Lock;
__IO HAL_UART_StateTypeDef gState;
__IO HAL_UART_StateTypeDef RxState;
__IO uint32_t ErrorCode;
} UART_HandleTypeDef;
UART函数
1、串口发送/接收函数
HAL_UART_Transmit();串口发送数据,使用超时管理机制 HAL_UART_Receive();串口接收数据,使用超时管理机制 HAL_UART_Transmit_IT();串口中断模式发送 HAL_UART_Receive_IT();串口中断模式接收 HAL_UART_Transmit_DMA();串口DMA模式发送 HAL_UART_Transmit_DMA();串口DMA模式接收 这几个函数的参数基本都是一样的,我们挑两个讲解一下
串口发送数据:
HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
功能:串口发送指定长度的数据。如果超时没发送完成,则不再发送,返回超时标志(HAL_TIMEOUT)。
参数:
UART_HandleTypeDef *huart UATR的别名 如 : UART_HandleTypeDef huart1; 别名就是huart1 *pData 需要发送的数据 Size 发送的字节数 Timeout 最大发送时间,发送数据超过该时间退出发送
举例: HAL_UART_Transmit(&huart1, (uint8_t *)ZZX, 3, 0xffff);
中断接收数据:
HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
功能:串口中断接收,以中断方式接收指定长度数据。 大致过程是,设置数据存放位置,接收数据长度,然后使能串口接收中断。接收到数据时,会触发串口中断。 再然后,串口中断函数处理,直到接收到指定长度数据,而后关闭中断,进入中断接收回调函数,不再触发接收中断。(只触发一次中断)
参数:
UART_HandleTypeDef *huart UATR的别名 如 : UART_HandleTypeDef huart1; 别名就是huart1 *pData 接收到的数据存放地址 Size 接收的字节数
举例: HAL_UART_Receive_IT(&huart1,(uint8_t *)&value,1);
2、串口中断函数
HAL_UART_IRQHandler(UART_HandleTypeDef *huart); //串口中断处理函数 HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart); //串口发送中断回调函数 HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart); //串口发送一半中断回调函数(用的较少) HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart); //串口接收中断回调函数 HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart);//串口接收一半回调函数(用的较少) HAL_UART_ErrorCallback();串口接收错误函数
串口接收中断回调函数:
HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
功能:HAL库的中断进行完之后,并不会直接退出,而是会进入中断回调函数中,用户可以在其中设置代码。
串口中断接收完成之后,会进入该函数,该函数为空函数,用户需自行修改
参数:
UART_HandleTypeDef *huart UATR的别名 如 : UART_HandleTypeDef huart1; 别名就是huart1
举例: HAL_UART_RxCpltCallback(&huart1){
串口中断处理函数
HAL_UART_IRQHandler(UART_HandleTypeDef *huart);
功能:对接收到的数据进行判断和处理 判断是发送中断还是接收中断,然后进行数据的发送和接收,在中断服务函数中使用
如果接收数据,则会进行接收中断处理函数
if((tmp_flag != RESET) && (tmp_it_source != RESET))
{
UART_Receive_IT(huart);
}
如果发送数据,则会进行发送中断处理函数
if (((isrflags & USART_SR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET))
{
UART_Transmit_IT(huart);
return;
}
3、串口查询函数
HAL_UART_GetState(); 判断UART的接收是否结束,或者发送数据是否忙碌
举例:
while(HAL_UART_GetState(&huart4) == HAL_UART_STATE_BUSY_TX)
主程序
#include "main.h"
#include "usart.h"
#include "gpio.h"
uint8_t RxBuffer;
void SystemClock_Config(void);
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&RxBuffer, 1, 10);
HAL_UART_Receive_IT(&huart1, (uint8_t *)&RxBuffer, 1);
}
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_USART2_UART_Init();
HAL_UART_Receive_IT(&huart1, (uint8_t *)&RxBuffer, 1);
HAL_UART_Transmit(&huart1, "HAL_UART_Transmit test\r\n", 24, 0xffff);
while (1)
{
HAL_UART_Transmit(&huart1, "hello world\r\n", 13, 0xffff);
HAL_Delay(500);
}
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 4;
RCC_OscInitStruct.PLL.PLLN = 96;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 4;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_3) != HAL_OK)
{
Error_Handler();
}
}
void Error_Handler(void)
{
__disable_irq();
while (1)
{
}
}
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
}
#endif
七、实验效果
八、重定向 printf
在工程中添加下面代码,重定义 fputc
#include "usart.h"
#include <stdio.h>
int fputc(int ch,FILE *f)
{
HAL_UART_Transmit(&huart1,(uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
需要使用C语言函数
将前文示例代码中的 HAL_UART_Transmit() 替换成 printf() 将实现同样的功能
|