【雅特力AT-START-F435】USART发送、VOFA+上位机使用、串口空闲中断+DMA不定长接收
[复制链接]
本次测评中参考例程中USART,实现多通道采样及上位机波形显示。
参考文件目录如下:
AT32F435_437_Firmware_Library_V2.1.2\project\at_start_f435\examples\usart\transfer_by_dma_interrupt
USART的使用
功能简介
AT32F435/437有4个同步异步收发器(USART)、4个异步收发器(UART),支持IrDA SIR ENDEC传输编解码、多处理器通信模式、单线半双工通信模式和LIN主/从功能,其中串口1、2、3、6支持硬件的CTS和RTS信号管理、兼容ISO7816的智能卡模式和类似SPI通信模式。8个串口波特率高达9Mbit/s,且都可以使用DMA。
串口发送
使用轮询发送,官方提供的API中,下面的函数只能发送单个字节。
void usart_data_transmit(usart_type* usart_x, uint16_t data)
{
usart_x->dt = (data & 0x01FF);
}
下面是自己写的串口发送数组函数,发送单个字节后需要判断发送完成标志USART_TDC_FLAG,才能发送下一个函数
void usart_buf_trans(uint8_t* data_buf,uint32_t data_size)
{
//轮询发送
uint32_t i = 0;
for ( i = 0; i < data_size; i++)
{
usart_data_transmit(USART2,data_buf[i]);
while (RESET == usart_flag_get(USART2, USART_TDC_FLAG)) ;
}
}
使用VOFA打印float类型的数据
协议的参考文档为https://www.vofa.plus/
void USART_VOFA_JUSTFLOAT_FLOATBUF_TRANS(float *data, uint16_t data_size)
{
uint16_t var = 0;
uint8_t data_tmp[4] = { 0 };
uint8_t tail[4] = { 0x00, 0x00, 0x80, 0x7f };
for (var = 0; var < data_size; var++)
{
memcpy(data_tmp, (uint8_t *)&data[var], sizeof(data[var]));
usart_buf_trans(data_tmp,4);
}
usart_buf_trans(tail, 4);
}
效果如下:
串口空闲中断+DMA接收
串口接收一般有三种方式:(1)轮询接收;(2)中断接收;(3)串口空闲中断+DMA不定长接收。
第一种方式需要单片机不断循环读取串口的状态,如果读取的频率不够高,可能后漏掉一部分信息;第二种方式一般是定长接收,在发送、接收双方约定好数据帧的格式和长度的情况下较为使用;第三种方式一般是用于接收不定长的数据。方式1和方式2较为简单,下面记录一下串口空闲中断+DMA接收接收不定长数据。
①GPIO初始化
使用串口2做发送和接收,可以从数据手册中可以找到,IO和串口的对应关系是,PA2->USART2_TX,PA3->USART2_RX。Gpio的muxing function应该选择GPIO_MUX_7。
参考代码如下:
gpio_default_para_init(&gpio_init_struct);
/* configure the usart2 tx, rx 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 = GPIO_PINS_2 | GPIO_PINS_3;
gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
gpio_init(GPIOA, &gpio_init_struct);
gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE2, GPIO_MUX_7);
gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE3, GPIO_MUX_7);
②USART初始化
选择串口2,波特率选择115200(USB转TTL模块不支持单片机的最高9M波特率),8位数据,1位停止位,同时使能串口的接收和发送,使能串口的DMA接收和发送。打开串口的空闲中断。
/* configure usart2 param */
usart_init(USART2, 115200, USART_DATA_8BITS, USART_STOP_1_BIT);
usart_transmitter_enable(USART2, TRUE);
usart_receiver_enable(USART2, TRUE);
usart_dma_transmitter_enable(USART2, TRUE);
usart_dma_receiver_enable(USART2, TRUE);
usart_enable(USART2, TRUE);
//enable usart2 IDLE interrupt
usart_interrupt_enable(USART2,USART_IDLE_INT,TRUE);
nvic_irq_enable(USART2_IRQn, 1, 1);
③DMA初始化
我们可以从图中看出USART2的DMA请求来源是26和27,使用了DMA1.首先打开DMA1,初始化DMA,方向是从外设地址到内存,还有其余的一些初始化项(和STM32一样的,不再赘述)。最后打开DMA的接收完成中断。
dmamux_enable(DMA1, TRUE);
/* dma1 channel2 for usart2 rx configuration */
dma_reset(DMA1_CHANNEL2);
dma_default_para_init(&dma_init_struct);
dma_init_struct.buffer_size = USART2_RX_BUFFER_SIZE;
dma_init_struct.direction = DMA_DIR_PERIPHERAL_TO_MEMORY;
dma_init_struct.memory_base_addr = (uint32_t)usart2_rx_buffer;
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)&USART2->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(DMA1_CHANNEL2, &dma_init_struct);
/* enable transfer full data intterrupt */
dma_interrupt_enable(DMA1_CHANNEL2, DMA_FDT_INT, TRUE);
/* dma1 channel2 interrupt nvic init */
nvic_irq_enable(DMA1_Channel2_IRQn, 0, 0);
/* config flexible dma for usart2 rx */
dmamux_init(DMA1MUX_CHANNEL2, DMAMUX_DMAREQ_ID_USART2_RX);
/* usart2 tx begin dma transmitting */
dma_channel_enable(DMA1_CHANNEL1, TRUE);
/* usart2 rx begin dma receiving */
dma_channel_enable(DMA1_CHANNEL2, TRUE);
④中断服务函数
DMA可以直接把USART的数据搬运到指定内存的位置,但如何获取传输数据量的大小呢,可以通过下面的逻辑来实现
在at32f435_f437_int.c文件中找到串口空闲中断的服务函数,添加自己的用户函数,过程如下:①判断中断来源是串口空闲中断;②关闭DMA,防止修改DMA时DMA传输失败;③USART2_RX_BUFFER_SIZE是DMA传输的最大数量,dma_data_number_get(DMA1_CHANNEL2)可以获取DMA传输完成后、串口空闲中断发生时,DMA通道未传输的数据的个数,相减就可以计算出本次传输数据的个数;④重新打开DMA,并把DMA传输数据的个数重新设置为USART2_RX_BUFFER_SIZE。
/**
* [url=home.php?mod=space&uid=159083]@brief[/url] this function handles usart2 handler.
* @param none
* @retval none
*/
void USART2_IRQHandler(void)
{
if (SET == usart_flag_get(USART2, USART_IDLEF_FLAG))
{
usart_flag_clear(USART2, USART_IDLEF_FLAG);
dma_channel_enable(DMA1_CHANNEL2, FALSE);
usart_recv_status.recv_data_size = USART2_RX_BUFFER_SIZE - dma_data_number_get(DMA1_CHANNEL2);
usart_recv_status.recv_end_flag = SET;
usart_recv_data_proc();
//data process
//reopen the usart2_rx_dma_channel
dma_data_number_set(DMA1_CHANNEL2, USART2_RX_BUFFER_SIZE);
dma_channel_enable(DMA1_CHANNEL2, TRUE);
usart_recv_status_reset(&usart_recv_status);
}
}
|