学习STM32 CubeMX实验7:使用USART进行printf打印和串口接收特定数据包

 一、printf的实现

学过C语言的同学都知道printf函数,是打开C语言大门的一个函数,如何在stm32上使用该函数呢!

要在stm32上使用printf函数,需要对printf进行重定向,把串口发送的数据通过printf打印出来

//printf重定向代码(可添加到usart_it.c或者其他地方)

#include <stdio.h>


int fputc(int ch,FILE *p)  //函数默认的,在使用printf函数时自动调用
{
    USART1->DR = (uint8_t) ch;//发送
	while((USART1->SR & 0X40) == 0);//等待发送完成
	return ch;
}

对printf重定向后,避免使用半主机模式二导致库函数程序无法运行,使用微库Micro LIB可以避免半主机模式,在keil中点击Options for Target…,勾选Use Micro LIB,点击OK即可。

避免使用半主机模式还有一种方法是定义 _sys_exit(),可以在不使用Micro LIB(取消勾选Use Micro LIB)的情况避免半主机模式。

#if 1
#pragma import(__use_no_semihosting)   // 确保没有从 C 库链接使用半主机的函数

int fputc(int ch,FILE *p)  //函数默认的,在使用printf函数时自动调用
{
    USART1->DR = (uint8_t) ch;//发送
	while((USART1->SR & 0X40) == 0);//等待发送完成
	return ch;
}

FILE __stdout;

struct __FILE
{
    int handle;
};

//定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x)
{
    x=x;
}
#endif

在main循环中添加printf打印函数,即可观察上位机到上位机每100ms打印一次数据。

int main(void)
{
  /* USER CODE BEGIN 1 */
    
  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  
  /*为了观看方便,把头文件放到这里*/
    #include "usart_it.h"
    #include <stdio.h>
    /*为了观看方便,把头文件放到这里*/
    
//    //开启串口接收中断,每次接收到一个字节后中断一次
//    HAL_UART_Receive_IT(&huart1,&USART1_Stru.receive,1);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
      static uint8_t i=0;
      i++;
      printf("%d\r\n",i);
      HAL_Delay(100);
  }
  /* USER CODE END 3 */
}

 二、数据包接收

当要使用串口上位机控制单片机时,可以通过发送一个字节进行控制,但是在实际应用中可能需要传输一组数据,这时发送一个字节就无法控制了,需要发送一组数据,同时修改控制参数,要怎么接收这一组数据呢!

可以定义一个数据帧,通过该帧格式获取每个数据的信息,如在调试PID时要控制三个参数,通过串口上位机发送该数据帧就能调节PID参数。在该例子中可以定义帧格式为:

0xcc 0xpid_p 0xpid_i 0xpid_d 0xeb 0x90
帧头   数据1   数据2   数据3     帧尾

接收到数据后,先判断是否接收到帧尾的两个字节,当收到一帧数据后,通过帧头进行检验,校验通过后将收到的数据帧回传到串口上位机,通过该代码可以解析出一帧数据dat1/dat2/dat3。接收代码如下:

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if(huart==&huart1)
    {
        //读取接收到的数据
        USART1_Stru.RxBuff[USART1_Stru.RxLen] = USART1_Stru.receive;
        //帧尾
        if (USART1_Stru.RxBuff[USART1_Stru.RxLen] == 0x90\
            && USART1_Stru.RxBuff[USART1_Stru.RxLen - 1] == 0xeb)
        {
            //帧头
            if (USART1_Stru.RxBuff[0] == 0xCC)
            {
                //将收到的数据帧发送回上位机,也可以将数据复制到其他地方,方便调用
                HAL_UART_Transmit(&huart1,USART1_Stru.RxBuff,USART1_Stru.RxLen+1,1000);
            }
            USART1_Stru.RxLen = 0;
        }
        else
        {
            USART1_Stru.RxLen++;
        }
        
        //需要再次开启接收中断,否则只进入一次
        HAL_UART_Receive_IT(&huart1,&USART1_Stru.receive,1);
    }
}

全部代码如下:

#include "usart_it.h"
#include <stdio.h>

#if 1
#pragma import(__use_no_semihosting)   // 确保没有从 C 库链接使用半主机的函数

int fputc(int ch,FILE *p)  //函数默认的,在使用printf函数时自动调用
{
    USART1->DR = (uint8_t) ch;//发送
	while((USART1->SR & 0X40) == 0);//等待发送完成
	return ch;
}

FILE __stdout;

struct __FILE
{
    int handle;
};

//定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x)
{
    x=x;
}
#endif


USART1_Structure USART1_Stru = { 0 };

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if(huart==&huart1)
    {
        //读取接收到的数据
        USART1_Stru.RxBuff[USART1_Stru.RxLen] = USART1_Stru.receive;
        //帧尾
        if (USART1_Stru.RxBuff[USART1_Stru.RxLen] == 0x90\
            && USART1_Stru.RxBuff[USART1_Stru.RxLen - 1] == 0xeb)
        {
            //帧头
            if (USART1_Stru.RxBuff[0] == 0xCC)
            {
                //将收到的数据帧发送回上位机,也可以将数据复制到其他地方,方便调用
                HAL_UART_Transmit(&huart1,USART1_Stru.RxBuff,USART1_Stru.RxLen+1,1000);
            }
            USART1_Stru.RxLen = 0;
        }
        else
        {
            USART1_Stru.RxLen++;
        }
        
        //需要再次开启接收中断,否则只进入一次
        HAL_UART_Receive_IT(&huart1,&USART1_Stru.receive,1);
    }
}




#ifndef _USART_IT_H
#define _USART_IT_H

#include "main.h"
#include "usart.h"


//最大接收数据长度
#define USART1RxLEN 200

typedef struct
{
    uint8_t receive;
    uint8_t RxBuff[USART1RxLEN];
    uint8_t RxLen;
}USART1_Structure;
extern USART1_Structure USART1_Stru;




#endif


//main函数中只需要添加接收中断代码
int main(void)
{
  /* USER CODE BEGIN 1 */
    
  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
  
  /*为了观看方便,把头文件放到这里*/
    #include "usart_it.h"
    #include <stdio.h>
    /*为了观看方便,把头文件放到这里*/
    
    //开启串口接收中断,每次接收到一个字节后中断一次
    HAL_UART_Receive_IT(&huart1,&USART1_Stru.receive,1);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
//      static uint8_t i=0;
//      i++;
//      printf("%d\r\n",i);
//      HAL_Delay(100);
  }
  /* USER CODE END 3 */
}







已经实现了printf打印和数据包的接收功能,下期一起学习USART-DMA接收不定长度字节的数据。

作者:单片kun

物联沃分享整理
物联沃-IOTWORD物联网 » 学习STM32 CubeMX实验7:使用USART进行printf打印和串口接收特定数据包

发表评论