概述
通信的两种方式:
并行通信
-传输原理:数据各个位同时传输。
-优点:速度快
-缺点:占用引脚资源多
串行通信
-传输原理:数据按位顺序传输。
-优点:占用引脚资源少
-缺点:速度相对较慢
串行通信分类(按照数据传送方向)
单工(a):
数据传输只支持数据在一个方向上传输
半双工(b):
允许数据在两个方向上传输,但是,在某一时刻,只允许数据在一个方向上传输,它实际上是一种切换方向的单工通信;
全双工(c):
允许数据同时在两个方向上传输,因此,全双工通信是两个单工通信方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力。
串行通信的通信方式
同步通信(带时钟同步信号传输):
SPI,IIC通信接口
异步通信(不带时钟同步信号传输):
UART(通用异步收发器),单总线
STM32的串口通信接口
UART:通用异步收发器。
USART:通用同步异步收发器。
大容量STM32F10x系列芯片,包含3个USART和2个UART
UART异步通信方式引脚连接方法:
-RXD:数据输入引脚。数据接受。
-TXD:数据发送引脚。数据发送。
UART异步通信方式特点:
全双工异步通信。
分数波特率发生器系统,提供精确的波特率。
-发送和接受共用的可编程波特率,最高可达4.5Mbits/s
可编程的数据字长度(8位或者9位);
可配置的停止位(支持1或者2位停止位);
可配置的使用DMA多缓冲器通信。
单独的发送器和接收器使能位。
检测标志:① 接受缓冲器 ②发送缓冲器空 ③传输结束标志
多个带标志的中断源。触发中断。
其他:校验控制,四个错误检测标志。
USART的寄存器可以参考STM32中文参考手册的第25部分中的第六小节。
《STM32F1开发指南-库函数版本》- 5.3 usart串口文件夹介绍 -第9章 串口实验
常用的串口相关寄存器
USART_SR状态寄存器
USART_DR数据寄存器
USART_BRR波特率寄存器 (填写下面计算后的数值)
USART_CR1控制寄存器
根据该图可了解串行通信的相关配置。根据下半图,可得到波特率的计算方法:
串口操作相关库函数
void USART_Init(); //串口初始化:波特率,数据字长,奇偶校验,硬件流控以及收发使能
void USART_Cmd();//使能串口
void USART_ITConfig();//使能相关中断
void USART_SendData();//发送数据到串口,DR
uint16_t USART_ReceiveData();//接受数据,从DR读取接受到的数据
FlagStatus USART_GetFlagStatus();//获取状态标志位,SR
void USART_ClearFlag();//清除状态标志位,SR
ITStatus USART_GetITStatus();//获取中断状态标志位,SR
void USART_ClearITPendingBit();//清除中断状态标志位,SR
实验4串口实验的代码中FWLib文件夹下stm32f10x_usart.c下的stm32f10x_usart.h下,可找到相关函数的声明与定义,然后可再进行追溯。
串口配置一般步骤
①串口时钟使能,GPIO时钟使能:RCC_APB2PeriphClockCmd();
②串口复位:USART_DeInit(); 这一步不是必须的
③GPIO端口模式设置:GPIO_Init(); 模式设置为GPIO_Mode_AF_PP
④串口参数初始化:USART_Init();
⑤开启中断并且初始化NVIC(如果需要开启中断才需要这个步骤)
NVIC_Init();
USART_ITConfig();
⑥使能串口:USART_Cmd();
⑦编写中断处理函数:USARTx_IRQHandler();
⑧串口数据收发:
void USART_SendData();//发送数据到串口,DR
uint16_t USART_ReceiveData();//接受数据,从DR读取接受到的数据
⑨串口传输状态获取:
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);
void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);
具体实现:
第一步:新建模版,并使能串口时钟和GPIO时钟。
跟之前一样,先建立一个简单的模版。
在USER文件夹下找到system_stm32f10x.c下的stm32f10x_rcc.h.中找到void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);
之前就知道这个函数是用来使能的,然后先Go To xxx找到函数的定义后,在函数中assert_param(IS_RCC_APB2_PERIPH(RCC_APB2Periph));
查看函数中的第一个参数RCC_APB2Periph
Go To xxx后我们可以看到,该函数即可使能GPIOA又可使能USART1。故使能语句可这么编写.
void My_USART1_Init(){
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 , ENABLE);
}
串口复位非必须,故可省略。
第二步:GPIO端口模式设置
在USER文件夹下找到system_stm32f10x.c下的stm32f10x_gpio.h.中找到void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
该函数之前已经讲解过了,程序可修改为:
void My_USART1_Init(){
GPIO_InitTypeDef GPIO_InitStructure; //定义结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE); //使能GPIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 , ENABLE); //使能串口时钟
GPIO_InitStructure.GPIO_Mode= GPIO_Mode_AF_PP; //设置为复用推挽输出
GPIO_InitStructure.GPIO_Pin= GPIO_Pin_9; //引脚9
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz; //速度10MHz
GPIO_Init(GPIOA,&GPIO_InitStructure); //GPIOA模式设置
GPIO_InitStructure.GPIO_Mode= GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_InitStructure.GPIO_Pin= GPIO_Pin_10; //引脚10
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz; //速度10MHz
GPIO_Init(GPIOA,&GPIO_InitStructure); //GPIOA模式设置
}
这边要注意模式,模式设置是要根据STM32中文参考手册中的第8张8.1.11中关于外设的GPIO配置,如下图:
第三步:串口参数初始化和使能串口
在USER文件夹下找到system_stm32f10x.c下的stm32f10x_usart.h.中找到void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);、void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState);
,程序可修改为:
void My_USART1_Init(){
GPIO_InitTypeDef GPIO_InitStructure; //定义GPIO结构体
USART_InitTypeDef USART_InitStructure;//定义USART结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE); //使能GPIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 , ENABLE); //使能串口时钟
GPIO_InitStructure.GPIO_Mode= GPIO_Mode_AF_PP; //设置为复用推挽输出
GPIO_InitStructure.GPIO_Pin= GPIO_Pin_9; //引脚9
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz; //速度10MHz
GPIO_Init(GPIOA,&GPIO_InitStructure); //GPIOA模式设置
GPIO_InitStructure.GPIO_Mode= GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_InitStructure.GPIO_Pin= GPIO_Pin_10; //引脚10
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz; //速度10MHz
GPIO_Init(GPIOA,&GPIO_InitStructure); //GPIOA模式设置
USART_InitStructure.USART_BaudRate=115200;//波特率
USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None ;//硬件流:无
USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;//TX和RX都使能故使用|
USART_InitStructure.USART_Parity=USART_Parity_No;//奇偶校验位:无
USART_InitStructure.USART_StopBits=USART_StopBits_1;//停止位:1
USART_InitStructure.USART_WordLength=USART_WordLength_8b;//字长:8位
USART_Init(USART1,&USART_InitStructure);//串口参数初始化
USART_Cmd(USART1,ENABLE); //使能串口
}
第四步:开启中断并且初始化NVIC
在FWLIB文件夹下找到misc下找到void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
,函数中找到;assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup));
,在Go To xxx,查看填写格式,选择NVIC_PriorityGroup_2
,即:两位响应优先级和两位抢占优先级。
在USER文件夹下找到system_stm32f10x.c下的stm32f10x_usart.h.中找到void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState)
程序可修改为:
#include "stm32f10x.h"
void My_USART1_Init(){
GPIO_InitTypeDef GPIO_InitStructure; //定义GPIO结构体
USART_InitTypeDef USART_InitStructure;//定义USART结构体
NVIC_InitTypeDef NVIC_InitStructure;//定义NVIC结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE); //使能GPIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 , ENABLE); //使能串口时钟
GPIO_InitStructure.GPIO_Mode= GPIO_Mode_AF_PP; //设置为复用推挽输出
GPIO_InitStructure.GPIO_Pin= GPIO_Pin_9; //引脚9
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz; //速度10MHz
GPIO_Init(GPIOA,&GPIO_InitStructure); //GPIOA模式设置
GPIO_InitStructure.GPIO_Mode= GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_InitStructure.GPIO_Pin= GPIO_Pin_10; //引脚10
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz; //速度10MHz
GPIO_Init(GPIOA,&GPIO_InitStructure); //GPIOA模式设置
USART_InitStructure.USART_BaudRate=115200;//波特率
USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None ;//硬件流:无
USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;//TX和RX都使能故使用|
USART_InitStructure.USART_Parity=USART_Parity_No;//奇偶校验位:无
USART_InitStructure.USART_StopBits=USART_StopBits_1;//停止位:1
USART_InitStructure.USART_WordLength=USART_WordLength_8b;//字长:8位
USART_Init(USART1,&USART_InitStructure);//串口参数初始化
USART_Cmd(USART1,ENABLE); //使能串口
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//接收缓冲区非空就接收USART_IT_RXNE,打开中断服务
NVIC_InitStructure.NVIC_IRQChannel=USART1_IRQn;//在stm32f10x.h头文件中上面部分有些IRQn。
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;//开不开器通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;//设置抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;//设置抢占子优先级
NVIC_Init(&NVIC_InitStructure);
}
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
}
第五步:编写中断处理函数
在文件stm32f10x_usart.h文件下打开:ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT);
#include "stm32f10x.h"
void My_USART1_Init(){
GPIO_InitTypeDef GPIO_InitStructure; //定义GPIO结构体
USART_InitTypeDef USART_InitStructure;//定义USART结构体
NVIC_InitTypeDef NVIC_InitStructure;//定义NVIC结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE); //使能GPIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 , ENABLE); //使能串口时钟
GPIO_InitStructure.GPIO_Mode= GPIO_Mode_AF_PP; //设置为复用推挽输出
GPIO_InitStructure.GPIO_Pin= GPIO_Pin_9; //引脚9
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz; //速度10MHz
GPIO_Init(GPIOA,&GPIO_InitStructure); //GPIOA模式设置
GPIO_InitStructure.GPIO_Mode= GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_InitStructure.GPIO_Pin= GPIO_Pin_10; //引脚10
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz; //速度10MHz
GPIO_Init(GPIOA,&GPIO_InitStructure); //GPIOA模式设置
USART_InitStructure.USART_BaudRate=115200;//波特率
USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None ;//硬件流:无
USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;//TX和RX都使能故使用|
USART_InitStructure.USART_Parity=USART_Parity_No;//奇偶校验位:无
USART_InitStructure.USART_StopBits=USART_StopBits_1;//停止位:1
USART_InitStructure.USART_WordLength=USART_WordLength_8b;//字长:8位
USART_Init(USART1,&USART_InitStructure);//串口参数初始化
USART_Cmd(USART1,ENABLE); //使能串口
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//接收缓冲区非空就接收USART_IT_RXNE,打开中断服务
NVIC_InitStructure.NVIC_IRQChannel=USART1_IRQn;//在stm32f10x.h头文件中上面部分有些IRQn。
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;//开不开器通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;//设置抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;//设置抢占子优先级
NVIC_Init(&NVIC_InitStructure);
}
void USART1_IRQHandler(void){
u8 res;
if(USART_GetITStatus(USART1,USART_IT_RXNE)){//若是接收到中断
res=USART_ReceiveData(USART1);//接收数据
USART_SendData(USART1,res);//接收到在发送数据,才可以在串口监视器中看到数据
}
}
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
My_USART1_Init();
while(1);
}
最后编译就通过了,注意要把模版中文件夹SYSTEM中的uart删除,因为定义重复了。
之后就可以上传程序,然后利用串口调试器,注意调试器中的设置要跟程序一样,波特率为115200,停止位1,等等。最终的实验现象就是发送什么,最后在调试软件中就会看到什么。
SYSTEM文件夹下,usart.c文件中,可看到以下代码(实验4串口实验):
void USART1_IRQHandler(void) //串口1中断服务程序
{
u8 Res;
#if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
OSIntEnter();
#endif
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
Res =USART_ReceiveData(USART1); //读取接收到的数据
if((USART_RX_STA&0x8000)==0)//接收未完成,判断最高位是不是0,是0表示接收未完成,则往下执行。若为1则不往下执行(上一次接收没清空)
{
if(USART_RX_STA&0x4000)//接收到了0x0d,若第二位为1则表示接收到0x0d,在往下判断下一位是不是0x0a,若不是重新开始,是的话则则给第一位置1
{
if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
else USART_RX_STA|=0x8000; //接收完成了
}
else //还没收到0X0D,第二位不是1,则判断什么时候接收到0x0d时,将第二位置为1
{
if(Res==0x0d)USART_RX_STA|=0x4000;
else//若一直没收到的话,则一直讲Res变量中的数值给变量,且通过位与来判断该位是否溢出(若第1、2位有数据给他清零)
{
USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
USART_RX_STA++;
if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收
}
}
}
}
#if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
OSIntExit();
#endif
}
#endif
最后
以上就是甜美哈密瓜为你收集整理的STM32精英版(正点原子STM32F103ZET6开发板)学习篇8——串行通信的全部内容,希望文章能够帮你解决STM32精英版(正点原子STM32F103ZET6开发板)学习篇8——串行通信所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复