介绍
是串口通讯有着简单可靠,跨平台应用,可拓展性强,使用范围也很普及等优点,使得它成为开发者最常用的通讯协议之一,配合串口调试助手使用也是开发者的调试好帮手。本文不过多讲述串口协议的底层原理,直接阐述基于cubeMX配置HAL库STM32的uart和printf重定向使用方法。
UART通信
下面是USART1,USART2,USART3的通讯示例
cubeMX配置
点击Connectivity下拉菜单的USART1
按上图步骤配置好串口后,点击生成代码,cubeMX配置步骤就已完成
建议也可以把这个勾选上,可以对每个模块独立生成.c 和.h文件,使main.c文件看起来没那么臃肿。
Uart程序
cubxMX配置好的初始化等程序这里不再过多赘述直接看需要更改的程序
下面cubeMX后main.c主要内容
* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "myuart.h"
#include "stm32f1xx_hal.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
//myuart.h上定义的接收结构体
RX_struct RX={0};
RX_struct RX2={0};
RX_struct RX3={0};
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_USART2_UART_Init();
MX_USART3_UART_Init();
/* USER CODE BEGIN 2 */
/* 启动串口接收 */
HAL_UART_Receive_IT(&huart1, &RX.rx_temp, 1);
HAL_UART_Receive_IT(&huart2, &RX2.rx_temp, 1);
HAL_UART_Receive_IT(&huart3, &RX3.rx_temp, 1);
/*测试串口程序*/
Myuart_sent_string(&huart1,"\r\nuart1 is ok !!\r\n");
Myuart_sent_string(&huart2,"\r\nuart2 is ok !!\r\n");
Myuart_sent_string(&huart3,"\r\nuart3 is ok !!\r\n");
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
myuart.c:
HAL_UART_RxCpltCallback函数可以根据自己需求更改
#include "myuart.h"
#include "usart.h"
extern RX_struct RX;
extern RX_struct RX2;
extern RX_struct RX3;
/**
* @brief 发送一个字节
* @param uart指针
* @param 要发送的字节数据
* @retval None
*/
void Myuart_sent_byte(UART_HandleTypeDef *huart,uint8_t ch)
{
HAL_UART_Transmit(huart,&ch,1,0xFFFF);
}
/**
* @brief 发送一个字符串
* @param uart指针
* @param 要发送的字符串指针
* @retval None
*/
void Myuart_sent_string(UART_HandleTypeDef *huart,char *ch)
{
for(int ptr=0;ch[ptr]!='\0';ptr++)
{
char temp =ch[ptr];
Myuart_sent_byte(huart,(uint8_t)temp);
}
}
/**
* @brief 发送len个字节的数据
* @param uart指针
* @param 数据长度
* @param 数据指针
* @retval None
*/
void Myuart_sent_lenarray(UART_HandleTypeDef *huart,uint8_t len,uint8_t *ch)
{
for(int i=0;i<len;i++)
{
HAL_UART_Transmit(huart,&ch[i],1,0xFFFF);
}
}
/**
* @brief 发送一个字节数据,用16进制串口显示
* @param uart指针
* @param 要展示的数据
* @retval None
*/
void Myuart_sent_uint8(UART_HandleTypeDef *huart,uint8_t x)
{
Myuart_sent_string(huart," 0X");
for(int i=1;i>=0;i--){
if(((x>>(4*i))&0x000f)<10){uint8_t temp='0'+((x>>(4*i))&0x000f);Myuart_sent_byte(huart,temp);}
else if(((x>>(4*i))&0x000f)==10){uint8_t temp='A';Myuart_sent_byte(huart,temp);}
else if(((x>>(4*i))&0x000f)==11){uint8_t temp='B';Myuart_sent_byte(huart,temp);}
else if(((x>>(4*i))&0x000f)==12){uint8_t temp='C';Myuart_sent_byte(huart,temp);}
else if(((x>>(4*i))&0x000f)==13){uint8_t temp='D';Myuart_sent_byte(huart,temp);}
else if(((x>>(4*i))&0x000f)==14){uint8_t temp='E';Myuart_sent_byte(huart,temp);}
else if(((x>>(4*i))&0x000f)==15){uint8_t temp='F';Myuart_sent_byte(huart,temp);}
}
}
/**
* @brief 发送len个字节数据,用16进制串口显示
* @param uart指针
* @param 数据长度
* @param 数据指针
* @retval None
*/
void Myuart_sent_lenuint8(UART_HandleTypeDef *huart,uint8_t len,uint8_t *ch)
{
for(int i=0;i<len;i++)
{
Myuart_sent_uint8(huart,ch[i]);
}
}
/**
* @brief 串口接收中断回调函数
* @param uart指针
* @retval None
*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
/* 判断是否是当前使用的串口 */
if (huart == &huart1) {
RX.rx_dat[RX.rx_ptr++]=RX.rx_temp;
// Myuart_sent_byte(&huart1,RX.rx_temp);
HAL_UART_Receive_IT(&huart1, &RX.rx_temp, 1);
if(RX.rx_ptr>=4){
RX.rx_ptr=0;
Myuart_sent_string(&huart1,"\r\n");
Myuart_sent_lenarray(&huart1,4,RX.rx_dat);
Myuart_sent_string(&huart1,"\r\n");
}
}
if (huart == &huart2)
{
RX2.rx_dat[RX2.rx_ptr++]=RX2.rx_temp;
Myuart_sent_uint8(&huart1,RX2.rx_temp);
HAL_UART_Receive_IT(&huart2, &RX2.rx_temp, 1);
if(RX2.rx_ptr>=8){
RX2.rx_ptr=0;
Myuart_sent_string(&huart1,"\r\nthe data is : ");
Myuart_sent_lenarray(&huart1,8,RX2.rx_dat);
Myuart_sent_string(&huart1,"\r\n");
}
}
if (huart == &huart3)
{
RX3.rx_dat[RX3.rx_ptr++]=RX3.rx_temp;
//Myuart_sent_uint8(&huart1,RX3.rx_temp);
HAL_UART_Receive_IT(&huart3, &RX3.rx_temp, 1);
if(RX3.rx_ptr>=8){
RX3.rx_ptr=0;
Myuart_sent_string(&huart1,"\r\nthe data is : ");
Myuart_sent_lenuint8(&huart1,8,RX3.rx_dat);
Myuart_sent_string(&huart1,"\r\n");
}
}
}
myuart.h
#ifndef _MYUART_H_
#define _MYUART_H_
#include "main.h"
typedef struct
{
uint8_t rx_dat[30];
uint8_t rx_ptr;
uint8_t rx_temp;
}RX_struct;
extern void Myuart_sent_byte(UART_HandleTypeDef *huart,uint8_t ch);
extern void Myuart_sent_string(UART_HandleTypeDef *huart,char *ch);
extern void Myuart_sent_lenarray(UART_HandleTypeDef *huart,uint8_t len,uint8_t *ch);
extern void Myuart_sent_uint8(UART_HandleTypeDef *huart,uint8_t x);
extern void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
extern void Myuart_sent_lenuint8(UART_HandleTypeDef *huart,uint8_t len,uint8_t *ch);
#endif
以上完成了串口通讯的代码,printf重定向后串口调试将更加的方便
printf 重定向
方法一
第一步:勾选MicroLib
第二步:在myuart文件下添加下面函数 。 *注意得包含头文件stdio.h。
int fputc (int ch, FILE *f)
{
(void)HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 1000);
return ch;
}
之后就可以愉快的使用printf函数输出调试了
方法二
在myuart文件下添加下列函数即可
//重定向printf1至uart1的demo
void printf1(const char *fmt,...)
{
char buf[50] = {0};
va_list arg;
va_start(arg,fmt);
vsnprintf(buf,sizeof(buf),fmt,arg);
va_end(arg);
HAL_UART_Transmit(&huart1, (uint8_t*)buf, strlen(buf), 5000);
}
需要注意的是,此函数需要引用头文件**#include ”stdarg.h“** 和 #include “string.h”。
此方法不需要勾选MicroLib,而且可以很方便的自定义多个printf函数。