文章目录
一、引脚连接
-RXD:数据输入引脚。数据接受。
-TXD:数据发送引脚。数据发送。
在原理图中的TX、RX为模块的输出端与接收端,标号表示连载一起,单片机原理图上的连接点也是表示模块的引脚。
可以根据引脚再查看数据手册进行再次确认。
物理层通信
TTL电平:传输电压低,常用于近距离传输连接
RS232电平:传输电压较大,转换为RS232可用于较远距离数据传输连接
串口转(安装相应的驱动)
串口转USB:ch340芯片——1:2.4V~5V 0:0V~0.4V
串口转RS232:max232芯片——
串口转RS485:max485芯片
二、串口通信协议
起始位:1位
数据位:8-9位(一般为8位)
奇偶校验位:1或1.5或2位(一般用1位,设置为0—即不设置奇偶校验)
停止位:1位
波特率设置:在数据传输和接收双方,需要预先统一波特率,以便正确的传输数据。
波特率 = 时钟频率 / (分频系数 × (1 + 分频器值))
=串口时钟 / BRR
波特率与时间的关系:假定设置UART波特率为9600 N 8 1 (代表无校验8位数据位1位停止位)
9600表示每秒9600位,每个字节是8位, 如果外加1个起始位和1个停止位,那就要10个位才能传送1个字节, 理论速度为:9600/10 = 960 字节每秒
三、串口通信过程
四、常用相关寄存器(具体查看寄存器手册)
USART框图
常用寄存器
USART_DR数据寄存器(data register)——发送、接受缓冲区
USART_CR数据寄存器(control register)
USART_BRR波特率寄存器
USART_SR状态寄存器(state registe)
SR寄存器——标志位
TC位:发送完成,当该位被置1,表示DR寄存器数据已经发送完成
RXNE位:接受寄存器非空(判断是否有数据传输进来,需要接受)
IDLE位:判断总线是非为空闲(判断数据是否是传输完成)
五、中断标志位(位于UARTx->SR状态寄存器)
六、串口配置一般步骤(重要)
1.串口时钟使能,GPIO时钟使能:RCC_APB2PeriphClockCmd();
2.串口复位:USART_DeInit(); 这一步不是必须的
3.GPIO端口模式设置:GPIO_Init(); 模式设置为GPIO_Mode_AF_PP
4.串口参数初始化:USART_Init();
5.开启中断并且初始化NVIC(如果需要开启中断才需要这个步骤)
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);//设置分组函数调用
NVIC_Init();//抢占优先级,相应优先级,设置串口中断线
USART_ITConfig();//中断触发源,(RXNE/IDLE)
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);
七、中断接收方法(最好会写)
方法1.字符\r\n
注释:程序要求,发送的字符是以回车换行结束(0x0D,0x0A
假如发送数据为:ABCDEFGHI…….\r\n
字符\r\n 对应ASCII表的十进制数据是
ABCDEFGHI…….(0x0D),(0x0A)
1.宏定义一个USART_REC_LEN用于表示数组长度
#define USART_REC_LEN 200 //定义最大接收字节数 200
2.设置一 个 数 组USART_RX_BUF[]
u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
3.设置一个接收状态全局变量 USART_RX_STA(记录数组的位置)
u16 USART_RX_STA; //接收状态标记
代码:(直接用)
void USART1_IRQHandler()
{
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;//接收错误,重新开始
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;//接收数据错误,重新开始接收
}
}
}
}
}
if((USART_RX_STA&0x8000)==0)
可用于判断是否有数据需要接收
方法2.中断标志位(RXNE.IDLE)
USART_IT_RXNE 接收缓存器标志位 如果有数据接收到该位置一
USART_IT_IDLE 空闲标志位 如果数据接收完毕该位置一
1.宏定义一个USART_REC_LEN用于表示数组长度
#define USART_REC_LEN 200 //定义最大接收字节数 200
2.设置一 个 数 组USART_RX_BUF[]
u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
3.设置一个接收状态全局变量 USART_RX_STA(记录数组的位置)
u16 USART_RX_STA; //接收状态标记
USART_RX_STA = 1;//数据接收完毕
USART_RX_STA = 0;//数据尚未接收完毕
代码:(直接用)
void USART1_IRQHandler()
{
u8 res;
if(USART_GetITStatus(USART1,USART_IT_RXNE)) //SR寄存器的 RXNE位,读寄存器非空,判断是否有数据
{
res = USART_ReceiveData(USART1); //读取数据相当于清除中断
USART_RX_BUF[USART_RX_STA&0X3FFF]=res; //将接收到数据存入数组
USART_RX_STA++; //数组长度变量++
if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接受的数据长度超过数组最大值,重新开始
}
if(USART_GetITStatus(USART1,USART_IT_IDLE)) //SR寄存器的 IDLE位,判断总线是否空闲,也就是判断是否接受完数据
{
res = USART1->SR;
res = USART1->DR; //清空闲中断标志位,看中文数据操作手册:先读SR,再读DR,后清除中断
USART_RX_STA|=0x8000;//第十五位置一
}
}
注意事项:注意IDLE的使能添加:USART_ITConfig
if((USART_RX_STA&0x8000))
可用于判断是否有数据需要接收
注意调用清0
USART_RX_STA=0;
memset(USART_RX_BUF,0,200);
方法3.通过接收超时判断
假设传输速度位9600bit/s
现传输一个9600bit的数据需要1秒钟
通过判断USART_IT_RXNE 总线上在一个字节的时间内没有再接收到数据时发生。 (模仿空闲中断)
1.宏定义一个USART_REC_LEN用于表示数组长度
#define USART_REC_LEN 200 //定义最大接收字节数 200
2.设置一 个 数 组USART_RX_BUF[]
u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
3.设置一个接收状态全局变量 USART_RX_STA(记录数组的位置)
u16 USART_RX_STA; //接收状态标记
方法4.缓存队列
可以实现边接收数据,边发送数据
八、串口实现printf打印(写在串口模块)(直接用,最好会写)
//加入以下代码,支持printf函数,而不需要选择use MicroLIB,避免出现半主机模式 ,写在模块程序上面
#if 1
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{ int handle;
};
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x)
{ x = x; }
//重定义fputc函数
int fputc(int ch, FILE *f) //文件流方式
{
while((USART1->SR&0X40)==0);//循环发送,直到发送完毕 //寄存器TC位,在没有置1时一直循环,置1后跳出循环
USART1->DR = (u8) ch;
return ch;
}
#endif
九、发送函数封装(最好会写)
//发送一个字节数据
void usart_sendbyte(USART_TypeDef* pUSARTx, uint8_t data)
{
//发送一个字节数据
USART_SendData(pUSARTx,data);
//等待发送数据寄存器为空
while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TXE) == RESET);
}
//发送一个数组数据
void usart_sendarr(USART_TypeDef* pUSARTx, uint8_t *arr, uint8_t len)
{
uint8_t i;
for(i = 0; i < len; i++)
{
//发送一个字节数据
usart_sendbyte(pUSARTx,*arr++);
}
//等待发送完成
while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC) == RESET);
}
//发送字符串数据
void usart_sendstr(USART_TypeDef* pUSARTx, char *str)
{
do
{
//发送一个字节数据
usart_sendbyte(pUSARTx,*str++);
}while((*str) != '\0');
//等待发送完成
while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC) == RESET);
}