【应用笔记】TAE32芯片平台搭建VOFA+应用环境
1 应用功能介绍
在电机、电源等高速控制的场合内,传统的调试手段显然无法快速准确地反映采集与输出控制的关系,故引入 VOFA+ 调试工具。
图1.1 VOFA+软件
VOFA+ 本质是串口通讯,故只需芯片平台拥有串口即可。本方案在此基础上,为了避免通讯操作过多占用 CPU 资源,故采用了 Uart+DMA 传输方案,本文以 TAE32F5300 与 TAE32G5800 为演示平台。
2 使用步骤
代码构成如图 2.1 所示。
图2.1 VOFA+驱动库代码结构
Inc 的 vofa_conf.h 文件主要用于配置工程属性,如属于哪个芯片平台,以及固定使用的串口号与 DMA 通道号。vofa_core.h 文件内声明了传输数据的结构体,以及驱动文件提供的接口函数。
VOFA+ 驱动库的使用步骤如所示。
图2.2 VOFA+驱动库使用步骤
2.1 数据结构体
本文使用 VOFA+ 的 JustFloat 协议来传输浮点数据,协议规定的报文结构体如程序清单 2.1 所示。
程序清单2.1 传输数据结构体
路径:Inc/vofa_core.h
#include "vofa_conf.h"
typedef union {
float f[tx_data_num];
uint8_t c[tx_data_len];
}tx_function;
typedef struct {
tx_function data;
uint8_t tail[4];
} tx_data;
union 联合体型的 tx_function 存放浮点型数据,t_function 联合体成员分为 float 和 uint8_t,共用了变量空间,所以要发送的数据填进 f 数组后,后面串口操作 c 数据即可。然后再封装到 tx_data 结构体内,以 tail 为报文尾部,内容是固定字节。
2.2 库需要传入的参数
本小节讲 VOFA+ 库需要用传入哪些参数才能正常工作,配置文件如所示。
程序清单2.2 VOFA+库配置参数
路径:Inc/vofa_conf.h
#ifndef __VOFA_CONF_H__
#define __VOFA_CONF_H__
//#define TAE32G58XX
#define TAE32F53XX
//#define TAE32F56XX
#ifdef TAE32G58XX
#include "tae32g58xx_ll.h"
#define VOFA_UART UART1
#define VOFA_TX_DMA_CH DMA_CHANNEL_5
#define VOFA_BAUDRATE 115200
#else
#ifdef TAE32F53XX
#include "tae32f53xx_ll.h"
#define VOFA_UART UART0
#define VOFA_TX_DMA_CH DMA_CHANNEL_0
#define VOFA_BAUDRATE 115200
#else
// #include "tae32g58xx_ll.h"
// #define VOFA_UART UART2
// #define VOFA_DMA_CH DMA_CHANNEL_1
#endif
#endif
/*发送的数据:即想要在VOFA上观察的数据,可以是ADC采集的电压或电流的值*/
#define tx_data_num 3 //发送数据的个数
#define tx_data_len (tx_data_num)*4
#endif
需要填写的参数有:
1、选择平台宏,如 #define TAE32F53XX;
2、VOFA_UART,指定库占用的串口号;
3、VOFA_TX_DMA_CH,指定库占用的 DMA 通道号;
4、VOFA_BAUDRATE,指定串口通讯的波特率,其他停止位、奇偶校验位的配置需要进入到 Src 目录下的各个驱动文件内自行修改
5、tx_data_num,指定需要显示的波形通道数;
6、tx_data_len,此处不可修改。
2.3 接口函数使用方法
本方案提供的接口函数如下:
void VOFA_UartInit(void);
void VOFA_Uart_Tx(void);
使用步骤如图 2.2 所示,在初始化阶段调用 VOFA_UartInit(),然后填充数据,最后调用发送,需要注意,本方案使用 DMA 传输,即发送操作只告诉 DMA 模块从哪个地址取数据发送到 Tx 寄存器中,而无须 CPU 等待发送完成。所以两个 VOFA_Uart_Tx() 之间不可间隔太短。应用场合内,一般是做完一次环路计算发送一次,故本方案可满足计算后发送的需求。操作代码如所示。
程序清单2.3 接口函数使用方法
int main(void)
{
……
#include "vofa_core.h"
VOFA_UartInit();
extern tx_data vofa_tx_buf;
vofa_tx_buf.data.f[0] = 0;
vofa_tx_buf.data.f[1] = 0;
vofa_tx_buf.data.f[2] = 0;
while (1) {
vofa_tx_buf.data.f[0] += 1.1;
vofa_tx_buf.data.f[1] += 2.2;
vofa_tx_buf.data.f[2] += 3.5;
if(vofa_tx_buf.data.f[0] > 100)
vofa_tx_buf.data.f[0] = 0;
if(vofa_tx_buf.data.f[1] > 150)
vofa_tx_buf.data.f[1] = 0;
if(vofa_tx_buf.data.f[2] > 300)
vofa_tx_buf.data.f[2] = 0;
VOFA_Uart_Tx();
delay_ms(100);
}
}
3 软件使用
VOFA+ 的使用可参考网上资料,本处简单介绍。软件配置界面如下所示。选择 JustFloat 协议才可动态发送波形。串口参数配置根据实际使用配置。
图3.1 VOFA+软件配置界面
波形通道配置如图 3.2 所示。
图3.2 VOFA+波形显示配置界面
4 驱动库构成
本小节介绍驱动库的组成。
4.1 TAE32F53xx的初始化代码
初始化函数主要分为串口初始化与 DMA 初始化。串口初始化代码如程序清单 4.1 所示。
程序清单4.1 53xx串口初始化
路径:Src/tae32f53xx_uart.c
uartinitstruct.baudrate = VOFA_BAUDRATE; //波特率
uartinitstruct.dat_len = UART_DAT_LEN_8b; //8位数据长度
uartinitstruct.parity = UART_PARITY_NO; //无奇偶校验
uartinitstruct.stop_len = UART_STOP_LEN_1b; //1位停止位
uartinitstruct.rx_tl = UART_RX_AVL_TRI_LVL_1CHAR; //接收阈值为1
uartinitstruct.tx_tl = UART_TX_EMPTY_TRI_LVL_EMPTY; //发送阈值为空
LL_UART_Init(VOFA_UART, &uartinitstruct);
DMA 初始化代码如所示。
程序清单4.2 53xx DMA通道初始化
路径:Src/tae32f53xx_uart.c
DMA_UserCfgTypeDef dma_user_cfg;
memset((void *)&dma_user_cfg, 0x00, sizeof(dma_user_cfg));
dma_user_cfg.trans_type = DMA_TRANS_TYPE_M2P; //该通道方向为从内存到外设
dma_user_cfg.src_addr_mode = DMA_SRC_ADDR_MODE_INC; //通道源地址为增长模式
dma_user_cfg.dst_addr_mode = DMA_DST_ADDR_MODE_FIX; //通道目的地址为固定模式
dma_user_cfg.src_data_width = DMA_SRC_TRANS_WIDTH_8b; //通讯位宽为8bit
dma_user_cfg.dst_data_width = DMA_DST_TRANS_WIDTH_8b;
dma_user_cfg.src_hs_ifc = DMA_SRC_HANDSHAKE_IFC_MEMORY; //源的硬件索引为内存
if(VOFA_UART == UART0) { //根据Uart号选择DMA硬件索引
dma_user_cfg.dst_hs_ifc = DMA_DST_HANDSHAKE_IFC_UART0_TX;
} else if(VOFA_UART == UART1) {
dma_user_cfg.dst_hs_ifc = DMA_DST_HANDSHAKE_IFC_UART1_TX;
}
LL_DMA_ChReqSpecific(VOFA_TX_DMA_CH); //申请固定的DMA通道号
LL_DMA_Init(DMA, VOFA_TX_DMA_CH, &dma_user_cfg);
由于 Uart 发送是从内存里的 Buffer 写到外设寄存器中,所以传输方向类型为 M2P;内存的 Buffer 地址是从 [0] 到 [Max] 增长的,所以配置为 INC 增长模式。而外设寄存器地址是固定的,即所有发送的数据都往这个地址写入,故配置为 FIX 固定模式。
src_hs_ifc 与 dst_hs_ifc 为硬件索引值,是 DMA 通道寻找源地址、目的地址的索引,可以理解为触发源,像本方案中其实利用的是 dst_hs_ifc 指定串口的 Tx 事件,本质是利用串口的 TxFIFO empty(芯片内部设计)来驱动 DMA 传输。
特别注意,SDK 有一套 DMA 管理系统,所以采用 LL_DMA_ChReqSpecific() 来申请特殊的 DMA 通道。若用户不适用 SDK 包,需要避免通道复用问题。
4.2 TAE32F53xx的发送代码
TAE32F53xx 由于内存地址划分成几块,有不同 DMA 管理者,故发送操作需要根据变量空间做区分。
图4.1 TAE53xx的发送代码组成
代码最后使能通道即可自动发送。
4.3 TAE32G58xx的初始化代码
同理,TAE32G58xx 的串口初始化部分如程序清单 4.3 所示。
程序清单4.3 TAE32G58xx的串口初始化代码
路径:Src/ tae32g58xx_uart.c
uartinitstruct.baudrate = VOFA_BAUDRATE;
uartinitstruct.dat_len = UART_DAT_LEN_8b;
uartinitstruct.parity = UART_PARITY_NO;
uartinitstruct.stop_len = UART_STOP_LEN_1b;
uartinitstruct.ll_cfg = (struct __UART_LLCfgTypeDef *)&vofa_uart_ll_cfg;
LL_UART_Init(VOFA_UART, &uartinitstruct);
__LL_UART_Tx_En(VOFA_UART);
TAE32G58xx 的 DMA 初始化如所示。
程序清单4.4 TAE32G58xx的DMA初始化代码
路径:Src/ tae32g58xx_uart.c
dma_user_cfg.trans_type = DMA_TRANS_TYPE_M2P;
dma_user_cfg.trans_mode = DMA_TRANS_MODE_SINGLE;
dma_user_cfg.src_addr_mode = DMA_ADDR_MODE_INC;
dma_user_cfg.dst_addr_mode = DMA_ADDR_MODE_FIX;
dma_user_cfg.src_data_width = DMA_TRANS_WIDTH_8b;
dma_user_cfg.dst_data_width = DMA_TRANS_WIDTH_8b;
dma_user_cfg.src_hs_ifc = DMA_HANDSHAKE_IFC_MEMORY;
if(VOFA_UART == UART0) {
dma_user_cfg.dst_hs_ifc = DMA_HANDSHAKE_IFC_UART0_TX;
} else if(VOFA_UART == UART1) {
dma_user_cfg.dst_hs_ifc = DMA_HANDSHAKE_IFC_UART1_TX;
} else if(VOFA_UART == UART2) {
dma_user_cfg.dst_hs_ifc = DMA_HANDSHAKE_IFC_UART2_TX;
} else if(VOFA_UART == UART3) {
dma_user_cfg.dst_hs_ifc = DMA_HANDSHAKE_IFC_UART3_TX;
} else if(VOFA_UART == UART4) {
dma_user_cfg.dst_hs_ifc = DMA_HANDSHAKE_IFC_UART4_TX;
}
dma_user_cfg.end_arg = NULL;
dma_user_cfg.end_callback = NULL;
dma_user_cfg.err_arg = NULL;
dma_user_cfg.err_callback = NULL;
dma_user_cfg.half_arg = NULL;
dma_user_cfg.half_callback = NULL;
LL_DMA_ChReqSpecific(VOFA_TX_DMA_CH);
LL_DMA_Init(DMA, VOFA_TX_DMA_CH, &dma_user_cfg);
__LL_UART_TxDMA_En(VOFA_UART);
TAE32G58xx 的 DMA 配置项还需额外指定,每次 DMA 传输为单次传输。
4.4 TAE32G58xx的发送代码
发送代码如程序清单 4.5 所示。
程序清单4.5 TAE32G58xx的发送代码操作
路径:Src/ tae32g58xx_uart.c
void VOFA_Uart_Tx(void) {
if(1==__LL_DMA_IsTransCpltIntPnd(DMA, VOFA_TX_DMA_CH) ) {
__LL_DMA_TransCpltIntPnd_Clr(DMA, VOFA_TX_DMA_CH);
}
__LL_DMA_SrcAddr_Set(DMA, VOFA_TX_DMA_CH, (uint32_t)&vofa_tx_buf.data.c);
__LL_DMA_DstAddr_Set(DMA, VOFA_TX_DMA_CH, (uint32_t)&(VOFA_UART->TDR));
__LL_DMA_BlockTransCnt_Set(DMA, VOFA_TX_DMA_CH, tx_data_len+4);
__LL_DMA_Ch_En(DMA, VOFA_TX_DMA_CH);
}
对比起来,58xx 的 DMA 使用更加简便,地址空间做到统一管理。