STM32物联网套件基础版06-串口通信

前言

串口通信在嵌入式开发中占据非常重要的低位,串口经常被用来调试系统,打印必要的调试信息,帮助我们分析定位问题,同时很多常见的外设也支持串口协议,如WIFI模块、蓝牙模块、4G模块、指纹模块、PM2.5传感器、甲醛传感器等诸多元器件,本节开始,我们正式学习STM32单片机的串口。

一、基础知识

1. STM32物联网套件简介

STM32物联网套件目前有两个版本:基础版和高级版,后续会增加应用版本和语音版,核心板均采用STM32F103C8T6核心板,基础版主要元器件如下所示:
在这里插入图片描述
高级版版主要元器件如下所示:
在这里插入图片描述
STM32物联网套件致力于带领大家入门物联网,不仅学习STM32,也了解转微信小程序开发、物联网服务器后台开发,真正做到了解一个物联网项目的方方面面,基于此,我们定制了一套通用的WIFI通讯协议(可以理解为类似AT指令,不过集成度更高,几条指令就可以直连接云平台),如三条指令连接腾讯云实例

后续我们会继续增加涂鸦智能、电信云、移动Onenet、阿里云等主流云平台的支持,力争做到,一套STM32代码,通过定制的WIFI模块可以连接到不同的云平台,也欢迎有产品开发需求的朋友私聊我们咨询、定制物联网方案!

本套物联网套件可以支持大学生参加物联网相关比赛、申请学校大创项目、完成毕业设计等,我们的定位是做一家开源智能硬件服务商,和大家一起探索物联网,我们的使命是推动更多物联网产品的落地和普及,让技术不再是阻碍!

2.通信方式简介

串口使用的通信方式是串行通信,串口通信是相对于并行通信来说的,所谓并行通信指的是数据的各位同时传送,其特点是传输速度快、效率高,但是使用数据线较多,传输成本较高,适合近距离通信,其示意图如下:
在这里插入图片描述
所谓串行通信其实是指数据通过一根数据线一位一位依次传送给目标设备,其特点是至少只需要一根传输线即可完成,成本低但传输速断慢,其只需要少数几条线就可以在系统间交换信息。其示意图如下:
在这里插入图片描述

3. 串行通信制式

按照数据传输方向,可以分为三张总制式,即单工、半双工、全双工。
在这里插入图片描述

( a ) 单工制式
A站和B站数据只能从一个设备发至另一个设备,单向传输。

( b ) 半双工工制式
A站和B站数据传送是双向的,但同一时刻只能有一个方向数据传送,接收开关可由软件控制。

( c ) 全双工制式
A站和B站任意时刻数据都可以同时发送和接收数据

4. STM32串口简介

STM32F103C8T6共有三个USART,分别是USART1 USART2 USART3,USART(通用同步异步收发器)是一个串行通信设备,可以设置位同步模式和异步模式。
在这里插入图片描述
一般单片机还有一个UART,它是在 USART 基础上裁剪掉了同步通信功能,只有异步通信。两者区别是,同步通信需要有一根时钟线提供时钟,我们平时一般用的串口通信基本都是 UART。

(1) 串口常见用法

(a) 打印系统调试日志,快速定位问题

(b) 控制串口设备

(c) 通过RS232/RS485等控制工业通讯设备

(2) STM32F103C8T6串口资源
在这里插入图片描述

5. 硬件设计

本次串口通信实验使用串口1和串口2,其中串口1用来下载程序,打印系统日志,串口2中断方式接收到数据后立刻返回,连接方法如下:
在这里插入图片描述
实物图如下所示:
在这里插入图片描述

二、实例

1. 新建工程

使用STM32CubeMX创建一个新的工程,参考环境搭建章节配置方式,设置RCC和PC13引脚输出,使能串口1,选择异步通信方式。
在这里插入图片描述
使能串口2,同时进行中断配置
在这里插入图片描述
进入Clock configuration页面,选择HSE时钟源,倍频后主时钟为72MHz
在这里插入图片描述
切换到Project Manager栏目,设置工程名字、工程保存目录、工具链等信息,具参数如下图所示
在这里插入图片描述
点击左边栏目Code Generator,然后勾选Generate peripheral initialization as a pair of ‘.c/.h’ files per peripheral,勾选此选项,外设将单独保存在一个文件中,而不是全部都在main.c中。
在这里插入图片描述
点击GENERATE按钮,生成新的工程
在这里插入图片描述

2. 函数说明

我们先看下串口初始化函数,串口1和串口2配置相同,都是115200波特率,8位字长,一位停止位,无校验,无流控。

void MX_USART1_UART_Init(void)
{
  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();
  }
}
/* USART2 init function */
void MX_USART2_UART_Init(void)
{
  huart2.Instance = USART2;
  huart2.Init.BaudRate = 115200;
  huart2.Init.WordLength = UART_WORDLENGTH_8B;
  huart2.Init.StopBits = UART_STOPBITS_1;
  huart2.Init.Parity = UART_PARITY_NONE;
  huart2.Init.Mode = UART_MODE_TX_RX;
  huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart2.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart2) != HAL_OK)
  {
    Error_Handler();
  }

}

然后我们看下串口处理HAL_UART_MspInit()函数

void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(uartHandle->Instance==USART1)
  {
  /* USER CODE BEGIN USART1_MspInit 0 */

  /* USER CODE END USART1_MspInit 0 */
    /* USART1 clock enable */
    __HAL_RCC_USART1_CLK_ENABLE();

    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**USART1 GPIO Configuration
    PA9     ------> USART1_TX
    PA10     ------> USART1_RX
    */
    GPIO_InitStruct.Pin = GPIO_PIN_9;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_10;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /* USER CODE BEGIN USART1_MspInit 1 */

  /* USER CODE END USART1_MspInit 1 */
  }
  else if(uartHandle->Instance==USART2)
  {
  /* USER CODE BEGIN USART2_MspInit 0 */

  /* USER CODE END USART2_MspInit 0 */
    /* USART2 clock enable */
    __HAL_RCC_USART2_CLK_ENABLE();

    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**USART2 GPIO Configuration
    PA2     ------> USART2_TX
    PA3     ------> USART2_RX
    */
    GPIO_InitStruct.Pin = GPIO_PIN_2;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_3;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* USART2 interrupt Init */
    HAL_NVIC_SetPriority(USART2_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(USART2_IRQn);
  /* USER CODE BEGIN USART2_MspInit 1 */

  /* USER CODE END USART2_MspInit 1 */
  }
}

此函数在初始化串口过程中配置了RCC时钟、GPIO引脚作用,对于串口2,还额外增加了串口2中断配置功能

3. 修改程序

(1) 首先重定义printf函数

欲实现printf输出到串口,需要将fputc函数里面的输出指向串口(重定向),方法很简单,在keil工程中实现这个函数就好,注意需要增加头文件#include “stdio.h”,否则无法识别FILE。

int fputc(int ch, FILE *f)
{
  HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
  return ch;
}

实际写代码时候,常常为了考虑系统兼容性,增加对GNU编译系统的兼容处理,修改如下:

#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
{
  /* 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(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
 
  return ch;
}

注意,GNU编译系统中,实现printf重定向,需要使用__io _putchar(int ch)函数,为了保持程序的兼容性,增加宏定义PUTCHAR_PROTOTYPE。

(2) 然后完成串口收发处理

a. usart.c文件中重写HAL_UART_RxCpltCallback 串口接收完成回调函数

uint8_t aRxBuffer;

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if (huart->Instance == USART2)
    {
        HAL_UART_Transmit(&huart2, &aRxBuffer, 1, 100); 
        HAL_UART_Receive_IT(&huart2, &aRxBuffer, 1);     
    }
}

注意该函数中 HAL_UART_Transmit 是串口发送函数, 上文实现收到数据马上发出功能,HAL_UART_Receive_IT函数作用是重新开启串口接收中断。

b. main.c文件中printf定期打印系统日志

extern uint8_t aRxBuffer;

int main(void)
{
  /* USER CODE BEGIN 1 */
        int flag_led = 0;
  /* 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();
  MX_USART2_UART_Init();
  /* USER CODE BEGIN 2 */
  HAL_UART_Receive_IT(&huart2,&aRxBuffer,1);
  /* USER CODE END 2 */

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

    /* USER CODE BEGIN 3 */
    HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
    printf("reset gpio!\r\n");
    HAL_Delay(1000); 
    HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
    printf("set gpio!\r\n");
    HAL_Delay(1000);
                
  }
  /* USER CODE END 3 */
}

首先开启串口接收功能,然后就可以调用printf函数,注意增加头文件#include"stdio.h"。

三、下载运行

按照上一节方式给核心板下载程序,可以看到系统正常打印调试日志,通过上位机发送数据到串口2,也可以立刻收到返回消息。
在这里插入图片描述

四、小结

如您在使用过程中有任何问题,请加QQ群进一步交流。

QQ交流群:906015840 (备注:物联网项目交流)

源码获取:关注公众号,回复xiaoyi_stm32kits获取资料

开源智能硬件:http://bbs.xiaoyiiot.cn/

小驿物联出品:宁愿做过了后悔,也不要后悔没去做!

  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
基于STM32物联网网关是一种可将物理设备与互联网连接起来的解决方案。它采用STM32系列微控制器作为主控芯片,结合适当的通信模块(如Wi-Fi、蓝牙、LoRa等)和传感器,可以实现设备之间的通信和数据传输。 首先,STM32物联网网关可以作为一个智能家居系统的核心,通过连接各种传感器和执行器,实现对家居设备的智能控制。例如,通过连接温度传感器,可以实时监测室内温度,并根据设定的温度范围来实现自动调节空调的温度。同时,通过连接灯光执行器,可以实现对家居灯光的智能控制,如自动开关、调光等。 其次,基于STM32物联网网关还可以应用于物流和仓储领域。通过与温湿度传感器、气体传感器等连接,可以实时监测货物的存储环境,并通过传输模块将数据上传至云端进行分析和存储。这样,物流公司可以及时了解货物的存储条件,确保货物的质量和安全。 另外,基于STM32物联网网关还可应用于智能农业领域。通过连接土壤湿度传感器、光照传感器等,可以实时监测农田的土壤湿度和光照等环境参数,并通过连接的水泵控制阀门等执行器,实现对灌溉设备的智能控制。这样,农民可以实现对农田环境的精确监测和灌溉设备的智能控制,提高农作物的产量和品质。 总之,基于STM32物联网网关的应用非常广泛,从智能家居到物流和仓储再到智能农业等领域,都可以利用它来实现设备之间的智能连接和远程控制,实现更加便捷和智能的生活、工作和生产环境。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

物联网客栈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值