本帖最后由 shenwushen 于 2021-11-26 10:55 编辑
#有奖活动# #申请原创#@21小跑堂
我用的是GD32F103RC(注:请确保自己有一个可以编译的GD32F103工程且里面已经包含了标准库)。
- 1、首先先配置各硬件外设的时钟(DMA时钟,GPIO时钟,USART时钟,AF时钟)。
void SystemClock_Reconfig(void)
{
/* Initializes the CPU, AHB and APB busses clocks */
rcu_pll_config(RCU_PLLSRC_HXTAL,RCU_PLL_MUL9);
rcu_system_clock_source_config(RCU_CKSYSSRC_PLL);
/* Due to AHB,APB2,APB1 has been initialized in SystemInit();(AHB = SYSCLK,APB2 = AHB/1,APB1 = AHB/2)
* We don't need to configure it again if unnecessary.
*
* CK_APB1 Max is 54MHz and CK_APB2 Max is 108MHz in GD32F10X,
* Please ensure your prescaler is not above Max Frequency.
*/
// rcu_ahb_clock_config(RCU_AHB_CKSYS_DIV1);
// rcu_apb1_clock_config(RCU_APB1_CKAHB_DIV2);
// rcu_apb2_clock_config(RCU_APB2_CKAHB_DIV1);
rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV6);
// rcu_usb_clock_config(RCU_CKUSB_CKPLL_DIV2);
// rcu_rtc_clock_config(RCU_RTCSRC_HXTAL_DIV_128);
/* Enable all peripherals clocks you need*/
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_GPIOB);
rcu_periph_clock_enable(RCU_GPIOC);
rcu_periph_clock_enable(RCU_GPIOD);
rcu_periph_clock_enable(RCU_GPIOE);
rcu_periph_clock_enable(RCU_GPIOF);
rcu_periph_clock_enable(RCU_GPIOG);
rcu_periph_clock_enable(RCU_AF);
rcu_periph_clock_enable(RCU_DMA0);
rcu_periph_clock_enable(RCU_DMA1);
rcu_periph_clock_enable(RCU_I2C1);
rcu_periph_clock_enable(RCU_ADC0);
rcu_periph_clock_enable(RCU_ADC2);
rcu_periph_clock_enable(RCU_USART1);
rcu_periph_clock_enable(RCU_USART2);
/* Timer1,2,3,4,5,6,11,12,13 are hanged on APB1,
* Timer0,7,8,9,10 are hanged on APB2
*/
rcu_periph_clock_enable(RCU_TIMER1);
}
- 2、接着配置咱们的GPIO。
- 我是用的是USART1(PA2、PA3)、USART2(PC10、PC11),再根据上面两表的描述,具体配置如下:
USAR1、USART2发送都使用DMA。接收如果是空闲中断的话使用DMA,否则仅使用接收中断。这里不列出查询法,因为在真正用32开发的项目中很少使用主函数查询法去等待接收或者发送数据,这种方法仅在demo实例中出现,实用性不高。
这里发送与接收都不需要配置成DMA循环模式。首先发送不能循环,这很好理解,每次发的数据和什么时候发由我们自己决定,我们使用它只是为了使用一次发送命令发送多个字节。
接收是否循环并没有多大关系。因为我们使用的是接收空闲中断,单片机外设在一个帧时间内,RX引脚检测到空闲状态,该位置1。也就意味着当一组数据传输完成后,才会产生一次接受空闲中断。在该中断里我们会重新置为DMA通道,无论是否开启循环都会被重新置位,所以这里默认关闭循环即可。对应TX的DMA通道初始化完后先关闭掉,等到要发数据时我们再开启它。 [color=rgba(0,]
/* USARTx Receiving Mode */
#define USARTx_RX_BY_IDLE_DMA 1 //1:使用接收空闲中断+DMA 0:仅使用接收中断
/* USARTx ADrawbuffer definition */
#define USART1_TX_SIZE 8
#define USART1_RX_SIZE 8
#define USART2_TX_SIZE 8
#define USART2_RX_SIZE 8
uint8_t USART1TX_Buffer[USART1_TX_SIZE] = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08};
uint8_t USART1RX_Buffer[USART1_RX_SIZE] = {0};
uint8_t USART2TX_Buffer[USART2_TX_SIZE] = {0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18};
uint8_t USART2RX_Buffer[USART2_RX_SIZE] = {0};
void DMA_Init(void)
{
dma_parameter_struct dma_init_Usart1_TX;
dma_parameter_struct dma_init_Usart1_RX;
dma_parameter_struct dma_init_Usart2_TX;
dma_parameter_struct dma_init_Usart2_RX;
dma_deinit(DMA0, DMA_CH1);
dma_deinit(DMA0, DMA_CH2);
dma_deinit(DMA0, DMA_CH5);
dma_deinit(DMA0, DMA_CH6);
/* initialize DMA0 channel1(Usart2_TX) */
dma_init_Usart2_TX.direction = DMA_MEMORY_TO_PERIPHERAL;
dma_init_Usart2_TX.memory_addr = (uint32_t)USART2TX_Buffer;
dma_init_Usart2_TX.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_init_Usart2_TX.memory_width = DMA_MEMORY_WIDTH_8BIT;
dma_init_Usart2_TX.number = (uint32_t)USART2_TX_SIZE;
dma_init_Usart2_TX.periph_addr = (uint32_t)(&USART_DATA(USART2));
dma_init_Usart2_TX.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_init_Usart2_TX.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
dma_init_Usart2_TX.priority = DMA_PRIORITY_LOW;
dma_init(DMA0, DMA_CH1, &dma_init_Usart2_TX);
dma_circulation_disable(DMA0, DMA_CH1);
/* initialize DMA0 channel2(Usart2_RX) */
dma_init_Usart2_RX.direction = DMA_PERIPHERAL_TO_MEMORY;
dma_init_Usart2_RX.memory_addr = (uint32_t)USART2RX_Buffer;
dma_init_Usart2_RX.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_init_Usart2_RX.memory_width = DMA_MEMORY_WIDTH_8BIT;
dma_init_Usart2_RX.number = (uint32_t)USART2_RX_SIZE;
dma_init_Usart2_RX.periph_addr = (uint32_t)(&USART_DATA(USART2));
dma_init_Usart2_RX.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_init_Usart2_RX.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
dma_init_Usart2_RX.priority = DMA_PRIORITY_LOW;
dma_init(DMA0, DMA_CH2, &dma_init_Usart2_RX);
dma_circulation_disable(DMA0, DMA_CH2);
/* initialize DMA0 channel6(Usart1_TX) */
dma_init_Usart1_TX.direction = DMA_MEMORY_TO_PERIPHERAL;
dma_init_Usart1_TX.memory_addr = (uint32_t)USART1TX_Buffer;
dma_init_Usart1_TX.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_init_Usart1_TX.memory_width = DMA_MEMORY_WIDTH_8BIT;
dma_init_Usart1_TX.number = (uint32_t)USART1_TX_SIZE;
dma_init_Usart1_TX.periph_addr = (uint32_t)(&USART_DATA(USART1));
dma_init_Usart1_TX.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_init_Usart1_TX.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
dma_init_Usart1_TX.priority = DMA_PRIORITY_LOW;
dma_init(DMA0, DMA_CH6, &dma_init_Usart1_TX);
dma_circulation_disable(DMA0, DMA_CH6);
/* initialize DMA0 channel5(Usart1_RX) */
dma_init_Usart1_RX.direction = DMA_PERIPHERAL_TO_MEMORY;
dma_init_Usart1_RX.memory_addr = (uint32_t)USART1RX_Buffer;
dma_init_Usart1_RX.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_init_Usart1_RX.memory_width = DMA_MEMORY_WIDTH_8BIT;
dma_init_Usart1_RX.number = (uint32_t)USART1_RX_SIZE;
dma_init_Usart1_RX.periph_addr = (uint32_t)(&USART_DATA(USART1));
dma_init_Usart1_RX.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_init_Usart1_RX.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
dma_init_Usart1_RX.priority = DMA_PRIORITY_LOW;
dma_init(DMA0, DMA_CH5, &dma_init_Usart1_RX);
dma_circulation_disable(DMA0, DMA_CH5); //circulate or not makes no difference
dma_memory_to_memory_disable(DMA0,DMA_CH1);
dma_memory_to_memory_disable(DMA0,DMA_CH2);
dma_memory_to_memory_disable(DMA0,DMA_CH5);
dma_memory_to_memory_disable(DMA0,DMA_CH6);
/* enable all DMA channels you need */
dma_channel_disable(DMA0,DMA_CH1);
dma_channel_enable(DMA0,DMA_CH2);
dma_channel_enable(DMA0,DMA_CH5);
dma_channel_disable(DMA0,DMA_CH6);
}
4、再配置USART1、UASRT2波特率38400,数据位8位,停止位1位,无校验位,不使用硬件流控制,接收/发送使能,允许发送使用DMA功能。接收是否使用DMA功能由USARTx_RX_BY_IDLE_DMA这个宏决定。void USARTx_Init(void) { /* USART1 configure */ usart_deinit(USART1); usart_baudrate_set(USART1, 38400U); usart_word_length_set(USART1, USART_WL_8BIT); usart_stop_bit_set(USART1, USART_STB_1BIT); usart_parity_config(USART1, USART_PM_NONE); usart_hardware_flow_rts_config(USART1, USART_RTS_DISABLE); usart_hardware_flow_cts_config(USART1, USART_CTS_DISABLE); usart_receive_config(USART1, USART_RECEIVE_ENABLE); usart_transmit_config(USART1, USART_TRANSMIT_ENABLE); usart_enable(USART1); /* USART2 configure */ usart_deinit(USART2); usart_baudrate_set(USART2, 38400U); usart_word_length_set(USART2, USART_WL_8BIT); usart_stop_bit_set(USART2, USART_STB_1BIT); usart_parity_config(USART2, USART_PM_NONE); usart_hardware_flow_rts_config(USART2, USART_RTS_DISABLE); usart_hardware_flow_cts_config(USART2, USART_CTS_DISABLE); usart_receive_config(USART2, USART_RECEIVE_ENABLE); usart_transmit_config(USART2, USART_TRANSMIT_ENABLE); usart_enable(USART2); /* config USARTx_TX transmit by DMA */ usart_dma_transmit_config(USART1,USART_DENT_ENABLE); usart_dma_transmit_config(USART2,USART_DENT_ENABLE);
#if USARTx_RX_BY_IDLE_DMA usart_dma_receive_config(USART1,USART_DENR_ENABLE); usart_dma_receive_config(USART2,USART_DENR_ENABLE); #endif
}
5、中断设置
- 这里根据接收方式选择开哪种接收中断void Nvic_init(void)
{ /* TIMER1 IRQ set */ nvic_irq_enable(TIMER1_IRQn, 1, 0); timer_interrupt_enable(TIMER1, TIMER_INT_UP); /* USART1/2 IRQ set */ nvic_irq_enable(USART1_IRQn, 1, 0); nvic_irq_enable(USART2_IRQn, 1, 0); #if USARTx_RX_BY_IDLE_DMA usart_interrupt_enable(USART1, USART_INT_FLAG_IDLE); usart_interrupt_enable(USART2, USART_INT_FLAG_IDLE); #else usart_interrupt_enable(USART1, USART_INT_RBNE); usart_interrupt_enable(USART2, USART_INT_RBNE); #endif }
6、中断服务函数
USART1与USART2各一个,我们只用看其中一个即可,另一个与之相同。
如果USARTx_RX_BY_IDLE_DMA这个宏为真,即选择使用接收空闲中断+DMA的方式接收数据。我们通过读相应的数据寄存器去清这个标志位(手册描述的清位方式),再重新置为对应的DMA接收通道。程序运行到此处时,DMA通道已经获取了一个或多个字节的数据(看你发了多少字节的数据),关闭再开启则是让DMA的内存buffer再次从头开始(比如我们定义的USART1RX_Buffer数组是USART1_RX的内存buff,在接受了3个字节的数据后,从USART1RX_Buffer[0]移到了USART1RX_Buffer[3],复位DMA后又将从USART1RX_Buffer[0]开始)。这样只要保证最大字节数不被覆盖,在有标志位置1后及时处理,就能完美接收不定长的数据,且程序被中断的次数最少。
如果USARTx_RX_BY_IDLE_DMA这个宏为假,即选择仅使用接收中断。那么每接收到一个字节的数据就会产生一次中断,再将每个字节的内容从设备数据寄存器搬运到我们自己定义的内存变量里。
void USART1_IRQHandler(void) { #if USARTx_RX_BY_IDLE_DMA if(RESET != usart_interrupt_flag_get(USART1, USART_INT_FLAG_IDLE)) { Module.USART1_RX_OK=1; /* clear USART_INT_FLAG_IDLE */ usart_data_receive(USART1); /* disable USART1_RX DMA_Channel */ dma_channel_disable(DMA0, DMA_CH5); /* reset DMA_Channel CNT */ dma_transfer_number_config(DMA0, DMA_CH5, USART1_RX_SIZE); /* enable USART1_RX DMA_Channel */ dma_channel_enable(DMA0, DMA_CH5); } #else if(RESET != usart_interrupt_flag_get(USART1, USART_INT_FLAG_RBNE)) { usart_interrupt_flag_clear(USART1,USART_INT_FLAG_RBNE); Module.USART1_RX_OK=1; USART1RX_Buffer[0] = usart_data_receive(USART1); } #endif }
void USART2_IRQHandler(void) { #if USARTx_RX_BY_IDLE_DMA if(RESET != usart_interrupt_flag_get(USART2, USART_INT_FLAG_IDLE)) { Module.USART2_RX_OK=1; /* clear USART_INT_FLAG_IDLE */ usart_data_receive(USART2); /* disable USART1_RX DMA_Channel */ dma_channel_disable(DMA0, DMA_CH2); /* reset DMA_Channel CNT */ dma_transfer_number_config(DMA0, DMA_CH2, USART2_RX_SIZE); /* enable USART1_RX DMA_Channel */ dma_channel_enable(DMA0, DMA_CH2); } #else if(RESET != usart_interrupt_flag_get(USART2, USART_INT_FLAG_RBNE)) { usart_interrupt_flag_clear(USART2,USART_INT_FLAG_RBNE); Module.USART2_RX_OK=1; USART2RX_Buffer[0] = usart_data_receive(USART2); } #endif }
7、发送DMA
void Usartx_TX_DMA_Send(uint32_t usart_periph,uint8_t* data_buffer,uint8_t length) { if(usart_periph==USART1) { /* Channel disable */ dma_channel_disable(DMA0, DMA_CH6); dma_memory_address_config(DMA0, DMA_CH6,(uint32_t)data_buffer); dma_transfer_number_config(DMA0,DMA_CH6,length); /* enable DMA channel to start send */ dma_channel_enable(DMA0, DMA_CH6); } else if(usart_periph==USART2) { /* Channel disable */ dma_channel_disable(DMA0, DMA_CH1); dma_memory_address_config(DMA0, DMA_CH1,(uint32_t)data_buffer); dma_transfer_number_config(DMA0,DMA_CH1,length); /* enable DMA channel to start send */ dma_channel_enable(DMA0, DMA_CH1); } }
主函数部分
- Module.OLED_REFRESH是每秒刷新一次,即每秒DMA传送一次。
- 我使用OLED显示接收到的数据。
- int main(void)
- {
- /* Reset of all peripherals, Initializes the Systick. */
- SystemTick_Init();
- /* Initializes all peripherals clock you need */
- SystemClock_Reconfig();
- /* Initialize all configured peripherals */
- GPIO_Init();
- DMA_Init();
- OLED_Init();
- ADCx_Init();
- USARTx_Init();
- Nvic_init();
- /* trigger start ADCx_channels conversion */
- adc_software_trigger_enable(ADC0, ADC_REGULAR_CHANNEL);
- adc_software_trigger_enable(ADC2, ADC_REGULAR_CHANNEL);
- while(1)
- {
- if(Module.LED_REFRESH)
- {
- gpio_bit_set(GPIOB,GPIO_PIN_4);
- }
- else
- {
- gpio_bit_reset(GPIOB,GPIO_PIN_4);
- }
- if(Module.OLED_REFRESH)
- {
- Module.OLED_REFRESH = 0;
- // Usartx_TX_DMA_Send(USART1,USART1TX_Buffer,(uint8_t)USART1_TX_SIZE);
- // Usartx_TX_DMA_Send(USART1,(uint8_t*)"hello,C world!\n",15);
- // Usartx_TX_DMA_Send(USART2,(uint8_t*)"hello,C world!\n",15);
- Usartx_TX_DMA_Send(USART2,USART2TX_Buffer,(uint8_t)USART2_TX_SIZE);
- OLED_ShowNum(24,0,USART1RX_Buffer[0],5,16);
- OLED_ShowNum(24,2,USART1RX_Buffer[1],5,16);
- OLED_ShowNum(24,4,USART1RX_Buffer[2],5,16);
- OLED_ShowNum(24,6,USART2RX_Buffer[1],5,16);//USART1RX_Buffer[3] memory_r[4]
- OLED_ShowNum(96,0,ADC0_Buffer[1][0],4,16);
- OLED_ShowNum(96,2,ADC2_Buffer[0][0],4,16);
- OLED_ShowNum(96,4,USART2RX_Buffer[0],4,16);
- OLED_ShowNum(96,6,USART2RX_Buffer[2],4,16);
- }
- }
- }
至此, 就完成了,由于现有的板子拿去修了,所以无法给大家展示结果图。但是这个流程是走通的,码字不易,求点赞,谢谢各位!
|