问题描述
在测试STM32串口发送完成中断的应用中,遇到了一个很奇怪的问题,在初始化完成之后直接就进入了串口中断函数的发送完成服务中断函数部分。本测试代码是在原来的基础上更改的,原来只使能了接受中断,此次仅仅是在初始化中加入了使能发送完成中断,所以问题的重点就处在了这个使能发送完成中断了。
查阅资料发现,在使能了发送完成中断之后,硬件就会紧接着发送一个空字符,那么发送完成之后不就进入了中断服务函数了。一般的解决方法是在初始化中不使能发送完成中断。仅是在发送数据的函数中使能发送完成中断,然后在中断函数中Disable此中断。
在初始化中开启接收中断没大意义,因为必须在其接收中断服务函数中将其Disable掉,再在需要发送的函数中将其Enable
在初始化时,配置串口发送、接收引脚,配置串口参数,配置串口中断优先级。
切记:在初始化时不能使能串口发送中断,如果初始化时使能TXEIE中断允许,则会导致程序运行时一直在进串口中断。
初始化
void My_USART1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate=115200;
USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;
USART_InitStructure.USART_Parity=USART_Parity_No;
USART_InitStructure.USART_StopBits=USART_StopBits_1;
USART_InitStructure.USART_WordLength=USART_WordLength_8b;
USART_Init(USART1,&USART_InitStructure);
USART_Cmd(USART1 ,ENABLE);
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
NVIC_InitStructure.NVIC_IRQChannel=USART1_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_ITConfig(USART1,USART_IT_TC,ENABLE);
USART_SendData(USART1,res);
}
if(USART_GetITStatus(USART1, USART_IT_TC) != RESET)
{
USART_ITConfig(USART1, USART_IT_TC, DISABLE);
USART_ClearFlag(USART1,USART_FLAG_TC);
}
}
建议的是在初始化时不好启用TXE中断,只在要发送数据(尤其是字符串、数组这样的系列数据)时才启用TXE。在发送完成后立即将其关闭,以免引起不必要的麻烦。
对于发送,需要注意TXE和TC的差别——这里简单描述一下(具体参考https://editor.csdn.net/md/?articleId=129428646),假设串口数据寄存器是DR、串口移位寄存器是SR以及TXD引脚TXDpin,其关系是DR->SR->TXDpin。当DR中的数据转移到SR中时TXE置1,如果有数据写入DR时就能将TXE置0;如果SR中的数据全部通过TXDpin移出并且没有数据进入DR,则TC置1。并且需要注意TXE只能通过写DR来置0,不能直接将其清零,而TC可以直接将其写1清零。
对于发送单个字符可以考虑不用中断,直接以查询方式完成。
对于发送字符串/数组类的数据,唯一要考虑的是只在最后一个字符发送后关闭发送中断,这里可以分为两种情况:对于发送可显示的字符串,其用0x00作为结尾的,因此在ISR中就用0x00作为关闭发送中断(TXE或者TC)的条件;第二种情况就是发送二进制数据,那就是0x00~0xFF中间的任意数据,就不能用0x00来判断结束了,这时必须知道数据的具体长度。
参考文章:https://blog.csdn.net/weixin_43202477/article/details/84848295
实例-正点原子串口实验,发送由轮询改为中断
轮询
void USART1_IRQHandler(void)
{
u8 Res;
#if SYSTEM_SUPPORT_OS
OSIntEnter();
#endif
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
Res =USART_ReceiveData(USART1);
if((USART_RX_STA&0x8000)==0)
{
if(USART_RX_STA&0x4000)
{
if(Res!=0x0a)USART_RX_STA=0;
else USART_RX_STA|=0x8000;
}
else
{
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 SYSTEM_SUPPORT_OS
OSIntExit();
#endif
}
int main(void)
{
u8 t;
u8 len;
u16 times=0;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
delay_init(168);
uart_init(115200);
LED_Init();
while(1)
{
u8 i;
if(USART_RX_STA&0x8000)
{
len=USART_RX_STA&0x3fff;
for(t=0;t<len;t++)
{
USART_SendData(USART1, USART_RX_BUF[t]);
j=USART_GetFlagStatus(USART1,USART_FLAG_TC);
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);
}
printf("\r\n\r\n");
USART_RX_STA=0;
}
else
{
times++;
if(times%5000==0)
{
printf("\r\nALIENTEK 探索者STM32F407开发板 串口实验\r\n");
printf("正点原子@ALIENTEK\r\n\r\n\r\n");
}
if(times%200==0)printf("请输入数据,以回车键结束\r\n");
if(times%30==0)LED0=!LED0;
delay_ms(10);
}
}
}
中断
void USART1_IRQHandler(void)
{
u8 Res;
u8 len;
static u8 t;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
Res =USART_ReceiveData(USART1);
if((USART_RX_STA&0x8000)==0)
{
if(USART_RX_STA&0x4000)
{
if(Res!=0x0a)USART_RX_STA=0;
else {USART_RX_STA|=0x8000;
USART_ITConfig(USART1,USART_IT_TC,ENABLE);
}
}
else
{
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_GetITStatus(USART1, USART_IT_TC) != RESET)
{
USART_SendData(USART1, USART_RX_BUF[t++]);
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);
if(t == (USART_RX_STA&0X3FFF))
{
t=0;
USART_RX_STA=0;
USART_ITConfig(USART1, USART_IT_TC, DISABLE);
printf("\r\n");
}
}
}
}
int main(void)
{
u8 t;
u8 len;
u16 times=0;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
delay_init(168);
uart_init(115200);
LED_Init();
while(1)
{
times++;
if(times%5000==0)
{
printf("\r\nALIENTEK 探索者STM32F407开发板 串口实验\r\n");
printf("正点原子@ALIENTEK\r\n\r\n\r\n");
}
if(times%200==0)printf("请输入数据,以回车键结束\r\n");
if(times%30==0)LED0=!LED0;
delay_ms(10);
}
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)