串行通信
通信分为串行通信和并行通信,区别如下,同样是发送0101的数据:
可以看的出来,串行通信的优点是消耗的数据线会小一些。
而并行通信的优点是传输的速度快。
通常我们会选择使用串行通信来进行设备间的通信,这样会节省单片机引脚资源,同时传输的速率也是我们可以接受的。
串行通信又分为异步通信和同步通信。
异步通信的数据传输按帧传输,每一帧需要包含起始位,数据位,校验位和停止位,通过起始位和校验位我们就可以确定每帧数据的范围,校验位用来检验传输的数据是否有误。
同步通信由1~2个同步字符和多位数据位组成.同步字符用于触发同步时钟开始发送或是接收数据。多字节数据之间不允许有空隙,每位占用的时间相同。
小总结一下,异步通信对硬件的要求较低,实现起来简单灵活,但由于传输每个字节都需要建立一次同步,所以工作速度较低,单片机系统中主要是用异步通信。同步通信对硬件要求高,但是传输速度快,适用于成批的数据传输。
STM32F103中的USART
STM32F103C8T6一共有三个USART外设资源,具体的功能引脚可以参考下图(来自b站江科大自化协)
接线的时候需要注意的是,两个设备之间通信,设备A的RX(接收端)需要接到设备B的TX(发送端),设备A的TX(发送端)需要接到设备B的RX(接收端)。所以两个设备之间使用串行通信只需要两根线即可,可以看出来确实是很节省引脚资源,甚至如果A不需要发送数据只需要接收的话,是连TX(发送端)都不用连的。
根据手册的参考,我们需要将TX的GPIO口设置为推挽复用输出,RX的GPIO口设置为浮空输入或是上拉输入(和传输有关的我们设置为浮空输入)。
以下是官方给的参考手册的介绍:
随便看看就好,不影响我们的编程,因为官方固件库都给我们封装好了。
固件库函数
初始化USART
参数一选择USART资源,STM32F103C8T6可以选的有USART1,USART2,USART3,要看准对应的引脚,不要接错了。
参数二参数USART_InitStruct类型的变量,通过这个变量的成员来对USART进行配置。
USART_BandRate:波特率,常用的有9600,115200等,最主要的是发送端和接收端的配置要一样,这样才可以进行通信。
USART_WordLength:传输的字长,可选的参数有USART_WordLength_8b或是USART_WordLength_9b。一般是选择8b,如果有校验位的话也就是加一位选9b。
USART_StopBits:停止位长度,一般选择USART_StopBits_1。这个和波特率一样没有什么特定需求,只需要发送方和接收方配置一样就行。
USART_Parity:校验,可以选择奇校验,偶校验,或者是不校验。奇校验就是在传输的数据后面再加一位校验位,在接收方收到数据之后,把接收到的数据(包括校验位)集中起来统计一下1出现的次数,如果为奇数那么就表示数据无误(实际上还是有可能是有问题的),偶校验和奇校验差不多,区别在于偶校验要求1出现的次数为偶数。如果选择了校验,那么上面的字长需要选择9b的选项。
USART_Mode:USART传输的模式,可选的有USART_Mode_RX和USART_Mode_TX。分别表示选择接收模式和发送模式,如果都要的话,可以用|连接起来,填入参数为USART_Mode_Rx|USART_Mode_Tx。
USART_HardwareFlowControl:硬件控制流选择,一般情况下用不着,我们选择USART_HardwareFlowControl_None即可。
上电USART
参数一选择USART资源,参数二选择是否使能(ENABLE/DISABLE)。
初始化和上电之后就可以开始发送数据了。
发送数据
参数一选择USART资源,参数二选择要传输数据。
这个函数执行完我们还需要等待数据传输完毕,等待的方法就是不断访问对应的标志位,直到标志位显示发送完毕即可。
查看标志位
参数一选择USART资源,参数二选择要访问的标志位。
检测发送是否完毕我们选择USART_FLAG_TXE
检测是否有数据可以接收USART_FLAG_RXNE
发送数据完毕之后标志位会自动置回去,所以我们不需要管。如果是接收数据的话,在接收完毕之后我们还要把对应的标志位手动清除。
清除标志位
参数一选择USART资源,参数二选择要清除的参数。
如果是只需要发送数据的话那么上面的函数就够用了,但如果还要接收数据的话还需要配置一下中断。
配置USART中断
参数一选择USART资源,参数二选择因为什么而中断,我们选择USART_IT_RXNE,即接收到数据之后进入中断,进入中断之后要根据上面的函数来获取标志位,清除标志位,然后再用接收数据的函数来获取数据。参数三选择ENABLE。
除了这个函数配置完中断之后还需要配置一下NVIC中断优先级配置,本文主要是讲USART的,就不细说,可以参考下面给出的代码。有关中断的内容,我后续专门写中断配置的时候会补上。
配置中断完毕之后,使用的中断函数为void USART1_IRQHandler(void)
中断函数的函数名可以在文件“startup_stm32f10x_md.s”中找到。
接收数据
参数一选择USART资源,返回接收到的数据。
这个函数我们在中断里调用,在上一条配置完中断以后,收到数据之后进入中断,然后使用这条函数来获取数据。
接线
使用USB转TTL的模块将STM32F103C8T6连接到电脑上,然后电脑上使用串口助手来查看传输的结果。我使用的串口助手是b站江科大自化协提供的,大家随便上网搜一个串口助手都能用。
将USB转TTL模块的RX接到STM32F103C8T6的USART1_TX(GPIOA的10号口)上,TX接到USART1_RX(GPIOA的9号口)上。
代码
#include "stm32f10x.h" // Device header
#include "Delay.h" //延时函数
#include "OLED.h" //OLED显示屏驱动函数
//中断函数
void USART1_IRQHandler(void){
//判断数据接收标志位
if(SET==USART_GetFlagStatus(USART1,USART_FLAG_RXNE)){
uint16_t data=USART_ReceiveData(USART1); //读取出接收的数据
OLED_ShowHexNum(2,1,data,4); //使用OLED显示接收的数据
USART_ClearITPendingBit(USART1,USART_FLAG_RXNE); //清除数据接收标志位
}
}
void sendbyte(uint16_t Data){
//发送数据
USART_SendData(USART1,Data);
//等待数据发送完毕
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}
int main(void){
OLED_Init();
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef itd;
itd.GPIO_Mode=GPIO_Mode_AF_PP; //复用推挽输出
itd.GPIO_Pin=GPIO_Pin_9; //TX引脚
itd.GPIO_Speed=GPIO_Speed_2MHz; //这个随意
GPIO_Init(GPIOA,&itd);
itd.GPIO_Mode=GPIO_Mode_IN_FLOATING;//浮空输入
itd.GPIO_Pin=GPIO_Pin_10; //RX引脚
GPIO_Init(GPIOA,&itd);
USART_InitTypeDef uitd;
uitd.USART_BaudRate=9600; //波特率
uitd.USART_HardwareFlowControl=USART_HardwareFlowControl_None; //硬件流控制
uitd.USART_Mode=USART_Mode_Rx|USART_Mode_Tx; //串口模式
uitd.USART_Parity=USART_Parity_No; //校验
uitd.USART_StopBits=USART_StopBits_1; //停止位长度
uitd.USART_WordLength=USART_WordLength_8b; //传输的字长
USART_Init(USART1,&uitd);
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); //开启USART接收中断
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //NVIC中断配置
NVIC_InitTypeDef nitd;
nitd.NVIC_IRQChannel=USART1_IRQn;
nitd.NVIC_IRQChannelCmd=ENABLE;
nitd.NVIC_IRQChannelPreemptionPriority=2;
nitd.NVIC_IRQChannelSubPriority=2;
NVIC_Init(&nitd);
USART_Cmd(USART1,ENABLE); //上电USART
uint16_t data=0x00;
while(1){
sendbyte(data);
data++;
Delay_ms(500);
}
}
效果
使用USART将调试信息打印到电脑的串口助手上
第一步先对Keil进行设置
选中魔术棒再勾上Use MicroLIB。
第二步加入库文件
#include <stdio.h>
第三步重写fputs函数,可以参考下面的写法。因为printf函数的底层就是调用的fputs函数,所以我们只需改写fputs即可。
int fputc(int ch, FILE *f){
//sendbyte为自己写的发送一个数据的函数
sendbyte(ch);
return ch;
}
接下来就可以在程序中使用printf这个函数了,打印的内容会通过USART传输到电脑上,可以通过串口助手来查看。
代码
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include <stdio.h>
//中断函数
void USART1_IRQHandler(void){
//判断数据接收标志位
if(SET==USART_GetFlagStatus(USART1,USART_FLAG_RXNE)){
uint16_t data=USART_ReceiveData(USART1); //读取出接收的数据
OLED_ShowHexNum(2,1,data,4); //使用OLED显示接收的数据
USART_ClearITPendingBit(USART1,USART_FLAG_RXNE); //清除数据接收标志位
}
}
void sendbyte(uint16_t Data){
//发送数据
USART_SendData(USART1,Data);
//等待数据发送完毕
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}
int fputc(int ch, FILE *f){
sendbyte(ch);
return ch;
}
int main(void){
OLED_Init();
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef itd;
itd.GPIO_Mode=GPIO_Mode_AF_PP; //复用推挽输出
itd.GPIO_Pin=GPIO_Pin_9; //TX引脚
itd.GPIO_Speed=GPIO_Speed_2MHz; //这个随意
GPIO_Init(GPIOA,&itd);
itd.GPIO_Mode=GPIO_Mode_IN_FLOATING;//浮空输入
itd.GPIO_Pin=GPIO_Pin_10; //RX引脚
GPIO_Init(GPIOA,&itd);
USART_InitTypeDef uitd;
uitd.USART_BaudRate=9600; //波特率
uitd.USART_HardwareFlowControl=USART_HardwareFlowControl_None; //硬件流控制
uitd.USART_Mode=USART_Mode_Rx|USART_Mode_Tx; //串口模式
uitd.USART_Parity=USART_Parity_No; //校验
uitd.USART_StopBits=USART_StopBits_1; //停止位长度
uitd.USART_WordLength=USART_WordLength_8b; //传输的字长
USART_Init(USART1,&uitd);
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); //开启USART接收中断
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //NVIC中断配置
NVIC_InitTypeDef nitd;
nitd.NVIC_IRQChannel=USART1_IRQn;
nitd.NVIC_IRQChannelCmd=ENABLE;
nitd.NVIC_IRQChannelPreemptionPriority=2;
nitd.NVIC_IRQChannelSubPriority=2;
NVIC_Init(&nitd);
USART_Cmd(USART1,ENABLE); //上电USART
uint16_t data=0;
while(1){
printf("Hello World! %d\r\n",data);
data++;
Delay_ms(500);
}
}
效果
串口助手需要改成接收文本模式。
参考
b站江科大自化协
《STM32F10xxx参考手册(中文)》
《ARM Cortex-M3 嵌入式原理及应用 基于STM32F103微控制器》