STM32
回帖(1)
2021-12-7 13:49:04
一、串口的初始化和中断设置
1、初始化GPIO:
根据手册的8.1.11节,我们可以找到下表:
在全双工的模式下,发送引脚需要设置为推挽复用输出,接收引脚则设置为浮空输入或带上拉的输入。因为一般不用同步和流量控制的方式,所以CK、RST、CTS引脚不作配置。当然啦,在使用STM32外设的时候不要忘记打开外设时钟(GPIO和USART的RCC)。
1. USART_FLAG_RXNE—接受数据寄存器非空标志位(Receive data register not empty flag)
当RDR移位寄存器中的数据被转移到USART_DR寄存器中,该位被硬件置位 (表示接受到一个字节) 。如果USART_CR1寄存器中的RXNEIE为1,则产生中断。对USART_DR的读操作可以将该位清零。RXNE位也可以通过写入0来清除,只有在多缓存通讯中才推荐这种清除程序。
0:数据没有收到;
1:收到数据,可以读出。
2. USART_FLAG_TXE —发送数据寄存器空标志位(Transmit data register empty flag)
当TDR寄存器中的数据被硬件转移到移位寄存器的时候,该位被硬件置位 (表示发送数据寄存器为空,可以继续写入数据。注:此时并不确定数据是否发送完成) 。如果USART_CR1寄存器中的TXEIE为1,则产生中断。对USART_DR的写操作,将该位清零。
0:数据还没有被转移到移位寄存器;
1:数据已经被转移到移位寄存器。
3. USART_FLAG_TC — 发送完成标志位(Transmission Complete flag)
包含有数据的一帧发送完成后,并且TXE=1时,由硬件将该位置’1’ (表示一帧数据发送完成,可以继续发送下一帧)。如果USART_CR1中的TCIE为’1’,则产生中断。由软件序列清除该位(先读USART_SR,然后写入USART_DR)。TC位也可以通过写入’0’来清除,只有在多缓存通讯中才推荐这种清除程序。
0:发送还未完成;
1:发送完成。
有一种形象的说法是:TXE是指“弹仓”空;TC是“枪膛”空。
也就是说,你写数据到串口时,是装入弹仓,硬件会将数据移到枪膛,这时,TXE为1,TC为0,STM32硬件的TX脚正在发送数据,但你还可以装入数据到弹仓,装入后,TXE为0,TC为0.
TX发送完一个数据后,立即将数据从弹仓移入枪膛,这时,TXE为1,TC为0.
最后TX发送完数据,你又没有装入新数据,这时。TXE为1,TC为1.
GPIO_InitTypeDef GPIO_InitStructure;
//开启串口和GPIO的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
//配置发送引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
//发送引脚设置为推挽复用
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//配置接收引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
//接收引脚设置为浮空输入
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
2、配置串口参数
有专门用于初始化串口的库函数(USART_Init)和对应的结构体(USART_InitTypeDef),好像每个外设都有这样的配套,具体内容可参看《STM32F10xxx固件库_3.xx.pdf》。
USART_InitTypeDef USART_InitStructure;
//波特率
USART_InitStructure.USART_BaudRate = 9600;
//数据长度
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;
//初始化串口1
USART_Init(USART1, &USART_InitStructure);
3、中断配置
在使用STM32的中断前,要对NVIC中断控制器进行配置,设置中断的优先级。
//配置中断优先级
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
4、使能串口及串口中断
注意1:初始化时不要随意打开TXE中断!只要TX-DR寄存器为空,TX和TXE标志都会马上被置位而立即会产生中断(参考《STM32中文参考手册》的25.3.2节),即使中断标志被清除,也会被重新置位。因此,我采用的是TC中断而不是采用TXE中断。
注意2:不要采用在一个中断配置函数中同时打开两个中断!例如:USART_ITConfig(USART1, USART_IT_TC | USART_IT_RXNE, ENABLE); 咋眼一看,明明只打开TC中断和RX中断,然而却会同时把TXE中断也打开。
//串口1使能
USART_Cmd(USART1, ENABLE);
//清除接收中断标记
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
//清除发送完成中断标记
USART_ClearITPendingBit(USART1, USART_IT_TC);
//打开串口1发送完中断
USART_ITConfig(USART1, USART_IT_TC, ENABLE);
//打开串口1接收中断 两个中断不能在一个函数中同时打开!!!太坑了T_T
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
————————————————
这样,串口1配置好了。但代码一运行就会发现不妥!为什么每次初始化完成就马上进入中断了呢???遇到这种现象千万不要大惊小怪,我很淡(dan)定(teng)地做了个实验,发现处理器复位后,串口的SR寄存器中的TC标志会被置位。而根《STM32中文参考手册》25.3.2节,在串口使能后会自动发送一个空闲帧,发送完毕后TC也会置位,所以初始化将导致串口初始化完毕后马上进入TC中断。为了避免这种情况,可以在串口使能后等待空闲帧发送完毕,再打开TC中断。
具体看下面完整的初始化代码:
————————————————
//配置串口1
void USART1_Config()
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
//配置发送引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
//发送引脚设置为推挽复用
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//配置接收引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
//接收引脚设置为浮空输入
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//波特率
USART_InitStructure.USART_BaudRate = 9600;
//数据长度
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;
//初始化串口1
USART_Init(USART1, &USART_InitStructure);
//USART1->SR寄存器复位后TC位为1,在此清零
USART_ClearFlag(USART1, USART_FLAG_TC);
//串口1使能
USART_Cmd(USART1, ENABLE);
//使能后串口发送一个空闲帧,等待空闲帧发送完毕后将TC标记位清零
while(USART_GetFlagStatus(USART1, USART_FLAG_TC) != SET);
//否则开启TC中断后会马上中断
USART_ClearFlag(USART1, USART_FLAG_TC);
//配置中断优先级
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/***************************************************************
注意:初始化时不要随意打开TXE中断,
因为只要TX-DR寄存器为空,TX和TXE都会马上被置位而立即会产生中断
***************************************************************/
//清除接收中断标记
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
//清除发送完成中断标记
USART_ClearITPendingBit(USART1, USART_IT_TC);
//打开串口1发送完中断
USART_ITConfig(USART1, USART_IT_TC, ENABLE);
//打开串口1接收中断 两个中断不能在一个函数中同时打开!!!太坑了T_T
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
}
————————————————
二、数据的发送和接收(注:以下代码有bug,博主至今还未找到原因T-T,仅供思路以参考!)
为了提高代码的效率,我使用基于环形缓冲的串口通信方式。
发送数据原理:把要发送的数据全部加入到缓冲区中,让处理器开始发送。一个数据发送结束后,即会产生TC中断,此时在中断服务程序中发送下一个数据。像吃饭看电视,在夹菜(发数据)的时候才要把注意力放到菜盘子上,嚼饭的时候(数据发送中)可以看电视,在开始发送数据到数据发送完毕触发中断的这段时间里,处理器可以去做别的事情。
接收数据原理:当一个数据接收完毕后,将数据立存入缓冲区而不处理,并在未处理数据的计数器上加1。等到处理器空闲,再从缓冲区读取这些数据做并处理(不在中断函数中)。
如此一来,串口的收发速率并不受影响,还能保证处理器在数据收发的过程中并行执行其他任务。
————————————————
#include "usart1.h"
#include "string.h"
//发送缓冲区
u8 Usart1_SendBuffer[USART_SendBufferSize];
//接收缓冲区
u8 Usart1_RecvBuffer[USART_RecvBufferSize];
//发送缓冲区指针
int Usart1_SendPointer = 0;
//接收缓冲区指针
int Usart1_RecvPointer = 0;
//发送字符队列的长度
int Usart1_SendDataSize = 0;
//接收未处理字符数
int Usart1_RecvDataSize = 0;
//串口1发送状态
int Usart1_SendStatus = USART_Status_Idle;
//生成字符串的缓冲区
char StringBuffer[100];
//发送字符串
void USART1_SendString(char *str)
{
// while(*str)
// {
// USART1_SendByte(*str++);
// }
USART1_SendArray((u8*)str, strlen(str));
}
//发送字节队列
void USART1_SendArray(u8 *DataArray, int count)
{
if(count <= 0)
{
return;
}
while(count)
{
USART1_SendByte(*DataArray++);
count --;
}
}
//发送一个字节
void USART1_SendByte(u8 data)
{
int pos;
//如果缓冲区满了,要等待
while(Usart1_SendDataSize >= USART_SendBufferSize);
//计算数据在缓冲区的位置
pos = Usart1_SendPointer + Usart1_SendDataSize;
//数据位置超过缓冲区尾地址
if(pos >= USART_SendBufferSize)
{
//重新计算位置
pos = pos - USART_SendBufferSize;
}
Usart1_SendBuffer[pos] = data;
Usart1_SendDataSize ++;
//如果串口空闲,立即发送
if(Usart1_SendStatus == USART_Status_Idle)
{
Usart1_SendStatus = USART_Status_Busy;
USART_SendData(USART1, Usart1_SendBuffer[Usart1_SendPointer++]);
//指针移动到缓冲区尾地址后,循环到缓冲区首地址
if(Usart1_SendPointer == USART_SendBufferSize)
{
Usart1_SendPointer = 0;
}
Usart1_SendDataSize --;
}
}
//串口1中断服务程序
void USART1_IRQHandler()
{
//判断发送完成中断
if(USART_GetITStatus(USART1, USART_IT_TC) == SET)
{
//清空发送完成TC标记位
USART_ClearFlag(USART1, USART_FLAG_TC);
//清空串口发送完成中断TCIE标记
USART_ClearITPendingBit(USART1, USART_IT_TC);
if(Usart1_SendDataSize > 0)
{
//发送下一个数据
USART_SendData(USART1, Usart1_SendBuffer[Usart1_SendPointer++]);
//指针移动到缓冲区尾地址后,循环到缓冲区首地址
if(Usart1_SendPointer == USART_SendBufferSize)
{
Usart1_SendPointer = 0;
}
//待发送数据减1
Usart1_SendDataSize --;
}
else
{
//发送完毕,串口1发送状态:空闲
Usart1_SendStatus = USART_Status_Idle;
}
}
//接收中断
if(USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
{
//清空串口接收标记
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
//获取缓冲区数据
Usart1_RecvBuffer[Usart1_RecvPointer++] = USART_ReceiveData(USART1);
//如果没有溢出,待处理数据+1。否则丢弃该数据
if(Usart1_RecvDataSize < USART_RecvBufferSize)
{
Usart1_RecvDataSize ++;
}
//指针移动到缓冲区尾地址后,循环到缓冲区首地址
if(Usart1_RecvPointer == USART_RecvBufferSize)
{
Usart1_RecvPointer = 0;
}
}
}
一、串口的初始化和中断设置
1、初始化GPIO:
根据手册的8.1.11节,我们可以找到下表:
在全双工的模式下,发送引脚需要设置为推挽复用输出,接收引脚则设置为浮空输入或带上拉的输入。因为一般不用同步和流量控制的方式,所以CK、RST、CTS引脚不作配置。当然啦,在使用STM32外设的时候不要忘记打开外设时钟(GPIO和USART的RCC)。
1. USART_FLAG_RXNE—接受数据寄存器非空标志位(Receive data register not empty flag)
当RDR移位寄存器中的数据被转移到USART_DR寄存器中,该位被硬件置位 (表示接受到一个字节) 。如果USART_CR1寄存器中的RXNEIE为1,则产生中断。对USART_DR的读操作可以将该位清零。RXNE位也可以通过写入0来清除,只有在多缓存通讯中才推荐这种清除程序。
0:数据没有收到;
1:收到数据,可以读出。
2. USART_FLAG_TXE —发送数据寄存器空标志位(Transmit data register empty flag)
当TDR寄存器中的数据被硬件转移到移位寄存器的时候,该位被硬件置位 (表示发送数据寄存器为空,可以继续写入数据。注:此时并不确定数据是否发送完成) 。如果USART_CR1寄存器中的TXEIE为1,则产生中断。对USART_DR的写操作,将该位清零。
0:数据还没有被转移到移位寄存器;
1:数据已经被转移到移位寄存器。
3. USART_FLAG_TC — 发送完成标志位(Transmission Complete flag)
包含有数据的一帧发送完成后,并且TXE=1时,由硬件将该位置’1’ (表示一帧数据发送完成,可以继续发送下一帧)。如果USART_CR1中的TCIE为’1’,则产生中断。由软件序列清除该位(先读USART_SR,然后写入USART_DR)。TC位也可以通过写入’0’来清除,只有在多缓存通讯中才推荐这种清除程序。
0:发送还未完成;
1:发送完成。
有一种形象的说法是:TXE是指“弹仓”空;TC是“枪膛”空。
也就是说,你写数据到串口时,是装入弹仓,硬件会将数据移到枪膛,这时,TXE为1,TC为0,STM32硬件的TX脚正在发送数据,但你还可以装入数据到弹仓,装入后,TXE为0,TC为0.
TX发送完一个数据后,立即将数据从弹仓移入枪膛,这时,TXE为1,TC为0.
最后TX发送完数据,你又没有装入新数据,这时。TXE为1,TC为1.
GPIO_InitTypeDef GPIO_InitStructure;
//开启串口和GPIO的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
//配置发送引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
//发送引脚设置为推挽复用
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//配置接收引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
//接收引脚设置为浮空输入
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
2、配置串口参数
有专门用于初始化串口的库函数(USART_Init)和对应的结构体(USART_InitTypeDef),好像每个外设都有这样的配套,具体内容可参看《STM32F10xxx固件库_3.xx.pdf》。
USART_InitTypeDef USART_InitStructure;
//波特率
USART_InitStructure.USART_BaudRate = 9600;
//数据长度
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;
//初始化串口1
USART_Init(USART1, &USART_InitStructure);
3、中断配置
在使用STM32的中断前,要对NVIC中断控制器进行配置,设置中断的优先级。
//配置中断优先级
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
4、使能串口及串口中断
注意1:初始化时不要随意打开TXE中断!只要TX-DR寄存器为空,TX和TXE标志都会马上被置位而立即会产生中断(参考《STM32中文参考手册》的25.3.2节),即使中断标志被清除,也会被重新置位。因此,我采用的是TC中断而不是采用TXE中断。
注意2:不要采用在一个中断配置函数中同时打开两个中断!例如:USART_ITConfig(USART1, USART_IT_TC | USART_IT_RXNE, ENABLE); 咋眼一看,明明只打开TC中断和RX中断,然而却会同时把TXE中断也打开。
//串口1使能
USART_Cmd(USART1, ENABLE);
//清除接收中断标记
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
//清除发送完成中断标记
USART_ClearITPendingBit(USART1, USART_IT_TC);
//打开串口1发送完中断
USART_ITConfig(USART1, USART_IT_TC, ENABLE);
//打开串口1接收中断 两个中断不能在一个函数中同时打开!!!太坑了T_T
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
————————————————
这样,串口1配置好了。但代码一运行就会发现不妥!为什么每次初始化完成就马上进入中断了呢???遇到这种现象千万不要大惊小怪,我很淡(dan)定(teng)地做了个实验,发现处理器复位后,串口的SR寄存器中的TC标志会被置位。而根《STM32中文参考手册》25.3.2节,在串口使能后会自动发送一个空闲帧,发送完毕后TC也会置位,所以初始化将导致串口初始化完毕后马上进入TC中断。为了避免这种情况,可以在串口使能后等待空闲帧发送完毕,再打开TC中断。
具体看下面完整的初始化代码:
————————————————
//配置串口1
void USART1_Config()
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
//配置发送引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
//发送引脚设置为推挽复用
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//配置接收引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
//接收引脚设置为浮空输入
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//波特率
USART_InitStructure.USART_BaudRate = 9600;
//数据长度
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;
//初始化串口1
USART_Init(USART1, &USART_InitStructure);
//USART1->SR寄存器复位后TC位为1,在此清零
USART_ClearFlag(USART1, USART_FLAG_TC);
//串口1使能
USART_Cmd(USART1, ENABLE);
//使能后串口发送一个空闲帧,等待空闲帧发送完毕后将TC标记位清零
while(USART_GetFlagStatus(USART1, USART_FLAG_TC) != SET);
//否则开启TC中断后会马上中断
USART_ClearFlag(USART1, USART_FLAG_TC);
//配置中断优先级
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/***************************************************************
注意:初始化时不要随意打开TXE中断,
因为只要TX-DR寄存器为空,TX和TXE都会马上被置位而立即会产生中断
***************************************************************/
//清除接收中断标记
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
//清除发送完成中断标记
USART_ClearITPendingBit(USART1, USART_IT_TC);
//打开串口1发送完中断
USART_ITConfig(USART1, USART_IT_TC, ENABLE);
//打开串口1接收中断 两个中断不能在一个函数中同时打开!!!太坑了T_T
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
}
————————————————
二、数据的发送和接收(注:以下代码有bug,博主至今还未找到原因T-T,仅供思路以参考!)
为了提高代码的效率,我使用基于环形缓冲的串口通信方式。
发送数据原理:把要发送的数据全部加入到缓冲区中,让处理器开始发送。一个数据发送结束后,即会产生TC中断,此时在中断服务程序中发送下一个数据。像吃饭看电视,在夹菜(发数据)的时候才要把注意力放到菜盘子上,嚼饭的时候(数据发送中)可以看电视,在开始发送数据到数据发送完毕触发中断的这段时间里,处理器可以去做别的事情。
接收数据原理:当一个数据接收完毕后,将数据立存入缓冲区而不处理,并在未处理数据的计数器上加1。等到处理器空闲,再从缓冲区读取这些数据做并处理(不在中断函数中)。
如此一来,串口的收发速率并不受影响,还能保证处理器在数据收发的过程中并行执行其他任务。
————————————————
#include "usart1.h"
#include "string.h"
//发送缓冲区
u8 Usart1_SendBuffer[USART_SendBufferSize];
//接收缓冲区
u8 Usart1_RecvBuffer[USART_RecvBufferSize];
//发送缓冲区指针
int Usart1_SendPointer = 0;
//接收缓冲区指针
int Usart1_RecvPointer = 0;
//发送字符队列的长度
int Usart1_SendDataSize = 0;
//接收未处理字符数
int Usart1_RecvDataSize = 0;
//串口1发送状态
int Usart1_SendStatus = USART_Status_Idle;
//生成字符串的缓冲区
char StringBuffer[100];
//发送字符串
void USART1_SendString(char *str)
{
// while(*str)
// {
// USART1_SendByte(*str++);
// }
USART1_SendArray((u8*)str, strlen(str));
}
//发送字节队列
void USART1_SendArray(u8 *DataArray, int count)
{
if(count <= 0)
{
return;
}
while(count)
{
USART1_SendByte(*DataArray++);
count --;
}
}
//发送一个字节
void USART1_SendByte(u8 data)
{
int pos;
//如果缓冲区满了,要等待
while(Usart1_SendDataSize >= USART_SendBufferSize);
//计算数据在缓冲区的位置
pos = Usart1_SendPointer + Usart1_SendDataSize;
//数据位置超过缓冲区尾地址
if(pos >= USART_SendBufferSize)
{
//重新计算位置
pos = pos - USART_SendBufferSize;
}
Usart1_SendBuffer[pos] = data;
Usart1_SendDataSize ++;
//如果串口空闲,立即发送
if(Usart1_SendStatus == USART_Status_Idle)
{
Usart1_SendStatus = USART_Status_Busy;
USART_SendData(USART1, Usart1_SendBuffer[Usart1_SendPointer++]);
//指针移动到缓冲区尾地址后,循环到缓冲区首地址
if(Usart1_SendPointer == USART_SendBufferSize)
{
Usart1_SendPointer = 0;
}
Usart1_SendDataSize --;
}
}
//串口1中断服务程序
void USART1_IRQHandler()
{
//判断发送完成中断
if(USART_GetITStatus(USART1, USART_IT_TC) == SET)
{
//清空发送完成TC标记位
USART_ClearFlag(USART1, USART_FLAG_TC);
//清空串口发送完成中断TCIE标记
USART_ClearITPendingBit(USART1, USART_IT_TC);
if(Usart1_SendDataSize > 0)
{
//发送下一个数据
USART_SendData(USART1, Usart1_SendBuffer[Usart1_SendPointer++]);
//指针移动到缓冲区尾地址后,循环到缓冲区首地址
if(Usart1_SendPointer == USART_SendBufferSize)
{
Usart1_SendPointer = 0;
}
//待发送数据减1
Usart1_SendDataSize --;
}
else
{
//发送完毕,串口1发送状态:空闲
Usart1_SendStatus = USART_Status_Idle;
}
}
//接收中断
if(USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
{
//清空串口接收标记
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
//获取缓冲区数据
Usart1_RecvBuffer[Usart1_RecvPointer++] = USART_ReceiveData(USART1);
//如果没有溢出,待处理数据+1。否则丢弃该数据
if(Usart1_RecvDataSize < USART_RecvBufferSize)
{
Usart1_RecvDataSize ++;
}
//指针移动到缓冲区尾地址后,循环到缓冲区首地址
if(Usart1_RecvPointer == USART_RecvBufferSize)
{
Usart1_RecvPointer = 0;
}
}
}
举报
更多回帖