目录

一、串口通信协议

1、UART简介

2、 UART通信协议

(1)起始位

(2)数据帧

(3)奇偶校验位

(4)停止位

(5)下个起始位

(6)波特率

二、STM32的USART串口通信(中断)

3、要求

2、工程的建立

三、建立STM32与PC之间的通信基础

1、串口助手的使用

2、效果呈现

总结

参考资料:


一、串口通信协议

1、UART简介

嵌入式开发中,UART串口通信协议是我们常用的通信协议(UART、I2C、SPI等)之一,全称叫做通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),是异步串口通信协议的一种,工作原理是将传输数据的每个字符一位接一位地传输,它能将要传输的资料在串行通信与并行通信之间加以转换,能够灵活地与外部设备进行全双工数据交换。

注:在此开发板中,是有USART(Universal Synchronous Asynchronous Receiver and Transmitter通用同步异步收发器)串口的,USART相当于UART的升级版,USART支持同步模式,因此USART 需要同步始终信号USART_CK(如STM32 单片机),通常情况同步信号很少使用,因此一般的单片机UART和USART使用方式是一样的,都使用异步模式。因为USART的使用方法上跟UART基本相同,所以在此就以UART来讲该通信协议了。

2、 UART通信协议

(1)起始位

当未有数据发送时,数据线处于逻辑“1”状态;先发出一个逻辑“0”信号,表示开始传输字符。

(2)数据帧

紧接着起始位之后。资料位的个数可以是4、5、6、7、8等,构成一个字符。通常采用ASCII码。从最低位开始传送,靠时钟定位。

(3)奇偶校验位

资料为加上这一位后,使得“1”的位数应为偶数(偶校验)或奇数(奇校验),以此来校验资料传送的正确性。

(4)停止位

它是一个字符数据的结束标志。可以是1位、1.5位、2位的高电平。 由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。

(5)下个起始位

处于逻辑“1”状态,表示当前线路上没有资料传送,进入空闲状态。

处于逻辑“0”状态,表示开始传送下一数据段。

(6)波特率

表示每秒钟传送的码元符号的个数,是衡量数据传送速率的指标,它用单位时间内载波调制状态改变的次数来表示。

常用的波特率有:9600、115200……

时间间隔计算:1秒除以波特率得出的时间,例如,波特率为9600的时间间隔为1s / 9600(波特率) = 104us。

这些就是UART对通信协议的一些理解,如果想详细了解请参考:基于STM32之UART串口通信协议(一)详解 - LLLIN000 - 博客园 (cnblogs.com)

二、STM32的USART串口通信(中断)

3、要求

2、完成一个STM32的USART串口通讯程序,要求:

1)设置波特率为115200,1位停止位,无校验位;

2)STM32系统给上位机(win10)连续发送“hello windows!”。win10采用“串口助手”工具接收。

2、工程的建立

在我们前面的学习中已经介绍过两种建立工程的方式,分别是STM32CubeMX生成基础代码和使用大佬的工程模板文件。这里我们还是选择用大佬"洋桃电子"编写好的工程文件来完善即可。

我们要实现的功能是USART串口通信,所以我们也照葫芦画瓢创建一个usart.c文件并添加到我们的Basic文件夹里,然后我们下一步该做什么呢,相信你心中一定有答案了,初始化!接下来我们就来看看我们的代码怎么写。

usart.c:

#include "usart.h"//使UASRT串口可用printf函数发送
//在usart.h文件里可更换使用printf函数的串口号
#if 1
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE {int handle;
};
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
_sys_exit(int x){ x = x;
}
//重定义fputc函数
int fputc(int ch, FILE *f){      while((USART_n->SR&0X40)==0);//循环发送,直到发送完毕   USART_n->DR = (u8) ch;      return ch;
}
#endif /*
USART1串口相关程序
*/#if EN_USART1   //USART1使用与屏蔽选择
u8 USART1_RX_BUF[USART1_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.
//接收状态
//bit15, 接收完成标志
//bit14, 接收到0x0d
//bit13~0,   接收到的有效字节数目
u16 USART1_RX_STA=0;       //接收状态标记
/*
USART1专用的printf函数
当同时开启2个以上串口时,printf函数只能用于其中之一,其他串口要自创独立的printf函数
调用方法:USART1_printf("123"); //向USART2发送字符123
*/
void USART1_printf (char *fmt, ...){ char buffer[USART1_REC_LEN+1];  // 数据长度u8 i = 0; va_list arg_ptr;va_start(arg_ptr, fmt);  vsnprintf(buffer, USART1_REC_LEN+1, fmt, arg_ptr);while ((i < USART1_REC_LEN) && (i < strlen(buffer))){USART_SendData(USART1, (u8) buffer[i++]);while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); }va_end(arg_ptr);
}
void USART1_Init(u32 bound){ //串口1初始化并启动//GPIO端口设置GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;     RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);    //使能USART1,GPIOA时钟//USART1_TX   PA.9GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出GPIO_Init(GPIOA, &GPIO_InitStructure);  //USART1_RX   PA.10GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入GPIO_Init(GPIOA, &GPIO_InitStructure); //Usart1 NVIC 配置NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0 ;//抢占优先级3NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;     //子优先级3NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;         //IRQ通道使能NVIC_Init(&NVIC_InitStructure);    //根据指定的参数初始化VIC寄存器 //USART 初始化设置USART_InitStructure.USART_BaudRate = bound;//一般设置为9600;USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式USART_Init(USART1, &USART_InitStructure); //初始化串口USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启ENABLE/关闭DISABLE中断接收到数据时中断 读寄存器DR清零,也可软件手动清零USART_Cmd(USART1, ENABLE);                    //使能串口
}void USART1_IRQHandler(void){ //串口1中断服务程序(固定的函数名不能修改)    u8 Res;//以下是字符串接收到USART1_RX_BUF[]的程序,(USART1_RX_STA&0x3FFF)是数据的长度(不包括回车)//当(USART1_RX_STA&0xC000)为真时表示数据接收完成,即超级终端里按下回车键。//在主函数里写判断if(USART1_RX_STA&0xC000),然后读USART1_RX_BUF[]数组,读到0x0d 0x0a即是结束。//注意在主函数处理完串口数据后,要将USART1_RX_STA清0if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET){  //接收中断(接收到的数据必须是0x0d 0x0a结尾)       Res =USART_ReceiveData(USART1);//(USART1->DR);  //读取接收到的数据printf("%c",Res); //把收到的数据以 a符号变量 发送回电脑     if((USART1_RX_STA&0x8000)==0){//接收未完成         if(USART1_RX_STA&0x4000){//接收到了0x0d             if(Res!=0x0a)USART1_RX_STA=0;//接收错误,重新开始else USART1_RX_STA|=0x8000;  //接收完成了 }else{ //还没收到0X0D                   if(Res==0x0d)USART1_RX_STA|=0x4000;else{USART1_RX_BUF[USART1_RX_STA&0X3FFF]=Res ; //将收到的数据放入数组USART1_RX_STA++;    //数据长度计数加1if(USART1_RX_STA>(USART1_REC_LEN-1))USART1_RX_STA=0;//接收数据错误,重新开始接收     }      }}          }
}
#endif

可以看到咱们的代码中用了 NVIC ,那么这个 NVIC 是个什么玩意儿呢,这就是我们之前说到过的中断,这时候肯定就有小伙伴要问了,我们为什么要用到中断呢,中断的作用是啥呢。首先我们要知道我们的串口发送数据可以用中断方式和扫描方式,那么二者的区别在哪儿呢。在这里我举一个通俗易懂的例子大家就可以明白了。咱们班上有人捡到100块钱,我们把钱交给辅导员希望辅导员找到失主并归还给他。

扫描方式:扫描方式就相当于辅导员找到咱们班然后一个一个问,这钱是不是你的,丢了多少,一个一个排查下去最后找到失主并完成我们的任务(归还财务)。这种方式听起来很傻而且很麻烦,所以接下来我们就要讲到中断的方式啦。

中断方式:辅导员不再主动,而是被动地等着失主自己去办公室询问他,确认失主以后归还钱财。是不是觉得这种方式才是我们的惯性思维,好了,相信大家也能了解中断和扫描的却别了吧。

还记得我们上次说过的咱们的板子上TX是PA.9,RX是PA.10吗,所以我们需要做些啥呢,是的,就是要初始化这两个管脚并开启USART功能,配置好我们需要用的管脚以后我们就需要开启中断,设置中断模式并编写中断函数。相信学过51单片机的小伙伴都接触过中断,当咱们的程序收到中断的信号时就会跳到咱们的中断函数中并执行中断函数,咱们这次需要完成的功能是串口通信,所以咱们的中断信号是接收完成或者是接收错误,大家可以看看上面的注释,也可以参考一下:浅谈USART_RX_STA各位的描述以及是如何实现数据接收的_JackCrum的博客-CSDN博客

好了,说了这么多,既然咱们有了usart.c文件就要有一个usart.h文件来声明咱们刚刚定义的函数,然后方便我们在main.c文件中调用,那么现在我们就来创建这个文件吧。

usart.h:

#ifndef __USART_H
#define __USART_H
#include <string.h>
#include "stdio.h"
#include "sys.h"
#define USART_n     USART1  //定义使用printf函数的串口,其他串口要使用USART_printf专用函数发送#define USART1_REC_LEN            200     //定义USART1最大接收字节数
#define EN_USART1           1       //使能(1)/禁止(0)串口1
extern u8  USART1_RX_BUF[USART1_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
extern u16 USART1_RX_STA;               //接收状态标记    //函数声明
void USART1_Init(u32 bound);//串口1初始化并启动
void USART1_printf(char* fmt,...); //串口1的专用printf函数
#endif

这个文件里装的都是一些函数声明以及一些变量的定义。现在俺们就需要把这两个文件保存并添加进俺们的Basic文件夹里啦,上次我忘了说如何将.h文件添加进咱们的工程中,有的小伙伴就向我反应咱们的编译有问题,今天我就来教教大家如何将.h文件添加进来。

首先咱们还是点击“仙女棒”,然后选择 C/C++ 。

看到下面有个路径选择没有(Include Paths), 咱们点击后面的省略号。

可以看到此时我们就需要选择路径了,我们只需要点击添加并找到我们放.h的文件夹添加进来就OK啦。

在介绍完我们的usart文件之后,我们再来讲讲我们的main.c文件该如何编写,相信大家现在都知道咱们的第一步是啥啦,没错,就是调用我们的初始化函数,然后剩下的只需要将咱们的串口输出函数写在while循环中就OK啦。

main.c:

#include "stm32f10x.h" //STM32头文件
#include "delay.h"
#include "usart.h"int main (void){//主程序//初始化程序RCC_Configuration(); //时钟设置USART1_Init(115200); //串口初始化(参数是波特率)//主循环while(1){printf("hello windows "); //纯字符串发送数据到串口delay_ms(1000); //延时}
}

大家可以看到我们将波特率设置为了题目要求的“115200” ,这也可以根据大家的需求来更改也是没问题的,但是在这里我们一定要记住这个数字,因为后面要用到。各位小伙伴一定发现了一个问题,这里咱们的串口输出用的函数是“printf”,是不是大家对这个函数格外亲切呀。在这里我要说的是,我们尤其要注意这个“printf”函数,当我们只开启一个串口通信时,我们可以直接用“printf”函数输出到我们的串口助手界面。但是如果我们要使用多个串口时,咱们的芯片就无法区分我们要输出的是哪个了,所以这个时候我们就要自己创建每个串口对应的输出函数了。但是在这里我们只开启了USART1,所以可以直接用“printf”函数就足够啦。

现在我们编译并运行咱们的程序,准备好烧录进咱们的板子中,烧录过程如果还有不清楚的小伙伴可以参考我之前的博客(25条消息) 用STM32F103C8T6制作流水灯_txmnQAQ的博客-CSDN博客,由于我们这次实现的功能不像之前的流水灯能通过咱们的LED直接表现出来,我们要借助一定的工具才能观察到我们的实验结果。

三、建立STM32与PC之间的通信基础

在我们之前的学习中就已经建立过板子与我们的PC之间的通信了,也许你还没反应过来,但是这是我们已经做过的事情啦。仔细回想一下我们是怎样将USB-TTL与板子连接并且烧录程序的呢,实际上这就是一种通信连接啊,将板子的发送接到PC的接收,板子的接收接到PC的发送。只是我们之前没有实现UASAT串口发送功能而已。

1、串口助手的使用

所以我们现在暂时不要动烧录时的接线,咱们直接打开咱们的串口助手。(我这里用的是“洋桃电子”家的串口助手,我将它放入百度网盘供大家下载。

链接:https://pan.baidu.com/s/1QsM4UWR557wzmJqYCL2hcw 
提取码:1111

咱们现在打开安装好的串口助手,如下图:

端口号我们可以参考咱们FlyMCU中烧录程序那个端口,我的是COM4端口,大家根据自己的来。是否还记得之前程序编写的时候我让大家记得波特率为 115200 ,我说后面咱们要用的,现在就派上用场啦 。然后我们是在PC上接收来自板子上的“hello windows”,所以我们选择接受模式中的“字符”,设置完成后如下图:

2、效果呈现

现在我们只需要最后一步,打开端口,神奇的事情就会发生啦!

咱们的对话框中会间隔一秒发送一个“hello windows” ,咱们本次实验就完成啦!

注:在我们使用串口助手观察时,咱们的COM4端口,也就是接板子那个端口是被占用了的,这时候如果发现咱们的程序有问题想重新烧录进去,一定要记得先在串口助手中将端口关闭!!!要不然我们直接使用FlyMCU的话就会报错,报告端口被占用。如下图:

总结

通过本次小实验,知道了如何使用stm32串口进行通信,将想表达的信息发送给我们的上位机。我们使用的直接时库函数的例程所以简单很多,后续需要多学习相关寄存器的配置才能真正理解串口通信。

参考资料:

(25条消息) 浅谈USART_RX_STA各位的描述以及是如何实现数据接收的_JackCrum的博客-CSDN博客

基于STM32之UART串口通信协议(一)详解 - LLLIN000 - 博客园 (cnblogs.com)

百度网盘链接:

链接:https://pan.baidu.com/s/1QsM4UWR557wzmJqYCL2hcw 
提取码:1111

STM32USART串口通信相关推荐

  1. STM32平台的USART串口通信

    本文目的是编写stm32串口通信程序,实现stm32上电自动循环发送hello LYJ!!!,并可通过上位机控制stm32串口发送与否.通过亲自做一下USART串口通信实验,学习如何使用stm32平台 ...

  2. 嵌入式系统 实验二 串口通信实验

    实验二 串口通信实验 一.实验目的 1.)了解 USART 的基本特性: 2.)掌握用库函数操作 USART 的方法: 3.)掌握如何使用 STM32 的串口发送和接收数据. 二.实验环境 1.)硬件 ...

  3. 串口通信之————USART

    B站账号:小光学嵌入式 ⏩ 大家好哇!我是小光,嵌入式爱好者,一个想要成为系统架构师的大二学生. ⏩最近开始系统性补习STM32基础知识,规划有:串口通信,Github,Ucos等等. ⏩今天总结一下 ...

  4. python第三方库之学习pyserial库--串口通信

    pyserial串口通信库 1.安装pyserial库 2.填写串口参数的注意事项 3.简单封装一下 4.碰到的bug 1.安装pyserial库 pip install pyserial versi ...

  5. VC串口通信编程-2

    VC串口通信编程 (2009-07-08 13:48:40) 转载▼ Win32串口编程(转:韩耀旭) 在工业控制中,工控机(一般都基于Windows平台)经常需要与智能仪表通过串口进行通信.串口通信 ...

  6. Linux ROS与嵌入式的串口通信

    1.根据ros wiki的官方教程 学习即可,并且有许多例子可供学习 http://wiki.ros.org/rosserial_arduino/Tutorials 2.用ASIO读写设备串行口 AS ...

  7. 投影串口测试程序_【原创】串口通信测试程序

    源代码: using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; ...

  8. labVIEW与单片机实现串口通信的设计过程

    1.概述 在现代测控系统中,我们经常会采用上位机和下位机的开发控制模式.下位机主要是用来采集数据,可以通过嵌入式控制器.单片机控制器.PLC等来实现.上位机主要是图形界面,用来实时显示采集数据,并进行 ...

  9. chord协议模拟实现_C#.NET和单片机串口通信实现监控单片机数字量输入继电器输出模拟量输入模拟量输出...

    一.必备软件和硬件: 1.C#.NET: 2.单片机开发板: 3.通信电缆. 二.通信参数: 1.数据位:8位 2.校验方式:无校验 3.停止位:1位 4.波特率:9600bps 5.通信协议:自定义 ...

最新文章

  1. 【网址收藏】k8s HPA自动伸缩异常:failed to get cpu utilization: missing request
  2. mysql goldengate_oracle GoldenGate实现Oracle到MySQL数据平滑迁移
  3. Rsync 同步搭建
  4. 谁都会做:简单易行的祛斑法 - 健康程序员,至尚生活!
  5. Mycat监控_监控平台安装Mycat-web_作为配置中心注册发现用---MyCat分布式数据库集群架构工作笔记0037
  6. 递归遍历文件及子文件夹下的文件(该代码是复制过来修改过的,如果有侵作者权的话,请作者联系我,立即删除)...
  7. linq学习笔记(5):Count/Sum/Min/Max/Avg
  8. 散粉在哪个步骤用_新手化妆步骤+50个美妆小技巧+化妆知识扫盲
  9. 海量高维数据与纬度约减
  10. 5 年 Java 面试大厂遭淘汰,面试官:连这个源码都不懂
  11. 计算机网络监控,计算机网络连接监控系统.doc
  12. java 图表 word_java Freemark模板生成word图表及目录
  13. Linux 端口扫描
  14. 中级财务管理机考计算机,2017年中级会计师考试无纸化机考技巧
  15. Hardhat以太坊智能合约开发框架基础教程
  16. 05-阿里云视频点播服务与谷粒学院整合视频点播技术
  17. 数学建模-灰色预测模型(预测模型)
  18. [R语言]{实例}车辆车架号VIN码校验函数
  19. 血族手游Lua脚本及资源文件解密
  20. 封基高折价 投资机会扩大

热门文章

  1. 天下乌鸦一般黑...
  2. Android开发6年,互联网寒冬公司倒闭后,耗时3个月北上广求职
  3. 树形结构的json格式封装
  4. do-while语句逐字稿
  5. 古币杂谈:现在玩儿得起的也就只有铜钱儿了
  6. SMT32H7系列DMA和DMAMUX的一点理解
  7. U盘不显示盘符的解决方案
  8. 学记笔记 $\times$ 巩固 · 期望泛做$Junior$
  9. Flutter 弹性布局的基石: flex 和 flexible
  10. [导入]荒城,狂风,斜阳如血......