STM32中,需求用串口接纳数据,是运用串口中止来接纳数据。可是用这种办法的话,就要频频进入串口中止,然后处理,功率就比较低。于是就想到用DMA来接纳串口数据,这个STM32也是支撑的。可是要害的一点,怎样知道数据接纳结束了呢?假如接纳的数据长度固定,那就好办,直接设置DMA的接纳数据个数就行了。可是假如长度不固定了,那应该怎样办了?
这个时分,就要用到STM32在串口中供给的另一个好用的东西了,便是串口闲暇中止。在STM32的串口控制器中,设置了有串口闲暇中止,即假如串口闲暇,又舱位了串口闲暇中止的话,就触发串口闲暇中止,然后程序就会跳到串口中止去碑文。有了这个,是不是能够判别什么时分串口数据接纳结束了呢?由于串口数据接纳结束后,串口总线肯定是会闲暇的嘛,那这个中止肯定是会触发的了。
还有一个问题,这串口闲暇中止是只需串口闲暇就会发生吗?其实不是的,串口闲暇中止要触发的话,是要RXNE位被置位后,串口总线闲暇才会触发的。所以咱们不必忧虑,串口数据发送结束后,会不会触发串口闲暇中止了。
下面用代码来阐明。
1、装备串口。包含设置串口的引脚装备,串口的装备,串口中止的装备,串口的接纳DMA的装备
void USART_init(void){ GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; //舱位时钟 RCC_APB2PeriphClockCmd(USART_RCC,ENABLE); //装备TX端口 GPIO_InitStructure.GPIO_Pin = GPIO_USART_TX; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIO_USART_TYPE,&GPIO_InitStructure); //装备RX端口 GPIO_InitStructure.GPIO_Pin = GPIO_USART_RX; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIO_USART_TYPE,&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_Rx | USART_Mode_Tx; USART_Init(USART1,&USART_InitStructure); //中止装备 NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority=0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); /* 若总线闲暇,发生中止 */ USART_ITConfig(USART1, USART_IT_IDLE, ENABLE); /*舱位串口DMA接纳*/ USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE); USART_Cmd(USART1,ENABLE);}
代码比较简单,一看就理解了,这便是运用库函数开发的优点,代码易懂。这儿,要害的是要舱位总线闲暇中止,而且舱位串口DMA接纳。留意,不要舱位串口接纳中止,否则接纳数据就会一向发生中止了。
2、DMA装备
DMA装备,要先检查串口接纳是运用的哪个DMA的哪个通道,关于USART1_RX运用的是DMA1的5通道。
然后便是代码装备DMA了。
void DMA_init(void){ DMA_InitTypeDef DMA_Initstructure;// NVIC_InitTypeDef NVIC_Initstructure; /*舱位DMA时钟*/ RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE); // /* Enable the DMA1 Interrupt */// NVIC_Initstructure.NVIC_IRQChannel = DMA1_Channel4_IRQn; //通道设置为串口1中止// NVIC_Initstructure.NVIC_IRQChannelSubPriority = 1; //中止呼应优先级0// NVIC_Initstructure.NVIC_IRQChannelPreemptionPriority=1;// NVIC_Initstructure.NVIC_IRQChannelCmd = ENABLE; //翻开中止// NVIC_Init(&NVIC_Initstructure); /*DMA装备*/ DMA_Initstructure.DMA_PeripheralBaseAddr = (u32)(&USART1->DR);; DMA_Initstructure.DMA_MemoryBaseAddr = (u32)receive_data; DMA_Initstructure.DMA_DIR = DMA_DIR_PeripheralSRC; DMA_Initstructure.DMA_BufferSize = 128; 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_MemoryDataSize_Byte; DMA_Initstructure.DMA_Mode = DMA_Mode_Normal; DMA_Initstructure.DMA_Priority = DMA_Priority_High; DMA_Initstructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel5,&DMA_Initstructure); //发动DMA DMA_Cmd(DMA1_Channel5,ENABLE); //舱位DMA发送发成中止 //DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE); }
由于这儿,不需求用到DMA中止,所以DMA中止就不要使能了。因而DMA中止装备也就不需求了。这儿,要害的是要设置DMA_DIR为DMA_DIR_PeripheralSRC,一共数据是从外设到内存。这儿设定的DMA_Mode是一般形式,即数据传输就只能一次。
3、串口中止程序编写
这个便是要害的当地了。在这儿,需求做什么了。需求对DMA设置下。当进入这个中止的时分,串口接纳的数据,已经在内存的数组中了。经过读取DMA的计数值,就能够知道接纳到了多少个数据。然后再把DMA给diable掉,从头设置接纳数据长度,在舱位DMA,接纳下一次串口数据。为什么要这么做了,由于在STM32手册中有如下阐明:
别的还有一点,串口闲暇中止触发后,硬件会主动将串口闲暇中止标志位给置1,咱们是需求将给标志位给置0的,否则又要进中止了,这个在手册中也有阐明。
代码就如下了:
void USART1_IRQHandler(void){ unsigned char num=0; if(USART_GetITStatus(USART1,USART_IT_IDLE) == SET) { num = USART1->SR; num = USART1->DR; //清USART_IT_IDLE标志 DMA_Cmd(DMA1_Channel5,DISABLE); //封闭DMA num = 128 - DMA_GetCurrDataCounter(DMA1_Channel5); //得到真实接纳数据个数 receive_data[num] = \0; DMA1_Channel5->CNDTR=128; //从头设置接纳数据个数 DMA_Cmd(DMA1_Channel5,ENABLE); //舱位DMA receive_flag = 1; //接纳数据标志方位1 }}
要害的一点,便是要读取SR,DR,将USART_IT_IDLE标志给清掉,然后DMA设置要留意下。
在主函数中,运用下面代码测验:
int main(){ periph_init(); printf("hello world\n"); while(1) { while(receive_flag == 0); receive_flag = 0; printf("%s",receive_data); }}
当串口接纳数据后,中止程序会使receive_flag为1,然后就跳出while循环。打印接纳到的数据。
测验成果:
发送什么,就接纳什么。
还测验了下,在波特率460800下,都仍是能正常的作业的。