说起通信,我们都知道通信分为并行通信和串行通信。并行通信速率快但是占用引脚数多,串行通信速度慢但是占用引脚数很少。
今天我们主要来说串行通信
串行通信还可以分为同步通信和异步通信。
同步通信:带时钟同步信号传输,栗子:SPI、IIC
异步通信:不带时钟同步信号,栗子:UART、单总线
对于同步通信来说,通信双方是通过同步时钟信号进行发送和接收数据的,即每来一个时钟信号,发送方就发送一位数据,这样接收方也可以通过时钟信号来进行数据的解析。
对于异步通信来说,通信双方之间并没有同步时钟信号,为了使接收方能够准确地把发送方发送的信息解析出来,通信双方在通信之前要约定好一个东西,我们叫它波特率,即发送方按照一定的频率去发送数据,接收方也根据这个频率来每隔固定的时间就去读取信号线的电平状态,从而实现了对发送数据的解析。
简单的列几个通信方式:

UART(通用异步收发器):全双工
USART(通用同步异步收发器):全双工
SPI:同步通信  全双工
IIC:同步通信  半双工
单总线:异步通信  半双工

接下来说一下STM32F103中的串口通信
在F103中,串口1连接的时钟是PCLK2(72MHz),串口2—4连接的时钟是PCLK1(36MHz),这在一会我们计算波特率的时候会用到

STM32F103串口异步通信需要配置的参数

 起始位数据位(8位或者9位)奇偶校验位(第9位)停止位(1,15,2位)波特率设置


USART逻辑框图(看一下,了解一下即可)

常用函数

void USART_Init(); //串口初始化:波特率,数据字长,奇偶校验,硬件流控以及收发使能
void USART_Cmd();//使能串口
void USART_ITConfig();//使能相关中断void USART_SendData();//发送数据到串口,DR
uint16_t USART_ReceiveData();//接受数据,从DR读取接受到的数据FlagStatus USART_GetFlagStatus();//获取状态标志位
void USART_ClearFlag();//清除状态标志位
ITStatus USART_GetITStatus();//获取中断状态标志位
void USART_ClearITPendingBit();//清除中断状态标志位


关于这个波特率的计算我简单的说一下:
STM32F103是支持设置分数波特率的,即用来存储波特率数值的寄存器的低四位用来存储小数部分,高12位用来存储整数部分。但是小数部分的数值也不是随便取的,它只能够取十六分之一的整数倍,即1/16、2/16、3/16…
所以说我们平常所常用的波特率并不是连续的数值,我们一般用的有4800、9600、115200等等,根据上面那个例子,假如说我们现在需要设置波特率为115200,那么把115200带入到官方手册中提供的公式中,即

式子中的Tx/Rx baud就是我们要的波特率115200,然后fck就是我们上面说的PCLK1和PCLK2了,PCLK1(36MHz)PCLK2(72MHz),然后USARTDIV代表的就是我们要往相应的寄存器中存储的数值。我们把115200带入到式子中求得USARTDIV=39.625,那么我们就直接把整数部分的39转换成16进制存入到相应寄存器(相应的寄存器为波特比率寄存器USART_BRR)的高12位,然后我们再对小数部分进行处理,我们是直接让小数乘以16,这其实就是十进制小数和十六进制小数之间的转换,我们把十进制的0.625转换成十六进制就是0.1,那么我们就把1存入到相应寄存器的低4位。再比如如果我们算出来小数部分是0.75,那么0.75转换成十六进制就是0.11,也就是0.C,那么我们就把0X0C存入到相应寄存器的低4位。大概的操作就是这样的,如果我们是用库函数来编写代码的话,这些内部计算我们不需要关注,我们只需要调用库函数USART_Init(),然后传入参数就行,参数就是我们要设置的波特率。
波特比率寄存器

串口配置一般步骤

1、串口时钟使能,GPIO时钟使能:RCC_APB2PeriphClockCmd();
2、串口复位:USART_DeInit(); 这一步不是必须的
3、GPIO端口模式设置:GPIO_Init(); 串口发送引脚配置成复用推挽输出   接收引脚配置成浮空输入
4、串口参数初始化:USART_Init();
5、开启中断并且初始化NVIC(如果需要开启中断才需要这个步骤)NVIC_Init();USART_ITConfig();
6、使能串口:USART_Cmd();
7、编写中断处理函数:USARTx_IRQHandler();
8、串口数据收发:
void USART_SendData();//发送数据到串口,DR
uint16_t USART_ReceiveData();//接受数据,从DR读取接受到的数据
9、串口传输状态获取:
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);
void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);

部分代码

u8 USART_RX_BUF[USART_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.
//接收状态
//bit15, 接收完成标志
//bit14, 接收到0x0d
//bit13~0,   接收到的有效字节数目
u16 USART_RX_STA=0;       //接收状态标记   void uart_init(u32 bound){//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   GPIOA.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);//初始化GPIOA.9//USART1_RX     GPIOA.10初始化GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10  //Usart1 NVIC 配置NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;       //子优先级3NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;         //IRQ通道使能NVIC_Init(&NVIC_InitStructure);    //根据指定的参数初始化VIC寄存器//USART 初始化设置USART_InitStructure.USART_BaudRate = bound;//串口波特率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); //初始化串口1USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断USART_Cmd(USART1, ENABLE);                    //使能串口1 }void USART1_IRQHandler(void)                 //串口1中断服务程序{u8 Res;if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾){Res =USART_ReceiveData(USART1);    //读取接收到的数据if((USART_RX_STA&0x8000)==0)//接收未完成{if(USART_RX_STA&0x4000)//如果已经接收到了0x0d{if(Res!=0x0a)USART_RX_STA=0;//如果在接收到0x0d之后,没有紧接着就接收到0z0a,那么就是接收错误,重新开始else USART_RX_STA|=0x8000; //接收完成了 }else //还没收到0X0D{   if(Res==0x0d)USART_RX_STA|=0x4000;else{USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;USART_RX_STA++;if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收   }      }}          }

在这里面,也没啥东西,这种处理方法和我之前博客里写的输入捕获有点相似,大家可以去看一下,输入捕获比这个还要稍微复杂一点点。
我们主要就是注意一下它里面是通过一个变量USART_RX_STA 来控制数据的接收与处理的。

USART_RX_STA的低14位是用来记录我们接收到的有效数据个数的,即每接收到一个有效数据,它的值就加一,如果接收到了0X0D,那么就把这个变量USART_RX_STA的次高位置1,如果我们紧接着又接收到了0X0A的话,那么就把这个变量USART_RX_STA的最高位置1,此时就代表着我们这次的传输完成了。
注意:0x0D和0X0A是ASCII中的控制字符,0x0D含义是Carriage return(回车键),0X0A含义是Line feed(换行键),串口发送后的0d 0a表示回车换行

主函数部分代码

 int main(void){     u16 t;  u16 len;    u16 times=0;delay_init();           //延时函数初始化    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级uart_init(115200);     //串口初始化为115200while(1){if(USART_RX_STA&0x8000){                       len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度printf("\r\n您发送的消息为:\r\n\r\n");for(t=0;t<len;t++){USART_SendData(USART1, USART_RX_BUF[t]);//向串口1发送数据while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束}printf("\r\n\r\n");//插入换行USART_RX_STA=0;}else{times++;if(times%200==0)printf("请输入数据,以回车键结束\n");  delay_ms(10);   }}   }

注意配置串口初始化函数的入口参数就是我们要设置的波特率

uart_init(115200);    //串口初始化,波特率设置为115200

开启串口接收中断(即接收缓冲器非空 USART_IT_RXNE)

USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断

判断产生的中断是否是接收中断

 if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)

STM32中的串口通信相关推荐

  1. STM32中的串口通信的基础知识

    目录 串口通信基本原理 并行通信与串行通信 串行通信中单工,半双工和全双工的区别 按通信方式不同又可分为异步通信和同步通信 常见的串行通信接口 STM32中的5个串口引脚 STM32中USART的通信 ...

  2. STM32中关于串口通信的printf()函数重定向问题

    在STM32串口通信程序中使用printf()函数发送数据非常方便,但是需要先进行简单的配置,下面介绍两种配置方法. 方法一.对工程属性进行配置 第1步:在main.c中包含"stdio.h ...

  3. 在WEBSTART中实现串口通信(WINDOWS版)

    在WEBSTART中实现串口通信(WINDOWS版) 看到这个题目,你是不是问:这跟在JAVA中实现串口通信有什么区别?在JAVA中能做在WEBSTART中不就 一样吗? 真聪明,的确是这样的,但事实 ...

  4. c# wifi串口通信_在C#中实现串口通信的方法

    通常,在C#中实现串口通信,我们有四种方法: 第一:通过MSCOMM控件这是最简单的,最方便的方法.可功能上很难做到控制自如,同时这个控件并不是系统本身所带,所以还得注册,不在本文讨论范围.可以访问h ...

  5. STM32和ROS串口通信常见问题汇总答疑

    STM32和ROS串口通信常见问题汇总答疑 大家好,我是白茶清欢,最近看了博客文章<stm32和ros的串口通信>有很多问题的评论,这里汇总回复一下. 问题1:运行时报错如下: rosru ...

  6. Ardunio 完成STM32板子的串口通信

    目录 一.Ardunio简介 二.Ardunio配置 三.实验 四.其它 实验目的: 安装 Ardunio IDE 和相关软件支持库,在Ardunio 上完成STM32板子的串口通信程序:持续向串口输 ...

  7. 普中单片机--串口通信(2)---通过串口助手发送数据点LED

    普中单片机–串口通信(2) 通过串口助手发送数据 点亮LED 软件部分 #include <reg52.h> #define jingzhen 12000000UL /*使用12.0M晶体 ...

  8. 普中单片机---串口通信(1)--通过按键控制发送

    普中单片机-串口通信(1)–通过按键控制发送 软件代码 #include <reg52.h>#define jingzhen 12000000UL /*使用12.0M晶体*/ #defin ...

  9. matlab+stm32通讯,matlab与stm32之间利用串口通信记录

    1.涉及到的函数 serial,fopen,fclose, instrfindall,instrhwinfo, fprintf,fscanf,fwrite,fread,isempty 1.1 串口函数 ...

最新文章

  1. java数据结构堆_Java 数据结构-堆实现
  2. 日志服务接入方式之Unity 3D篇
  3. 可能是全网写特征工程最通透的...
  4. WAF机制及绕过方法总结:注入篇
  5. python pandas dataframe基本使用整理
  6. 经典sql语句50题_SQL面试经典50题:带你从建表开始
  7. magento 增加一个layout template
  8. 使用LiteOS Studio图形化查看LiteOS在STM32上运行的奥秘
  9. 东农计算机应用与技术,东农16春《计算机应用与技术》在线作业.doc
  10. VSCode插件开发全攻略
  11. C++ select模型聊天室初版
  12. SPSS论证有这样的问题或错误?【SPSS 068期】
  13. CAD绘图教程之CAD中怎么进行消防计算?
  14. 大数据——海量数据处理的基本方法总结
  15. 和Keyle一起学ShaderForge - Overview
  16. 2022年强网杯rcefile wp
  17. DDoS 攻击防御方法
  18. 5月第2周业务风控关注 | 央行:严禁未经授权认可的APP接入征信系统
  19. OI组合数学相关知识点
  20. python 爬虫模拟点击_爬虫——模拟点击动态页面

热门文章

  1. 虚拟机下安装ubuntu
  2. Hive,Hbase shell 中文变问号(??) 的解决方法
  3. Entity Framework默认值字段不起作用解决方法
  4. informix 数据库锁表分析和解决方法
  5. hyperv虚拟机网络速度慢问题的解决办法
  6. 【报告分享】2020新零售直播活力报告.pdf(附下载链接)
  7. 【华为推荐】基于反事实学习的推荐系统研究.pdf(附pdf下载链接)
  8. Python实战从入门到精通第十四讲——定义有默认参数的函数
  9. 42岁老码农找工作记录
  10. BERT4Rec:当NLP王者BERT进军推荐领域