STM32从零开始 - 第三章:从串口输出HelloWorld

目录

 

前言

串口初始化

重定向C库的printf函数到串口

在main中添加测试代码

完整的main.c代码

编译下载

程序效果

扩展实验1:格式化字符串输出

扩展实验2:串口输入


前言

本章我们将使用STM32官方BSP中的串口驱动,在串口上输出HelloWorld。

 

从串口输出HelloWorld的方法

1. 简单的将HelloWorld这个字符串从串口里输出。

2. 可以将C库的printf输出重定向到串口上

本章我们使用第2种方法,因为第一种方法有太多的参考代码了。

 

串口初始化

首先我们需要将USART初始化为普通的异步串口。

USART的初始化代码,在我们第一章里,已经由STM32CubeMX自动产生出来了,因为我们当时在配置引脚的时候将PA9/PA10配置成了UART1,自动产生出的UART初始化代码请参考main.c里面的:

MX_USART1_UART_Init函数:

/**
  * @brief USART1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_USART1_UART_Init(void)
{

  /* USER CODE BEGIN USART1_Init 0 */

  /* USER CODE END USART1_Init 0 */

  /* USER CODE BEGIN USART1_Init 1 */

  /* USER CODE END USART1_Init 1 */
  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART1_Init 2 */

  /* USER CODE END USART1_Init 2 */

}

以及stm32f1xx_hal_msp.c里面的函数MX_USART1_UART_Init:

/**
  * @brief USART1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_USART1_UART_Init(void)
{

  /* USER CODE BEGIN USART1_Init 0 */

  /* USER CODE END USART1_Init 0 */

  /* USER CODE BEGIN USART1_Init 1 */

  /* USER CODE END USART1_Init 1 */
  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART1_Init 2 */

  /* USER CODE END USART1_Init 2 */

}

其中:MX_USART1_UART_Init是在HAL_UART_Init里面调用的。

然后我们稍微封装一下串口的输出接口:

void uart1_write(void* pdata, unsigned int len)
{
    HAL_UART_Transmit(&huart1, pdata, len, HAL_MAX_DELAY);
}

void uart1_write_byte(uint8_t value)
{
    uart1_write(&value, 1);
}

重定向C库的printf函数到串口

gcc编译器的small库需要重新实现__io_putchar即可。

而非gcc编译器,如ARMCC编译器则需要重新实现fputc。

#ifdef __GNUC__
  /* With GCC/RAISONANCE, 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
{
  uart1_write_byte((uint8_t)ch);
  return ch;
}

所以上面这段代码可以同时用于gcc或非gcc编译器。

在main中添加测试代码

在串口初始化后的任意地方加入HelloWorld输出语句:

printf("HelloWorld!\r\n");

完整的main.c代码

/* USER CODE BEGIN Header */
#include <stdio.h>
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2020 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
SPI_HandleTypeDef hspi2;

UART_HandleTypeDef huart1;

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_SPI2_Init(void);
static void MX_USART1_UART_Init(void);
void uart1_write_byte(uint8_t value);
/* USER CODE BEGIN PFP */

#ifdef __GNUC__
  /* With GCC/RAISONANCE, 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
{
  uart1_write_byte((uint8_t)ch);
  return ch;
}

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
static void led_init(void)
{
	// PC13 is LED control PIN
	GPIO_InitTypeDef gpiodef;

	gpiodef.Pin = GPIO_PIN_13;
	gpiodef.Mode = GPIO_MODE_OUTPUT_PP;
	gpiodef.Speed = GPIO_SPEED_FREQ_HIGH;

	HAL_GPIO_Init(GPIOC, &gpiodef);
}

static void led_onoff(int onoff)
{
	if (!onoff)
	{
		// PC13 = HIGH
		HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
	}
	else
	{
		// PC13 = LOW
		HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
	}
}
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
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_SPI2_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */

  led_init();

  printf("HelloWorld!\r\n");
  /* USER CODE END 2 */

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

	  led_onoff(1);
	  HAL_Delay(500);
	  led_onoff(0);
	  HAL_Delay(500);

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB buses clocks
  */
  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_2) != HAL_OK)
  {
    Error_Handler();
  }
  /** Enables the Clock Security System
  */
  HAL_RCC_EnableCSS();
}

/**
  * @brief SPI2 Initialization Function
  * @param None
  * @retval None
  */
static void MX_SPI2_Init(void)
{

  /* USER CODE BEGIN SPI2_Init 0 */

  /* USER CODE END SPI2_Init 0 */

  /* USER CODE BEGIN SPI2_Init 1 */

  /* USER CODE END SPI2_Init 1 */
  /* SPI2 parameter configuration*/
  hspi2.Instance = SPI2;
  hspi2.Init.Mode = SPI_MODE_MASTER;
  hspi2.Init.Direction = SPI_DIRECTION_2LINES;
  hspi2.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi2.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi2.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi2.Init.NSS = SPI_NSS_SOFT;
  hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
  hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi2.Init.CRCPolynomial = 10;
  if (HAL_SPI_Init(&hspi2) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN SPI2_Init 2 */

  /* USER CODE END SPI2_Init 2 */

}

/**
  * @brief USART1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_USART1_UART_Init(void)
{

  /* USER CODE BEGIN USART1_Init 0 */

  /* USER CODE END USART1_Init 0 */

  /* USER CODE BEGIN USART1_Init 1 */

  /* USER CODE END USART1_Init 1 */
  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART1_Init 2 */

  /* USER CODE END USART1_Init 2 */

}

void uart1_write(void* pdata, unsigned int len)
{
    HAL_UART_Transmit(&huart1, pdata, len, HAL_MAX_DELAY);
}

void uart1_write_byte(uint8_t value)
{
    uart1_write(&value, 1);
}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();

}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */

  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

我们保留了之前的LED闪灯代码,这样能知道程序是否正常运行。

并且我们修正了之前闪灯接口的一个错误,LED灯亮对应的是低电平,我们之前错设为高电平了。

编译下载

编译完成后,使用STM32CubeProgrammer工具,及CH341串口下载程序到STM32开发板中。

下载方法参考LED闪烁这个章节中的程序烧录相关章节的内容。

记得用串口下载前必需将BOOT0拉高才能进入下载模式,而下载完成后需要将BOOT0拉低才能启动之前烧进去的程序。

程序效果

连线:CH341线除了3.3V和GND外,还需要连接串口线,就是下载口的连接方式。

程序下载烧录完成后不用拔掉TX/RX线,我们的HelloWorld就是从STM32的Tx(PA9)上输出到CH341的Rx上的。

扩展实验1:格式化字符串输出

我们是用printf来输出的HelloWorld,也就是说,我们可以用printf来输出格式化字符串,测试语句:

{
	  int ia = 123;
	  char cb = 'A';
	  char *strc = "HelloWorld";
	  unsigned int hd = 0xA5;

	  printf("ia: %d\r\n", ia);
	  printf("cb: %d\r\n", cb);
	  printf("strc: %s\r\n", strc);
	  printf("hd: 0x%02X\r\n", hd);
  }

实际串口输出内容:

看起来格式化输出没问题了,然后在后续的实验里,我们就可以实验printf来输出一些调试日志了。

扩展实验2:串口输入

既然我们已经将串口初始化了,那么我们理论上是可以用串口接收来自上位机的数据的,这里我们的上位机就是连接CH341的电脑。

我们简单封装一个读串口的接口:

int uart1_read(void* pdata, unsigned int len)
{
    int ret = 0;
    if (HAL_OK != HAL_UART_Receive(&huart1, pdata, len, HAL_MAX_DELAY))
    {
        ret = -1;
    }
    else
    {
        ret = len;
    }

    return ret;
}

int uart1_read_byte(void)
{
    int bytedata = 0;
    if (1 != uart1_read(&bytedata, 1))
    {
        bytedata = -1;
    }

    return bytedata;
}

测试代码:

{
    int chtmp = uart1_read_byte();
    if (chtmp >= 0)
    {
        uart1_write_byte((uint8_t)chtmp);
    }
    else
    {
        uart1_write_byte(0xFF);
    }
}

这个测试代码是一个ECHO功能:将从串口上接收到的内容原样输出到串口上去。

我们将他放到while循环中去,这样程序就能监听串口,并将串口上的数据echo回去。

/* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
	  {
	      int chtmp = uart1_read_byte();
	      if (chtmp >= 0)
	      {
	          uart1_write_byte((uint8_t)chtmp);
	      }
	      else
	      {
	          uart1_write_byte(0xFF);
	      }
	  }

	  led_onoff(1);
	  HAL_Delay(500);
	  led_onoff(0);
	  HAL_Delay(500);

    /* USER CODE BEGIN 3 */
  }

原来的LED闪灯还在,但是这里闪灯的节奏会被串口读取的动作打乱,程序效果是当电脑发送一个字节给STM32开发板,程序会将字节内容发回给电脑,并控制灯亮灭一次。

实验输出:

当我们通过电脑向STM32发送Ab后,STM32将受到的A和b分别发送回给电脑了。

注意:由于我们在LED闪灯那里做了延时,所以此处不能一次发送多个字节,否则会因为LED闪灯等待时间中串口FIFO中的数据没有被及时读走而导致串口数据接收不完整。

 

 

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值