串口通信的数据包又发送设备通过自身的 TXD 接口传输到接收设备的 RXD 接口,在串口通讯的协议层中,数据结构又下图所示,起始位,数据位,校验位,停止位。
波特率
由于异步通信之中是没有时钟信号的,所以需要在两个设备之间约定好波特率(及每个码元的长度)。
通讯的起始和停止信号
串口通讯的一个数据包从起始信号开始,到停止信号结束,起始信号由一个逻辑 0 表示,停止信号可以由 0.5 , 1 , 1.5 , 或者 2 来表示( 2 个位),只要两台设备约定一致就行。
有效数据
起始位之后就是有效数据(主体数据),一般 5 , 6 , 7 , 8 位长。
数据校验
由于数据通信容易受到外部干扰而导致数据偏差,可以通过加入校验位来解决,一般来说,有奇偶校验, 01 校验,或者无校验。
奇校验要求有效数据加上校验位中 1 的个数位奇数,而偶校验就要求为偶数, 0 校验校验位总位为 0 , 1 校验校验位总为 1。
接下来我们来具体分析这张图:
功能引脚
从这儿我们可以看到,在 STM32 中,它有这么一些功能引脚:
TX :发送数据的引脚
RX:接收数据的引脚
SW_RX:数据接收引脚,只用于单线和智能卡模式,属于内部引脚,没有具体的外部引脚
nRTS:请求以发送, n 代表低电平有效。如果使能这个引脚,当准备接收数据时就会变成低电平,当接收寄存器已经满了,就会置高。
nCTS:清除以发送,低电平有效,若使能,当发送数据之前会检测一下,若是低电平,则可以发送数据,若是高电平,则在当前数据发送完后停止发送。
SCLK:发送器时钟输出引脚,仅适用于同步模式
因为每个芯片对应的 USART 引脚不一样,所以需要去芯片资料自行查阅。
数据寄存器
在 STM32 中 USART 的数据寄存器只有低九位有效用来存放接收和发送的数据,并且第九位数据是否有效取决于 USART 控制寄存器决定。
发送和接收的数据都是放在数据寄存器中,所以数据寄存器实际上包含了两个寄存器,一个是用于发送的可写寄存器 TDR ,一个是用于接收的可读 RDR,当进行读写操作时,数据都是放在这个数据寄存器当中。
TDR 和 RDR 都是介于系统总线和移位寄存器之间。串行通信是一个位一个位传输的, 发送时把 TDR 内容转移到发送移位寄存器,然后把移位寄存器数据每一位发送出去,接收 时把接收到的每一位顺序保存在接收移位寄存器内然后才转移到 RDR。
控制器
当 USART_CR1 寄存器发送使能置 1 ,也就是 TE 置 1 时,启动数据发送(此时可以往数据寄存器里面写入数据。),发送移位寄存器的 数据会在 TX 引脚输出,当 RE 置 1 ,使能 USART 接收, RX 口开始搜索起始位,当确定到起始位后,将接收到的数据放到数据寄存器里,并把 RXNE 位置 1 。如果是同步模式, SCLK 也会输出时钟信号。
停止位时间长短可以通过 USART 控制寄存器2控制。
小数波特率的生成
USART 的接收好和发送使用同一个波特率,计算公式为:
f(CK) 为 USART 时钟,USARTDIV是一个存放波特率的寄存器
校验控制
在 STM32F103 系列的 USART 还支持奇偶校验位,当使用奇偶校验时,数据位加上校验位一共 9 位,此时的 CR1 寄存器 M 位需要置 1 ,设置为 9 位数据位。
中断控制
当然,对于串口通信中,有多个串口中断请求事件,分别代表了各种状态下的情况。
所有 USART 的控制都在下列寄存器里。
此上,就是所有关于 STM32F103 的 USART 的所有寄存器,接下来来实操固件库函数吧!
#define DEBUG_USART_IRQ USART1_IRQn //宏定义中断源,方便移植,增强可读性。
#define DEBUG_USART_TX_GPIO_PORT GPIOA //宏定义TX的GPIO。
#define DEBUG_USART_TX_GPIO_PIN GPIO_Pin_9 //宏定义TX口的引脚。
#define DEBUG_USART_RX_GPIO_PORT GPIOA //宏定义RX的GPIO。
#define DEBUG_USART_RX_GPIO_PIN GPIO_Pin_10 //宏定义RX口的引脚。
#define DEBUG_USART_BAUDRATE 115200 //宏定义波特率。
#define DEBUG_USARTx USART1 //宏定义串口。
static void NVIC_Configuration(void) //这儿是USART的中断配置,static代表限制了此函数只能在本文件之中使用。
{
NVIC_InitTypeDef NVIC_InitStructure; //定义一个NVIC的结构体。
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置优先级分组。
NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ; //配置USART为中断源
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //选择中断主优先级。
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //配置子优先级。
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能中断。
NVIC_Init(&NVIC_InitStructure); //初始化NVIC结构体。
}
void USART_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure; //定义一个GPIO结构体。
USART_InitTypeDef USART_InitStructure; //定义一个USART结构体。
DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE); // 打开串口GPIO的时钟。
DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE); // 打开USART的时钟。
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN; //配置发送引脚。
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //模式选择推挽复用模式。
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //配置引脚速度。
GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure); //初始化发送引脚。
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN; //配置接收引脚。
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //模式选择浮空输入模式。
GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure); //初始化接收引脚。
USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE; //配置波特率。
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //配置数据字长。
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(DEBUG_USARTx, &USART_InitStructure); //初始化USART结构体。
NVIC_Configuration(); //串口中断优先级配置。
USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE); //使能串口接收中断。
USART_Cmd(DEBUG_USARTx, ENABLE); //使能串口。
}
以上,就是串口的所有配置了,此时串口已经打开,我们需要怎么来使用呢?可以直接使用固件库里面的函数。
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch) //发送一个字节的数据
{
USART_SendData(pUSARTx, ch); //发送一个字节数据到USART的数据寄存器,进行发送。
while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET); //等待发送数据寄存器为空,数据发送出去后才退出发送函数。
}
void Usart_SendArray( USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num) //发送一个八位数据的数组。
{
uint8_t i; //定义一个数来记录数组个数。
for(i=0; i<num; i++) //i记录发送数据组数,当数组全部发送完成,退出循环。
{
Usart_SendByte(pUSARTx, array[i]); //每次发送一个字节数据到USART数据寄存器。
}
while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TC)==RESET); //等待发送完成后退出函数。
}
void Usart_SendString( USART_TypeDef * pUSARTx, char *str) //发送字符串。
{
unsigned int k=0; //定义一个数,记录字符串长度。
do
{
Usart_SendByte(pUSARTx, *(str + k)); //字符是有ASCII表表达,所以每一个字符都是一个八位的数据。
k++; //记录数据已发送长度。
} while(*(str + k)!='\0'); //判断下一个字符是否为'\0'。
while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET); //等待发送完成。
}
void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch) //发送16位数据。
{
uint8_t temp_h, temp_l; //将16位数据拆分为两个8位数据。
temp_h = (ch&0XFF00)>>8; //取出高八位。
temp_l = ch&0XFF; //取出低八位。
USART_SendData(pUSARTx,temp_h); //发送高八位。
while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET); //等待高8位发送完成。
USART_SendData(pUSARTx,temp_l); //发送低八位。
while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET); //等待低8位发送完成。
}
以上就是串口的发送函数了,读取数据就要去中断里面读取,中断函数全部在 stm32f10x_it.c 文件里。
#define DEBUG_USART_IRQHandler USART1_IRQHandler //宏定义串口中断函数名字。
void DEBUG_USART_IRQHandler(void)
{
uint8_t ucTemp; //定义一个8位数据来保存数据。
if(USART_GetITStatus(DEBUG_USARTx, USART_IT_RXNE)!=RESET) //确认数据寄存器不为空,读取数据。
{
ucTemp = USART_ReceiveData(DEBUG_USARTx); //读取数据,可以使用全局变量来保存。
}
}
以上,就是关于串口的发送和读取了,不过,对于写习惯了 C 语言的人来说,更加习惯用 C 语言里面的输入输出吧,那串口可以使用这个来进行发送和接收数据吗?当然可以!只需要将函数重定向就可以了。
int fputc(int ch, FILE *f) //重定向c库函数printf到串口,重定向后可使用printf函数。
{
USART_SendData(DEBUG_USARTx, (uint8_t) ch); //发送一个字节数据到串口。
while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET); //等待发送完毕。
return (ch);
}
int fgetc(FILE *f) //重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数。
{
while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET); //等待串口输入数据。
return (int)USART_ReceiveData(DEBUG_USARTx);
}
是不是很简单,相信大家都已经学会了 STM32 的串口使用了吧。
谢谢大家的阅读!!!
项目中用到的一些特殊字符和图标 html代码 XML/HTML Code 复制内容到剪贴板 div ...
错误描述: 在开发.net项目中,通过microsoft.ACE.oledb读取excel文件信息时,报...
正则忽略大小写 – RegexOptions.IgnoreCase 例如: 复制代码 代码如下: Str = R...
上篇文章给大家介绍了 Java正则表达式匹配,替换,查找,切割的方法 ,接下来,...
复制代码 代码如下: % URL="http://news.163.com/special/00011K6L/rss_newstop....
DELETEFROMTablesWHEREIDNOTIN(SELECTMin(ID)FROMTablesGROUPBYName) Min的话保...
工具:Eclipse,Oracle,smartupload.jar;语言:jsp,Java;数据存储:Oracle。...
本文实例讲述了Laravel框架源码解析之反射的使用。分享给大家供大家参考,具体如...
Elasticsearch 是通过 Lucene 的倒排索引技术实现比关系型数据库更快的过滤。特...
4月11日20:30~22:00通过腾讯会议进行了第二次在线学习讨论我把学习笔记整理一下...