我遇到了一个匪夷所思的问题,我开启了IDLE中断和DMA中断,其中IDLE中断是可以正常运行的,DMA中断却始终无法进入中断服务函数,哪怕在IDLE中断中用串口打印了出了dma相应的中断标志位是1,但仍然无法进入dma中断服务函数,不知道问题出在了哪里
//dma.c配置函数,其中设置dma缓冲为5,循环模式,开启DMA1_IT_TC5中断,dma抢占优先级为1,高于usart的中断优先级
void DMA_Config(DMA_Channel_TypeDef* DMA_CHx, u32 dir, u32 cpar, u32 cmar, u16 cndtr)
{
//使能DMA时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
//记录传输数据量
CNDTR = cndtr;
//配置DMA中断
NVIC_InitStructure_DMA.NVIC_IRQChannel = DMA1_Channel5_IRQn; //开启DMA中断通道
NVIC_InitStructure_DMA.NVIC_IRQChannelPreemptionPriority=1 ;//抢占优先级2,优先于串口中断
NVIC_InitStructure_DMA.NVIC_IRQChannelSubPriority = 3; //子优先级3
NVIC_InitStructure_DMA.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure_DMA); //根据指定的参数初始化VIC寄存器
//去初始化DMA
DMA_DeInit(DMA_CHx);
//为 DMA_InitTpyeDef 结构体成员赋值
DMA_InitStruct. DMA_PeripheralBaseAddr = cpar;
DMA_InitStruct. DMA_MemoryBaseAddr = cmar;
DMA_InitStruct. DMA_BufferSize = cndtr;
DMA_InitStruct. DMA_Priority = DMA_Priority_Medium;
DMA_InitStruct. DMA_DIR = dir;
DMA_InitStruct. DMA_Mode = DMA_Mode_Circular; //循环模式
DMA_InitStruct. DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStruct. DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStruct. DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStruct. DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStruct. DMA_M2M = DMA_M2M_Disable;
//DMA中断标志位初始化
DMA_ClearITPendingBit(DMA1_IT_TC5);
//DMA1_CH5_TC中断使能
DMA_ITConfig(DMA_CHx,DMA1_IT_TC5, ENABLE); //开启DMA1_CH5_TC中断,DMA传输完成标志!
//DMA初始化
DMA_Init(DMA_CHx, &DMA_InitStruct);
//使能DMA
DMA_Cmd(DMA_CHx, ENABLE);
}
//dma开启函数,过程为关闭dma,重新赋值dma缓冲通道,开启dma
void DMA_EnableCmd(DMA_Channel_TypeDef* DMA_CHx)
{
//每次开启DMA之前必须先重装在CNDTR,开启CNDTR之前必须关闭DMA
DMA_Cmd(DMA_CHx, DISABLE);
//重装在CNDTR
DMA_SetCurrDataCounter(DMA_CHx, CNDTR);
//开启本次DMA传输
DMA_Cmd(DMA_CHx, ENABLE);
}
//DMA1_IT_TC5中断服务函数,功能是每次dma传输完成后,将MyFlag全局变量置1,但是这个中断服务函数始终进不去!!
void DMA1_Channel5_IRQHandler() //将超载标志置1
{
if(DMA_GetITStatus(DMA1_IT_TC5) != RESET)
{
MyFlag = 1;
DMA_ClearITPendingBit(DMA1_IT_GL5);
DMA_ClearITPendingBit(DMA1_IT_TC5); //清除DMA1_CH5_TC中断标志位,该函数作用原理和ClearFlag是一样的,都是对DMA_IFCR寄存器进行写操作!
}
}
//usart.c配置函数,其中开启了usart1的dma功能
void uart_init(u32 bound){
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟
//USART1_TX GPIOA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
//USART1_RX GPIOA.10初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10
//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
//USART 初始化设置
USART_InitStructure.USART_BaudRate = bound;//串口波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
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(USART1, &USART_InitStructure); //初始化串口1
USART_ITConfig(USART1,USART_IT_IDLE,ENABLE); //开启串口空闲检测IDLE中断
USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE); //开启串口DMA接收
USART_Cmd(USART1, ENABLE); //使能串口1
}
//IDLE中断服务函数,先打印dma对应的中断标志位,再打印MyFlag标志,其中MyFlag是一个全局变量,初始化为0,在dma中断服务函数中置1,也就是说如果打印得到DMA1_IT_TC5是1的话,MyFlag打印也应该得到1才对!但是实际情况是DMA1_IT_TC5打印值为1时,MyFlag依然打印值是0,说明根本没进入dma中断服务函数,要哭了.
if(USART_GetITStatus(USART1,USART_IT_IDLE) != RESET)
{
DMA_FLAG = DMA_GetFlagStatus(DMA1_IT_TC5); //获取DMA1_IT_TC5中断标志位
printf("The DMA1_TC5 status is %d\r\n",DMA_FLAG); //打印DMA1_IT_TC5中断标志位
printf("The MyFlag is %d\r\n",MyFlag); //打印MyFlag全局变量
printf("The IDLE status was SET!\r\n");
IDLE_ClearBit = USART_ReceiveData(USART1); //清除IDLE标志位
if(USART_GetITStatus(USART1,USART_IT_IDLE) == RESET)
{
printf("The IDLE status is CLEAR!\r\n");
if(MyFlag == 0)
{
Cmd = ServoCmd(USART_RX_BUF);
printf("The current Cmd is %d\r\n",Cmd);
if(Cmd >= 20 && Cmd <= 90)
{
printf("占空比是%d%%\r\n",Cmd);
Duty_Count = Cmd * arr /100;
TIM_SetCompare1(TIM4,Duty_Count); //IDLE中断控制TIM4_CH1的CCR寄存器,用于修改占空比
}
else printf("非法指令!请重新输入:(输入范围:20至90)\r\n");
}
else
{
MyFlag = 0; //如果超载,重新刷新MyFlag,本次USART_RX_BUF中的数据无效,不做处理!
printf("数据超载!请重新输入o3o\r\n");
}
DMA_EnableCmd(DMA1_Channel5); //重置DMA,使之从BUF[0]开始记录数据
printf("接收缓冲器内的数据是 %s\r\n",USART_RX_BUF); //打印USART_RX_BUF的数据
memset(USART_RX_BUF,0,sizeof(USART_RX_BUF));
}
}
用串口打印结果如下:
输入“50\r\n”时,数据长度还没有达到dma缓冲长度,可以得到正确的结果:DMA1_IT_TC5 = 0,MyFlag = 0;
输入“123\r\n”时,数据长度达到了dma缓冲长度,dma完成一次传输,DMA1_IT_TC5 = 1,但是MyFlag依然是0,说明DMA1_IT_TC5中断服务函数没有运行!
现在遇到了一个新的问题,新建工程单独测试dma中断没有问题,在此基础上添加USART的IDLE中断,发现这个时候dma中断没问题,IDLE中断却又进不去了!如果把IDLE中断抢占优先级设置大于dma中断,串口打印就会出现类似于“干扰”一样的问题,所以我在想是不是串口中断和dma中断在stm32内部存在着冲突?
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
#include "stdio.h"
/* Private define ------------------------------------------------------------*/
#define USART1_DR_Base 0x40013804
#define SRC_USART1_DR (&(USART1->DR))
/* Private variables ---------------------------------------------------------*/
#define SENDBUFF_SIZE 5
vu8 SendBuff[SENDBUFF_SIZE];
void DMA_Configuration(void);
void USART1_Configuration(void);
//////////////////////////////////////////////////////////////////
//加入以下代码,支持printf函数,而不需要选择use MicroLIB
#if 1
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{
int handle;
};
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
_sys_exit(int x)
{
x = x;
}
//重定义fputc函数
int fputc(int ch, FILE *f)
{
while((USART1->SR&0X40)==0);//循环发送,直到发送完毕
USART1->DR = (u8) ch;
return ch;
}
#endif
//
//
int main(void)
{
SystemInit();
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
USART1_Configuration();
printf("USART1!\r\n");
DMA_Configuration();
while (1)
{
}
}
// Description : NUSART1设置
void USART1_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
USART_InitTypeDef USART_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO | RCC_APB2Periph_USART1, ENABLE);
//NVIC配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
//USART1_TX
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//USART1_RX
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = 115200;
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_Tx | USART_Mode_Rx;
USART_Init(USART1, &USART_InitStructure);
USART_ITConfig(USART1,USART_IT_IDLE,ENABLE);
USART_Cmd(USART1, ENABLE);
}
void DMA_Configuration(void)
{
DMA_InitTypeDef DMA_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA传输
DMA_DeInit(DMA1_Channel5);
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&(USART1->DR));
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)SendBuff;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //外设作源头//外设是作为数据传输的目的地还是来源
DMA_InitStructure.DMA_BufferSize = SENDBUFF_SIZE; //DMA缓存的大小 单位在下边设定
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址寄存器不递增
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址递增
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外设字节为单位
DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte; //内存字节为单位
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //工作在循环缓存模式
DMA_InitStructure.DMA_Priority = DMA_Priority_High; //4优先级之一的(高优先) VeryHigh/High/Medium/Low
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //非内存到内存
DMA_Init(DMA1_Channel5, &DMA_InitStructure); //根据DMA_InitStruct中指定的参数初始化DMA的通道1寄存器
DMA_ITConfig(DMA1_Channel5, DMA_IT_TC, ENABLE); //DMA5传输完成中断
USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE); //使能USART1的接收DMA请求
DMA_Cmd(DMA1_Channel5, ENABLE);
DMA_ClearFlag(DMA1_FLAG_TC5);
//NVIC配置
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void USART1_IQRHandler(void)
{
u16 IDLE_ClearBit;
printf("Start IDLE Interrupt!\r\n");
if(USART_GetITStatus(USART1,USART_IT_IDLE))
{
IDLE_ClearBit = USART_ReceiveData(USART1);
while(USART_GetITStatus(USART1,USART_IT_IDLE));
printf("%s\r\n", SendBuff);
}
}
void DMA1_Channel5_IRQHandler(void)
{
u8 end;
printf("Start DMA transmission!\r\n");
if(DMA_GetITStatus(DMA1_IT_TC5))
{
end = DMA_GetCurrDataCounter(DMA1_Channel5);
DMA_ClearITPendingBit(DMA1_IT_GL5);///* 清中断源
DMA_ClearFlag(DMA1_FLAG_TC5);
printf("%d\r\n", end);
}
}
运行结果:
如果USART的中断抢占优先级从3改为0,运行结果为:
每按一次发送就会打印几个字出来,太奇怪了!