基于MDK5实现STM32串口通信

一、串口协议与RS-232标准

1. 串口协议

串口通信(Serial Communication), 是指外设和计算机间,通过数据信号线 、地线、控制线等,按位进行传输数据的一种通讯方式。
串口通信协议是指规定了数据包的内容,内容包含了起始位、主体数据、校验位及停止位,双方需要约定一致的数据包格式才能正常收发数据的有关规范。在串口通信中,常用的协议包括RS-232、RS-422和RS-485。下面主要讲解RS-232标准。
在这里插入图片描述

串口通讯的数据包由发送设备通过自身的 TXD 接口传输到接收设备的 RXD 接口。在串口通讯的协议层中,规定了数据包的内容,它由启始位、主体数据、校验位以及停止位组成,通讯双方的数据包格式要约定一致才能正常收发数。

串口通信最重要的参数是波特率、数据位、停止位和奇偶校验位。对于两个进行通信的端口,这些参数必须匹配。
1.波特率
这是一个衡量符号传输速率的参数。指的是信号被调制以后在单位时间内的变化,即单位时间内载波参数变化的次数,如每秒钟传送240个字符,而每个字符格式包含10位(1个起始位,1个停止位,8个数据位),此时波特率为240Bd,比特率为10位*240个/秒=2400bps。
2.数据位
这是衡量通信中实际数据位的参数。当计算机发送一个信息包,实际的数据往往不会是8位的,标准的值是6、7和8位。如何设置取决于你想传送的信息。比如,标准的ASCII码是0~127(7位)。扩展的ASCII码是0~255(8位)。如果数据使用简单的文本(标准 ASCII码),那么每个数据包使用7位数据。每个包是指一个字节,包括开始/停止位,数据位和奇偶校验位。
3.停止位
用于表示单个包的最后一位。典型的值为1,1.5和2位。由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。
4.奇偶校验位
在串口通信中一种简单的检错方式。有四种检错方式:偶、奇、高和低。对于偶和奇校验的情况,串口会设置校验位(数据位后面的一位),用一个值确保传输的数据有偶个或者奇个逻辑高位。如果是奇校验,校验位为1,这样就有3个逻辑高位。高位和低位不真正的检查数据,简单置位逻辑高或者逻辑低校验。

2. RS-232标准

RS-232标准主要规定了信号的用途、通讯接口以及信号的电平标准。
使用RS-232标准的串口设备间常见的通讯结构如下:
————————————————
在上面的通讯方式中,两个通讯设备的"DB9接口"之间通过串口信号线建立起连接,串口信号线中使用"RS-232标准"传输数据信号。由于RS-232电平标准的信号不能直接被控制器直接识别,所以这些信号会经过一个"电平转换芯片"转换成控制器能识别的"TTL校准"的电平信号,才能实现通讯。

电平标准

根据通讯使用的电平标准不同,串口通讯可分为TTL标准及RS-232标准:

通讯标准电平标准(发送端)
5V TTL逻辑1:2.4V~5V ; 逻辑0:0~0.5V
RS-232逻辑1:-15V~-3V ;逻辑0:+3V~+15V

常见的电子电路中常使用TTL的电平标准,理想状态下,使用5V表示二进制逻辑1,使用0V表示逻辑0;而为了增加串口通讯的远距离传输及抗干扰能力,它使用-15V表示逻辑1,+15V表示逻辑0。使用RS232与TTL电平校准表示同一个信号时的对比见下图:
RS-232与TTL电平标准下表示同一个信号

3. STM32的USART

3.1 简介

STM32芯片具有多个USART外设用于串口通讯,是Universal Synchronous Asynchronous Receiver and Transmitter的缩写,即通用同步异步收发器可以灵活地与外部设备进行全双工数据交换。有别于USART,它还有具有UART外设(Universal Asynchronous Receiver and Transmitter),它是在USART基础上裁剪掉了同步通信功能,只有异步通信。简单区分同步和异步就是看通信时需不需要对外提供时钟输出,我们平时用的串口通信基本都是USART。
STM32的USART输出的是TTL电平信号,若需要RS-232标准的信号可使用MAX3232芯片进行转换。

3.2 功能框图

STM32的USART功能框图包含了USART最核心内容,掌握了功能框图,对USART就会有一个整体的把握,在编程时思路就会很清晰。
在这里插入图片描述
详见:USART—串口通讯

二、STM32的USART串口通讯

完成一个STM32的USART串口通讯程序,要求:
1)设置波特率为115200,1位停止位,无校验位;
2)STM32系统给上位机(win10)连续发送“hello windows!”(win10采用“串口助手”工具接收)。

1. 新建工程

STM32cubeMX的下载与安装以及相关工程的建立在之前的博客已经涉及过,详情可参考:
打开STM32cubeMX程序,点击File下的New Project新建一个工程;
请添加图片描述
Part Number处选择STM32F103C8,然后点击中间栏会出现的一列芯片的信息,再点击Start Project
在这里插入图片描述
点击System Core,选择SYS,在Debug处选择Serial Wire
请添加图片描述
点击RCC,将HES设为Crystal/Ceramic Resonator
请添加图片描述
切换到Clock Configuration界面,勾选PLLCLK,在HCLK处将晶振改为72,注意修改后回车
请添加图片描述
接着切换到Pinout & Configuration ,在Connnectivity下选择USART1串口1(即最小芯片RXD引脚接A10,TXD引脚接A9),Mode选择Asynchronous(即异步通信),此时在下面就可以看到配置的一些信息,包括波特率、字长、校验位和停止位等。

TXD:发送数据输出引脚
RXD:接收数据输出引脚

请添加图片描述
切换到Project Manager界面,在Project下自定义工程名和工程路径,修改Toolchain / IDEMDK-ARM,版本根据自己的需求选择;

注意工程路径中不能出现中文!否则会报错!!

请添加图片描述
最后进入Code Generate界面,在Generated files勾选生成初始化.c/.h文件,再点击GENERATE CODE,即可生成代码;
请添加图片描述
跳出提示框,点击Open Project,直接打开工程。
请添加图片描述

2. 重定向printf函数

继续上面的操作,点击魔法棒Options for Target打开工程配置;
在这里插入图片描述
Target界面勾选中Use MicroLIB,点击OK退出;
请添加图片描述
重定向printf函数代码如下:

// 重定向函数
int fputc(int ch,FILE *f)
{
    uint8_t temp[1]={ch};
    HAL_UART_Transmit(&huart1,temp,1,2);        //UartHandle是串口的句柄
}

注意:定义重定向printf函数记得加stdio.h头文件。

3. 主要代码

3.1 usart.h文件

#ifndef __USART_H__
#define __USART_H__
#include <stdio.h>
int fputc(int ch,FILE *f);	//用户自定义函数声明
#ifdef __cplusplus
extern "C" {
#endif
#include "main.h"
extern UART_HandleTypeDef huart1;
void MX_USART1_UART_Init(void);
#ifdef __cplusplus
}
#endif
#endif

3.2 usart.c文件

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

// 重定向printf函数
int fputc(int ch,FILE *f)
{
    uint8_t temp[1]={ch};
    HAL_UART_Transmit(&huart1,temp,1,2);        //UartHandle是串口的句柄
}
UART_HandleTypeDef huart1;
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();
  }
}

void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(uartHandle->Instance==USART1)
  {
    __HAL_RCC_USART1_CLK_ENABLE();

    __HAL_RCC_GPIOA_CLK_ENABLE();

    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);
  }
}

void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
{

  if(uartHandle->Instance==USART1)
  {
    __HAL_RCC_USART1_CLK_DISABLE();

    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9|GPIO_PIN_10);
  }
}

3.3 main.c文件

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

void SystemClock_Config(void);
int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  while (1)
  {
	  HAL_Delay(1000);	//延时
	  printf("hello windows!\n");	//输出hello windows!
  }
}
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  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();
  }
  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();
  }
}

void Error_Handler(void)
{
  __disable_irq();
  while (1){}
}

#ifdef  USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line){}
#endif /* USE_FULL_ASSERT */

4. 编译程序

调试编译程序,程序运行无误且生成相应的.hex文件;
请添加图片描述
也可以在文件夹MDK-ARM下,打开与工程名Hello相同的子文件夹,可以看到生成的.hex文件
请添加图片描述

5. 程序烧录

烧录过程的操作和所需软件可参考链接:
使用STM32寄存器点亮LED流水灯

接上面包板后,搜索相应串口,再选择刚刚生成的.hex文件,开始编程烧录代码,成功后即返回结果。
请添加图片描述

6. 串口工具运行结果

野火串口调试助手下载链接:野火串口调试助手
提取码:1234

烧录成功后,打开野火串口调试助手程序,点击打开串口
请添加图片描述
可以看到右边窗口持续输出hello windows!
并且每隔1s开发板发送一次数据,串口调试助手每隔1s接收数据。
请添加图片描述

三、总结

通过本次实验学习了串口协议和RS-232标准,并且在上一次的基础上再次使用STM32CubeMX软件自动生成代码,完成了STM32的USART串口通讯程序。不过在最后使用串口助手调试时,遇上了不能正确输出的问题,经过使用他人的设备测试代码,发现代码无误,再借用舍友的板子发现程序能够烧录进去,且正确输出,因此猜测应该是我们的面包板接触有问题。

四、参考文章

STM32最小核心板F103串口通信USART

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值