STM32F103 USART1 IDLE 旗标进中断ISR后, 会自动清除
目前在接收不定长度的 Modbus Frame上, 还是以 RXNE为基础
想改用STM32 DMA + USART IDLE试试, 看能不能降低中断次数/CPU使用率.
数据发送频率低, 长度<16
USART1 RX 以 DMA Normal mode 驱动 ( Buffer & CNDTR = 80)
接收 不定个字符后, 等USART1触发 IDLE 中断 (其他 RXNEIE, TXEIE, TCIE关闭),
网上 + CSDN 搜了 STM32 DMA + USART IDLE的文章, 概念上都建议在中断 ISR中判断 IDLEF
开始实作
(a) DMA1 -> RX Buffer 数据接收正确
(b) USART1 触发 IDLE中断, 程序进入ISR正常
void USART1_IRQHandler(void)
{
int iRXLen =0;
if( RESET != USART_GetITStatus( USART1, USART_IT_IDLE ))
{
iRXLen = USART_BUFFER_SIZE - (uint16_t)(DMA1_Channel5->CNDTR);
if( iRXLen > 3)
{
// DMA_Cmd( DMA1_Channel4, DISABLE );
DMA1_Channel4->CCR &= ~DMA_CCR4_EN;
flagMessgaeIn=1; ;
}
}
USART1->SR=0;
}
问题发生了:
当我判断 USART_FLAG_IDLE 时, IDLE旗标, 它自己清除了!
实验发现, 似乎进了中断USART1_IRQHandler后就会清除.
断点 设在进入后第一个点, SR=0x000000D0
执行第一个指令时, 就变成 SR=0x000000C0
嗯! USART_FLAG_IDLE = 0x00000010 ( SR bit4 )
<答案>
ST RM0008文件上, 说的是IDLEF要软件清除.它漏说了: IDLEF 进ISR会被自动清除!
意思是, 开了 IDLEIE, 你是摸不到IDLEF的!
使用上: 不开中断的状况下, 可以轮询 IDLEF, 再软件清除!
我就想知道那些说 STM32F103可以在 ISR里面判断 IDLEF的,
你们真的试过吗, 还是搬来搬去忽悠呢? 这属实是开了眼界.
想不到数字世界 0 , 1 之外, 竟然出现了薛定谔的猫!
那我家猫也出出镜好了
void DMA1_Init(void)
{
DMA_InitTypeDef DMA_InitStruct;
// Enable DMA1 Clock
RCC_AHBPeriphClockCmd( RCC_AHBPeriph_DMA1, ENABLE);
// TX Config
DMA_DeInit( DMA1_Channel4 );
DMA_InitStruct.DMA_BufferSize = 0;
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;
DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)USART_TX_Buffer;
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;
DMA_InitStruct.DMA_PeripheralBaseAddr = USART1_BASE +0x0004;
DMA_InitStruct.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte;
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStruct.DMA_Priority = DMA_Priority_High;
DMA_Init( DMA1_Channel4, &DMA_InitStruct);
DMA_Cmd( DMA1_Channel4, DISABLE );
// RX Config
DMA_DeInit( DMA1_Channel5);
DMA_InitStruct.DMA_BufferSize = USART_BUFFER_SIZE;
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;
DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)USART_RX_Buffer;
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;
DMA_InitStruct.DMA_PeripheralBaseAddr= USART1_BASE +0x0004;
DMA_InitStruct.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte;
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStruct.DMA_Priority = DMA_Priority_High;
DMA_Init( DMA1_Channel5, &DMA_InitStruct);
DMA_Cmd( DMA1_Channel5, ENABLE);
}
void USART1_Init(uint32_t baud)
{
USART_InitTypeDef USART_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStrue;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
// Set GPIO PA9 as USART1 TX
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;//USART1_TX PA.9
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// Set GPIO PA10 as USART1 RX
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;//USART1_RX PA.10
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// Init USART1
USART_DeInit(USART1);
USART_InitStructure.USART_BaudRate = baud;
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(USART1, &USART_InitStructure);
// Enable USART1 Interrupt
USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);
USART_ITConfig(USART1, USART_IT_TC, DISABLE);
USART_ITConfig(USART1, USART_IT_TXE, DISABLE);
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
// Set NVIC Interrupt
NVIC_PriorityGroupConfig( NVIC_PriorityGroup_0 );
NVIC_InitStrue.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStrue.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStrue.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStrue.NVIC_IRQChannelSubPriority = 13;
NVIC_Init(&NVIC_InitStrue);
DMA1_Init();
// USART1 TX Channle4
USART_DMACmd( USART1, USART_DMAReq_Tx, ENABLE);
// USART1 RX Channel5
USART_DMACmd( USART1, USART_DMAReq_Rx, ENABLE);
USART_Cmd(USART1,ENABLE);
DelayXms(2);
(void)USART1->SR;
(void)USART1->DR;
}
STM32F10x USART Status register
除了 IDLEIE外, 关闭其余所有 USART1 Interrupt Sources
void USART1_IRQHandler(void)
{
int iRXLen =0;
if( RESET == ( USART1->SR & 0x000F )) // No Error
{
iRXLen = USART_BUFFER_SIZE - (uint16_t)(DMA1_Channel5->CNDTR);
if( iRXLen > 3)
{ // DMA_Cmd( DMA1_Channel5, DISABLE );
DMA1_Channel5->CCR &= ~DMA_CCR5_EN;
flagMessgaeIn=1; ;
}
}
else
{ // Restart RX DMA when Error: PE , NE, FE, ORE
DMA1_Channel5->CCR &= ~DMA_CCR5_EN;
DMA1_Channel5->CNDTR = USART_BUFFER_SIZE;
DMA1_Channel5->CCR |= DMA_CCR5_EN;
}
// Clear All Pending Flags -> Expected to clear all error flags
(void)USART1->SR;
(void)USART1->DR;
}