STM32CubeMX_环境搭建_GPIO_外部中断
STM32CubeMX_定时器中断_PWM
前两节简单的总结了GPIO, EXTI, TIMER的相关用法, 本节总结一下STM32串口的用法, 依然是原理性的东西不多讲, 直接开干.
先看下缩写:
USART比UART多了个同步, 选择作为同步时, 硬件表现上多了USART_CK的时钟线, 选择作为异步时, 和UART差别不大, 同步方式很少用到, 我们就只用异步方式. 仍然使用NUCLEO-F767ZI
板子, 串口3连接到了板载的ST-Link的虚拟串口, 可以通过USB直接与PC通信:
LPUART是新出的 STM32G/H/WB系列
和 部分STM32L系列
才有的, 顾名思义, 低功耗, 允许只使用32.768K的LSE作为时钟达到9600bps通讯速率, 超级省电; 但当时钟源是100MHz时, 可以达到33.3Mbps通讯速率, 可谓上限极高. 此外, 支持多机通信, 单线半双工通信等. 详细可参考 en.STM32G4-Peripheral-LPUART_interface_LPUART.pdf
. LPUART的例子暂略, 通用用法大致一样.
HAL库函数的操作方式有轮询
, 中断
和DMA
3种, 轮询是Blocking mode
, 中断和DMA是 Non-Blocking mode
. 注意后缀很好区分, 如发送函数:
HAL_UART_Transmit()
HAL_UART_Transmit_IT()
HAL_UART_Transmit_DMA()
补充一点, 最近几年出的片子的UART/USART功能也越来越完善, 增加了诸如自动波特率
, 数据反转
, TX/RX引脚互换
等亮眼操作. 暂且不表, 先从 NUCLEO-F767ZI
的串口开始吧.
步骤如下:
STM32CubeMX
, 点击 ACCESS TO MCU SELECTOR
, 选择 STM32F767ZI
Pinout & Configuration
-> System Core
-> SYS
-> Debug
选择 Serial Wire
Pinout & Configuration
-> System Core
-> RCC
-> HSE
选择 Crystal/Ceramic Resonator
以上步骤不太熟悉的话, 可以参考 STM32CubeMX_环境搭建_GPIO_外部中断 一节.
Pinout & Configuration
-> Connectivity
-> USART3
-> Mode
选择异步Asynchronous
, 波特率之类的就默认115200-8-N-1:
然后发现图中的引脚居然是PB10, PB11
, 而不是原理图的PD8/PD9
:
不要慌, 鼠标悬停提示
, 我们按住Ctrl, 单击PB10, 查看USART3_TX都可以映射到哪些引脚, 发现PD8
和PC10
变蓝, 那接着就松开Ctrl, 单击PD8
, 选择USART3_TX
, 同样单击PD9, 选择USART3_RX
, 这样, USART3就挪到了PD8/PD9
:
Project Manager
-> Project
-> Browse
选择工程位置(Project Location
), 填入工程名(Project Name
), Toolchain/IDE
选择 MDK-ARM
.
Project Manager
-> Code Generator
-> 勾选Copy only the necessary library files
, 还有Generate peripheral initialization as a pair of .c/.h files per periphral
点击右上角 GENERATE CODE
按钮生成代码, 打开工程.
Keil 点击魔术棒或者Project
-> Options for Target ...
, 默认配置Debug
为ST-link Debugger
, 点击Setting
-> Flash Download
-> 勾选Reset and Run
, 这样下载后可以自动复位运行.
如有疑问, 仍然去参考 STM32CubeMX_环境搭建_GPIO_外部中断 一节.
主要是用HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
这个函数进行发送, main.c中补充代码发送字符串和十六进制:
/* USER CODE BEGIN 2 */
uint8_t str_arr[] = "UART Example!\n\r";
uint8_t hex_arr[] = {0x00, 0x02, 0x03, 0x04, 0x05, 0x06};
uint8_t arr_length = 0;
arr_length = sizeof(str_arr) / sizeof(*str_arr);
HAL_UART_Transmit(&huart3, str_arr, arr_length, 100);
HAL_Delay(1000);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
++hex_arr[0];
arr_length = sizeof(hex_arr) / sizeof(*hex_arr);
HAL_UART_Transmit(&huart3, hex_arr, arr_length, 100);
HAL_Delay(1000);
}
/* USER CODE END 3 */
选择板子的虚拟串口, 115200波特率, 打开串口调试助手, 编译下载程序, 发现接收到UART Example
信息, 切换HEX显示
, 符合预期:
随便找个地方添加以下代码:
#ifdef __GNUC__
/* With GCC, small printf (option LD Linker->Libraries->Small printf
set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
/**
* @brief Retargets the C library printf function to the USART.
* @param None
* @retval None
*/
PUTCHAR_PROTOTYPE
{
/* Place your implementation of fputc here */
/* e.g. write a character to the EVAL_COM1 and Loop until the end of transmission */
HAL_UART_Transmit(&huart3, (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
要问哪来的, 官方的Example -> UART_Printf
复制过来的, 同时, 别忘添加头文件:
#include
不然可能报错 identifier "FILE" is undefined
.
然后就可以愉快的使用printf了:
uint8_t str_arr[] = "print Example!";
uint8_t hex_arr[] = {0x00, 0x02, 0x03, 0x04, 0x05, 0x06};
printf("printf test: \n\r");
printf("string array: %s\n\r", str_arr);
printf("hex: hex_arr[1] = 0x%02x\n\r", hex_arr[1]);
printf("num one: %d\n\r", 1u);
printf("pi = %.3f\n\r", 3.1415926f);
关闭Keil, 回到STM32CubeMX, NVIC
中勾选 USART3 global interrupt
, 优先级不设置么, 默认(0, 0):
点击 GENERATE CODE
重新生成代码, 打开工程, 补充代码:
/* USER CODE BEGIN PD */
#define RXBUFFERSIZE 28
/* USER CODE END PD */
/* USER CODE BEGIN PV */
uint8_t aRxBuffer[RXBUFFERSIZE]; //Buffer used for reception
__IO ITStatus g_uart3_rx_status = RESET;
/* USER CODE END PV */
/* USER CODE BEGIN 0 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
if(huart->Instance == huart3.Instance) { //if(huart == &huart3)
g_uart3_rx_status = SET;
}
}
/* USER CODE END 0 */
/* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart3, (uint8_t *)aRxBuffer, RXBUFFERSIZE);
/* USER CODE END 2 */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if(g_uart3_rx_status == SET) {
g_uart3_rx_status = RESET;
HAL_UART_Transmit(&huart3, aRxBuffer, RXBUFFERSIZE, 20);
HAL_UART_Receive_IT(&huart3, (uint8_t *)aRxBuffer, RXBUFFERSIZE);
}
}
/* USER CODE END 3 */
100ms间隔发送26个英文字母加回车换行是28字节, 发送间隔20ms以上10字节收发没有什么问题, 再快就可能出问题卡死了:
这种对于 定长字符串的接收
还是可以用的, 对于 不定长字符串的接收
, 一般会有明显的结束标志, 比如回车换行, 典型的就是AT指令的接收, 可以设置1字节接收中断, 来一个字节存一个字节到一个数组里, 然后在中断里判断是否是结束标志如回车换行或者特殊字符之类的即可 .
上面的接收方式使用起来还是很抓狂的, 可能会出现卡死之类, 是没有调校好的结果. 好在STM32提供了空闲中断, 当字节流不连续
时, 总会出现串口总线空闲的时候, 出现空闲就表示一帧数据接收完毕, 这个时候来个中断岂不美哉, 嗯, DMA空闲中断就是这么好使. 对于定长不定长字符串的接收通通好用.
关掉Keil, 回到STM32CubeMX, 原来的工程不要了, 新建一个工程, 时钟引脚配置之类的步骤略, 到USART3的界面, 找到DMA Setting
, 点击 Add
按钮添加USART3_RX的DMA, 同样添加USART3_TX的DMA:
然后到NVIC里面勾选中断, 优先级可以设置低一点:
点击生成代码, 打开工程, keil别忘记Debug里面勾选Reset and Run.
main.h添加代码:
/* USER CODE BEGIN Private defines */
#define RXBUFFERSIZE 50
/* USER CODE END Private defines */
main.c添加代码:
/* USER CODE BEGIN PV */
uint8_t aRxBuffer[RXBUFFERSIZE];
/* USER CODE END PV */
/* USER CODE BEGIN 2 */
__HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE);
HAL_UART_Receive_DMA(&huart3, (uint8_t *)aRxBuffer, RXBUFFERSIZE);
/* USER CODE END 2 */
stm32f7xx_it.c中:
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
extern uint8_t aRxBuffer[];
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP */
void UART_RxIdleCallback(UART_HandleTypeDef *huart);
/* USER CODE END PFP */
//void USART3_IRQHandler(void)
/* USER CODE BEGIN USART3_IRQn 1 */
UART_RxIdleCallback(&huart3);
/* USER CODE END USART3_IRQn 1 */
/* USER CODE BEGIN 1 */
void UART_RxIdleCallback(UART_HandleTypeDef *huart) {
uint32_t temp;
if(huart == &huart3) {
if(__HAL_UART_GET_FLAG(&huart3,UART_FLAG_IDLE) != RESET ) {
__HAL_UART_CLEAR_IDLEFLAG(&huart3);
temp = huart3.Instance->ISR;
temp = huart3.Instance->ICR;
temp = huart3.Instance->RDR;
HAL_UART_DMAStop(&huart3);
temp = hdma_usart3_rx.Instance->NDTR; //DMA stream x number of data register
HAL_UART_Transmit_DMA(&huart3, aRxBuffer, RXBUFFERSIZE-temp);
HAL_UART_Receive_DMA(&huart3, aRxBuffer, RXBUFFERSIZE);
}
}
}
/* USER CODE END 1 */
编译下载, 打开串口调试助手, 可以看到只要长度不超过RXBUFFERSIZE
定义的字节, 都会自动回传:
https://download.csdn.net/download/weifengdq/11962932
欢迎扫描二维码关注本人微信公众号, 及时获取或者发送给我最新消息: