阻塞式轮询
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_FLAG
和DMA1_FDT2_FLAG
数据传输完成标志位,要使用dma_flag_clear()
函数清除掉。
这里说明一下,uart_dma_poll_ping()
发送接收时固定的11个字节,如果想要动态,要使用串口的空闲中断标志位进行实现。
测试结果