【AT32】第三章 雅特力AT32F437VGT7GPIO串口轮询,中断,弹性DMA解析

文章详细介绍了在AT32微控制器上配置UART进行阻塞式轮询、中断接收以及使用DMA传输数据的过程。代码示例包括初始化GPIO、串口参数设置、中断服务函数和DMA配置。同时,提供了测试用例展示不同模式下的功能验证。
摘要由CSDN通过智能技术生成

阻塞式轮询

void uart_poll_init(uint32_t baudrate)
{
	/* Configure gpio first */
  gpio_init_type gpio_init_struct; 
	
  /* enable the uart and gpio clock */
  crm_periph_clock_enable(PRINT_UART_CRM_CLK, TRUE);
  crm_periph_clock_enable(PRINT_UART_TX_GPIO_CRM_CLK, TRUE);
	crm_periph_clock_enable(PRINT_UART_RX_GPIO_CRM_CLK,TRUE);

  gpio_default_para_init(&gpio_init_struct);

  /* configure the uart tx pin */
  gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
  gpio_init_struct.gpio_out_type  = GPIO_OUTPUT_PUSH_PULL;
  gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
  gpio_init_struct.gpio_pins = PRINT_UART_TX_PIN;
  gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
  gpio_init(PRINT_UART_TX_GPIO, &gpio_init_struct);
	
	gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
  gpio_init_struct.gpio_pins = PRINT_UART_RX_PIN;
	gpio_init(PRINT_UART_RX_GPIO, &gpio_init_struct);

  gpio_pin_mux_config(PRINT_UART_TX_GPIO, PRINT_UART_TX_PIN_SOURCE, PRINT_UART_TX_PIN_MUX_NUM);
  gpio_pin_mux_config(PRINT_UART_RX_GPIO, PRINT_UART_RX_PIN_SOURCE, PRINT_UART_RX_PIN_MUX_NUM);

  /* configure uart param */
  usart_init(PRINT_UART, baudrate, USART_DATA_8BITS, USART_STOP_1_BIT);
	usart_parity_selection_config(PRINT_UART, USART_PARITY_NONE);
  usart_transmitter_enable(PRINT_UART, TRUE);
	usart_receiver_enable(PRINT_UART, TRUE);
  usart_enable(PRINT_UART, TRUE);
}

这里使用的是uart1,首先对串口时钟以及GPIO的时钟进行使能,gpio_default_para_init(&gpio_init_struct);是对定义的gpio_init_struct进行填充默认值,然后对串口的RX、TX引脚进行复用,到这里GPIO的配置有完毕了,最后对串口进行配置,波特率我使用的传参的形式,其他的值配置成了8数据位,1个停止位,无校验,最后对发送、接收以及串口进行使能,,到这里串口阻塞式轮询就配置完了,为了修改方便我对它进行了宏定义封装。

#define PRINT_UART                       USART1
#define PRINT_UART_CRM_CLK               CRM_USART1_PERIPH_CLOCK
#define PRINT_UART_TX_PIN                GPIO_PINS_9
#define PRINT_UART_TX_GPIO               GPIOA
#define PRINT_UART_TX_GPIO_CRM_CLK       CRM_GPIOA_PERIPH_CLOCK
#define PRINT_UART_TX_PIN_SOURCE         GPIO_PINS_SOURCE9
#define PRINT_UART_TX_PIN_MUX_NUM        GPIO_MUX_7

#define PRINT_UART_RX_PIN                GPIO_PINS_10
#define PRINT_UART_RX_GPIO               GPIOA
#define PRINT_UART_RX_GPIO_CRM_CLK       CRM_GPIOA_PERIPH_CLOCK
#define PRINT_UART_RX_PIN_SOURCE         GPIO_PINS_SOURCE10
#define PRINT_UART_RX_PIN_MUX_NUM        GPIO_MUX_7
#define USARTx_IRQn										   USART1_IRQn

#define PRINT_UART_DMA_CRM_CLK 					 CRM_DMA1_PERIPH_CLOCK
#define DMAx_Port 					   					 DMA1
#define UART_DMA_TX_CH				 					 DMA1_CHANNEL1
#define DMAMUX_TX_CH				 						 DMA1MUX_CHANNEL1
#define DMAMUX_CH_TO_TXBASE			 				 DMAMUX_DMAREQ_ID_USART1_TX
#define UART_DMA_RX_CH				 					 DMA1_CHANNEL2
#define DMAMUX_RX_CH				 						 DMA1MUX_CHANNEL2
#define DMAMUX_CH_TO_RXBASE			 				 DMAMUX_DMAREQ_ID_USART1_RX

上面的定义中包含中断,dma的重定义。

我对at32提供的函数进行了简单的封装。对他实现了发送回显,以及数据传输。

void uart_poll_ping(void)
{
	uint16_t ch;
	while(usart_flag_get(PRINT_UART, USART_RDBF_FLAG) == RESET);
	ch = usart_data_receive(PRINT_UART);
	while(usart_flag_get(PRINT_UART, USART_TDBE_FLAG) == RESET);
	usart_data_transmit(PRINT_UART, ch);
}

void uart_transmit_poll(uint8_t cmd,uint16_t *ch,uint8_t length)
{
	if(cmd == 0)
	{
		for(uint8_t i = 0;i < length;i++)
		{
			while(usart_flag_get(PRINT_UART, USART_TDBE_FLAG) == RESET){};
			usart_data_transmit(PRINT_UART, *ch);
			ch++;
		}
	}
	else if(cmd == 1)
	{
	
		for(uint8_t i = 0;i < length;i++)
		{
			while(usart_flag_get(PRINT_UART, USART_RDBF_FLAG) == RESET);
			*ch = usart_data_receive(PRINT_UART);
			ch++;
		}
	}
}

这里提醒一下各位,串口发送和接收虽然是以一个字节为单位,但是函数接收的两个字节,它函数内部对数据进行了位与操作

/**
  * @brief  transmit single data through the usart peripheral.
  * @param  usart_x: select the usart or the uart peripheral.
  *         this parameter can be one of the following values:
  *         USART1, USART2, USART3, UART4, UART5, USART6, UART7 or UART8.
  * @param  data: the data to transmit.
  * @retval none
  */
void usart_data_transmit(usart_type* usart_x, uint16_t data)
{
  usart_x->dt = (data & 0x01FF);
}

下面看一下测试结果

void uart1_poll_model_test(void)
{
	char ch1[] = "uart1 poll model test.\n";
	char ch2[] = "uart1 poll ping.\nEnter a character for the command output.\n";
	uint16_t converted_ch1[sizeof(ch1) / sizeof(ch1[0]) * 2]; 
	for (int i = 0; i < sizeof(ch1) / sizeof(ch1[0]); i++) 
	{
			converted_ch1[i * 2] = ch1[i] & 0xFF; 
			converted_ch1[i * 2 + 1] = (ch1[i] >> 8) & 0xFF; 
	}
	uint16_t converted_ch2[sizeof(ch2) / sizeof(ch2[0]) * 2]; 
	for (int i = 0; i < sizeof(ch2) / sizeof(ch2[0]); i++) 
	{
			converted_ch2[i * 2] = ch2[i] & 0xFF; 
			converted_ch2[i * 2 + 1] = (ch2[i] >> 8) & 0xFF; 
	}
	uart_poll_init(115200);
	
	uart_transmit_poll(0,converted_ch1,sizeof(converted_ch1) / sizeof(converted_ch1[0]));
	uart_transmit_poll(0,converted_ch2,sizeof(converted_ch2) / sizeof(converted_ch2[0]));
	while(1)
	{
		uart_poll_ping();
	}
	

}

我新建了一个bsp文件,这个根据个人习惯,也可以直接在main文件添加。
main函数

int main(void)
{
  system_clock_config();
  at32_board_init();
//	uart1_int_model_test();


//	uart_dmapoll_send((uint8_t *)&dma_poll_send_test,sizeof(dma_poll_send_test) / sizeof(dma_poll_send_test[0]));
  while(1)
  {
		uart1_poll_model_test();
//		uart1_dma_model_test();


  }
	return 0;
}

烧入后串口打印结果
在这里插入图片描述

中断接收阻塞式发送


void uart_int_init(uint32_t baudrate)
{
		/* Configure gpio first */
  gpio_init_type gpio_init_struct; 
	
  /* enable the uart and gpio clock */
  crm_periph_clock_enable(PRINT_UART_CRM_CLK, TRUE);
  crm_periph_clock_enable(PRINT_UART_TX_GPIO_CRM_CLK, TRUE);
	crm_periph_clock_enable(PRINT_UART_RX_GPIO_CRM_CLK,TRUE);

  gpio_default_para_init(&gpio_init_struct);

  /* configure the uart tx pin */
  gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
  gpio_init_struct.gpio_out_type  = GPIO_OUTPUT_PUSH_PULL;
  gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
  gpio_init_struct.gpio_pins = PRINT_UART_TX_PIN;
  gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
  gpio_init(PRINT_UART_TX_GPIO, &gpio_init_struct);
	
	gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
  gpio_init_struct.gpio_pins = PRINT_UART_RX_PIN;
	gpio_init(PRINT_UART_RX_GPIO, &gpio_init_struct);

  gpio_pin_mux_config(PRINT_UART_TX_GPIO, PRINT_UART_TX_PIN_SOURCE, PRINT_UART_TX_PIN_MUX_NUM);
  gpio_pin_mux_config(PRINT_UART_RX_GPIO, PRINT_UART_RX_PIN_SOURCE, PRINT_UART_RX_PIN_MUX_NUM);

	nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);
  nvic_irq_enable(USARTx_IRQn, 0, 0);

  /* configure uart param */
  usart_init(PRINT_UART, baudrate, USART_DATA_8BITS, USART_STOP_1_BIT);
	usart_parity_selection_config(PRINT_UART, USART_PARITY_NONE);
  usart_transmitter_enable(PRINT_UART, TRUE);
	usart_receiver_enable(PRINT_UART, TRUE);
	
	usart_interrupt_enable(PRINT_UART, USART_RDBF_INT, TRUE);


  usart_enable(PRINT_UART, TRUE);

}

这段代码与阻塞式轮询代码只是添加了中断优先级配置以及中断使能和接收中断使能。

	nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);
  nvic_irq_enable(USARTx_IRQn, 0, 0);
  usart_interrupt_enable(PRINT_UART, USART_RDBF_INT, TRUE);

由于接收进入中断后他会进入void USART1_IRQHandler(void)回调函数于是我又添加一个回调函数typedef void (*CallbackFunction)(uint16_t data);,方便把程序移植。

void setCallback(CallbackFunction cb)
{
    temp_callback = cb;
}




void USART1_IRQHandler(void)
{

	uint16_t ch;
    if(usart_flag_get(PRINT_UART, USART_RDBF_FLAG) != RESET)
    {
      /* read one byte from the receive data register */
      ch = usart_data_receive(PRINT_UART);			
			temp_callback(ch);
    }
  
}

void uart_send(uint16_t *ch,uint8_t length)
{

	for(uint8_t i = 0;i < length;i++)
	{
		while(usart_flag_get(PRINT_UART, USART_TDBE_FLAG) == RESET){};
		usart_data_transmit(PRINT_UART, *ch);
		ch++;
	}

}

该段代码实现了中断绑定和数据发送。
下面添加测试代码

static void myCallbackFunction(uint16_t data)
{
	char ch[] = "uart1 is interrupted.\n";
	uint16_t converted[sizeof(ch) / sizeof(ch[0]) * 2]; 
	for (int i = 0; i < sizeof(ch) / sizeof(ch[0]); i++) 
	{
			converted[i * 2] = ch[i] & 0xFF; 
			converted[i * 2 + 1] = (ch[i] >> 8) & 0xFF; 
	}
	uart_send(converted, sizeof(converted) / sizeof(converted[0]));
}


void uart1_int_model_test(void)
{
	char ch1[] = "uart1 int model test.\n";
	char ch2[] = "Please enter a character to trigger an interrupt.\n";
	uint16_t converted_ch1[sizeof(ch1) / sizeof(ch1[0]) * 2]; 
	for (int i = 0; i < sizeof(ch1) / sizeof(ch1[0]); i++) 
	{
			converted_ch1[i * 2] = ch1[i] & 0xFF; 
			converted_ch1[i * 2 + 1] = (ch1[i] >> 8) & 0xFF; 
	}
	uint16_t converted_ch2[sizeof(ch2) / sizeof(ch2[0]) * 2]; 
	for (int i = 0; i < sizeof(ch2) / sizeof(ch2[0]); i++) 
	{
			converted_ch2[i * 2] = ch2[i] & 0xFF; 
			converted_ch2[i * 2 + 1] = (ch2[i] >> 8) & 0xFF; 
	}
	uart_int_init(115200);
	uart_send(converted_ch1, sizeof(converted_ch1) / sizeof(converted_ch1[0]));
	uart_send(converted_ch2, sizeof(converted_ch2) / sizeof(converted_ch2[0]));
	setCallback(myCallbackFunction);
}

static void myCallbackFunction(uint16_t data)是自己定义的回调函数,触发中断后会触发它,然后通过setCallback(myCallbackFunction);注册这个回调函数。由于是接收中断,就不需要添加到while循环里了。
main函数

int main(void)
{
  system_clock_config();
  at32_board_init();
	uart1_int_model_test();



  while(1)
  {
//		uart1_poll_model_test();
		
//		uart1_dma_model_test();


  }
	return 0;
}

测试结果
在这里插入图片描述

弹性DMA

static void dma_configuration(void)
{
  dma_init_type dma_init_struct;

  /* enable dma clock */
  crm_periph_clock_enable(PRINT_UART_DMA_CRM_CLK, TRUE);

  dmamux_enable(DMAx_Port, TRUE);

  /* dma1 channel for usart tx configuration */
  dma_reset(UART_DMA_TX_CH);
  dma_default_para_init(&dma_init_struct);
  dma_init_struct.buffer_size = 0;
  dma_init_struct.direction = DMA_DIR_MEMORY_TO_PERIPHERAL;
  dma_init_struct.memory_base_addr = (uint32_t)0;
  dma_init_struct.memory_data_width = DMA_MEMORY_DATA_WIDTH_BYTE;
  dma_init_struct.memory_inc_enable = TRUE;
  dma_init_struct.peripheral_base_addr = (uint32_t)&PRINT_UART->dt;
  dma_init_struct.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_BYTE;
  dma_init_struct.peripheral_inc_enable = FALSE;
  dma_init_struct.priority = DMA_PRIORITY_MEDIUM;
  dma_init_struct.loop_mode_enable = FALSE;
  dma_init(UART_DMA_TX_CH, &dma_init_struct);

  /* config flexible dma for usart tx */
  dmamux_init(DMAMUX_TX_CH, DMAMUX_CH_TO_TXBASE);

  /* dma1 channel for usart rx configuration */
  dma_reset(UART_DMA_RX_CH);
  dma_default_para_init(&dma_init_struct);
  dma_init_struct.buffer_size = 0;
  dma_init_struct.direction = DMA_DIR_PERIPHERAL_TO_MEMORY;
  dma_init_struct.memory_base_addr = (uint32_t)0;
  dma_init_struct.memory_data_width = DMA_MEMORY_DATA_WIDTH_BYTE;
  dma_init_struct.memory_inc_enable = TRUE;
  dma_init_struct.peripheral_base_addr = (uint32_t)&PRINT_UART->dt;
  dma_init_struct.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_BYTE;
  dma_init_struct.peripheral_inc_enable = FALSE;
  dma_init_struct.priority = DMA_PRIORITY_MEDIUM;
  dma_init_struct.loop_mode_enable = FALSE;
  dma_init(UART_DMA_RX_CH, &dma_init_struct);

  /* config flexible dma for usart rx */
  dmamux_init(DMAMUX_RX_CH, DMAMUX_CH_TO_RXBASE);

  dma_channel_enable(UART_DMA_TX_CH, FALSE); 
  dma_channel_enable(UART_DMA_RX_CH, FALSE); 

}

void uart_dmaPoll_init(uint32_t baudrate)
{
			/* Configure gpio first */
  gpio_init_type gpio_init_struct; 
	
  /* enable the uart and gpio clock */
  crm_periph_clock_enable(PRINT_UART_CRM_CLK, TRUE);
  crm_periph_clock_enable(PRINT_UART_TX_GPIO_CRM_CLK, TRUE);
	crm_periph_clock_enable(PRINT_UART_RX_GPIO_CRM_CLK,TRUE);

  gpio_default_para_init(&gpio_init_struct);

  /* configure the uart tx pin */
  gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
  gpio_init_struct.gpio_out_type  = GPIO_OUTPUT_PUSH_PULL;
  gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
  gpio_init_struct.gpio_pins = PRINT_UART_TX_PIN;
  gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
  gpio_init(PRINT_UART_TX_GPIO, &gpio_init_struct);
	
	gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
  gpio_init_struct.gpio_pins = PRINT_UART_RX_PIN;
	gpio_init(PRINT_UART_RX_GPIO, &gpio_init_struct);

  gpio_pin_mux_config(PRINT_UART_TX_GPIO, PRINT_UART_TX_PIN_SOURCE, PRINT_UART_TX_PIN_MUX_NUM);
  gpio_pin_mux_config(PRINT_UART_RX_GPIO, PRINT_UART_RX_PIN_SOURCE, PRINT_UART_RX_PIN_MUX_NUM);

  /* configure uart param */
  usart_init(PRINT_UART, baudrate, USART_DATA_8BITS, USART_STOP_1_BIT);
	usart_parity_selection_config(PRINT_UART, USART_PARITY_NONE);
  usart_transmitter_enable(PRINT_UART, TRUE);
	usart_receiver_enable(PRINT_UART, TRUE);
	
	usart_dma_transmitter_enable(PRINT_UART, FALSE);
  usart_dma_receiver_enable(PRINT_UART, FALSE);

  usart_enable(PRINT_UART, TRUE);
	dma_configuration();
}

uart_dmaPoll_init函数实现的是对串口的GPIO和串口的配置代码,usart_dma_transmitter_enable(PRINT_UART, FALSE); usart_dma_receiver_enable(PRINT_UART, FALSE);后面添加了对串口mda的使能,然后调用dma_configuration();配置dma,这里dma具体配置了什么就不具体赘述了,相信看到这里大家也应熟练了at32库函数的逻辑与风格,因为它跟stm32一样,这里说明一下如何配置了弹性dma。dmamux_init(DMAMUX_TX_CH, DMAMUX_CH_TO_TXBASE);
dmamux_init(DMAMUX_RX_CH, DMAMUX_CH_TO_RXBASE);
这两段代码就是对dma通道进行绑定。我是用的是dma1的ch1和ch2,至于没什么没有中断,是因为懒,后期有时间改进吧,由于打开中断后,比如dma1的ch1和ch2它会进入这两通道的回调函数,要添加自己的回调函数,有兴趣的朋友可以参考串口中断的方式进行添加。

void uart_dmapoll_send(uint8_t *ch,uint16_t length)
{	
	UART_DMA_TX_CH->maddr = (uint32_t)ch;
	UART_DMA_TX_CH->dtcnt = length;
	usart_dma_transmitter_enable(PRINT_UART,TRUE);          
	dma_channel_enable(UART_DMA_TX_CH, TRUE);            
	while(dma_flag_get(DMA1_FDT1_FLAG)==RESET );        
	dma_flag_clear(DMA1_FDT1_FLAG);                     
	dma_channel_enable(UART_DMA_TX_CH, FALSE);           
	usart_dma_transmitter_enable(PRINT_UART,FALSE);         
}


void uart_dmapoll_read(uint8_t *ch,uint16_t length)
{	
	      
	dma_flag_clear(DMA1_FDT2_FLAG);                 
	dma_channel_enable(UART_DMA_RX_CH, FALSE);       
	usart_dma_receiver_enable(PRINT_UART,FALSE);        
	UART_DMA_RX_CH->maddr = (uint32_t)ch;
	UART_DMA_RX_CH->dtcnt = length;        
	usart_dma_receiver_enable(PRINT_UART,TRUE);         
	dma_channel_enable(UART_DMA_RX_CH, TRUE); 
	while(dma_flag_get(DMA1_FDT2_FLAG)==RESET ); 	
}

void uart_dma_poll_ping(void)
{
	char rx_buff[11];
	uart_dmapoll_read((uint8_t *)&rx_buff,11);
	uart_dmapoll_send((uint8_t *)&rx_buff,11);
}

这三个函数是实现了dma的发送接收与回显DMA1_FDT1_FLAGDMA1_FDT2_FLAG数据传输完成标志位,要使用dma_flag_clear()函数清除掉。
这里说明一下,uart_dma_poll_ping()发送接收时固定的11个字节,如果想要动态,要使用串口的空闲中断标志位进行实现。
测试结果
在这里插入图片描述

GitHub链接: github
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值