简介:
S32K DMA 模块可以在发送数据时无需CPU搬运数据,释放CPU从而提高运行效率,S32 DS 中DMA 的配置方法也比较简单,只需要简单的配置DMA 通路即可,之前适配串口的时候我们使用的中断的方式,我们以串口配置为例来说明DMA模块的使用。
DMA 通路配置
在S32DS 中配置UART RX/TX的DMA 参数配置,对应配置如下:
对应的配置会生成如下的DMA 配置代码:
/*********************************************************************************************************************** * This file was generated by the S32 Configuration Tools. Any manual edits made to this file * will be overwritten if the respective S32 Configuration Tools is used to update this file. **********************************************************************************************************************/ /* clang-format off */ /* TEXT BELOW IS USED AS SETTING FOR TOOLS ************************************* !!GlobalInfo product: Peripherals v11.0 processor: S32K146 package_id: S32K146_LQFP144 mcu_data: s32sdk_s32k1xx_rtm_401 processor_version: 0.0.0 functionalGroups: - name: BOARD_InitPeripherals UUID: 2d0c0af7-dbe7-4f00-821a-837510d6ab4e called_from_default_init: true selectedCore: core0 * BE CAREFUL MODIFYING THIS COMMENT - IT IS YAML SETTINGS FOR TOOLS **********/ /* clang-format on */ /******************************************************************************* * Included files ******************************************************************************/ #include "peripherals_edma_config_1.h" /******************************************************************************* * edma_config_1 initialization code ******************************************************************************/ /* clang-format off */ /* TEXT BELOW IS USED AS SETTING FOR TOOLS ************************************* instance: - name: 'edma_config_1' - type: 'edma_config' - mode: 'general' - custom_name_enabled: 'false' - type_id: 'edma' - functional_group: 'BOARD_InitPeripherals' - peripheral: 'EDMA' - config_sets: - edma_driver: - settings_edmaUserCfg: - userStateStruct: 'dmaController_State' - userCfgName: 'dmaController_InitConfig' - readOnly: 'true' - chnArbitration: 'EDMA_ARBITRATION_FIXED_PRIORITY' - haltOnError: 'false' - settings_array_edmaChCfg: - array_chCfgStructs: - 0: - chStateStructName: 'dmaControllerChn0_State' - chConfigName: 'dmaControllerChn0_Config' - chType: 'edma_channel_config_t' - virtCh: '0' - chPrio: 'EDMA_CHN_DEFAULT_PRIORITY' - chReq: 'EDMA_REQ_LPUART1_RX' - chCallback: 'NULL' - chCallbackParam: 'NULL' - enableTrigger: 'false' - 1: - chStateStructName: 'dmaControllerChn1_State' - chConfigName: 'dmaControllerChn1_Config' - chType: 'edma_channel_config_t' - virtCh: '1' - chPrio: 'EDMA_CHN_DEFAULT_PRIORITY' - chReq: 'EDMA_REQ_LPUART1_TX' - chCallback: 'NULL' - chCallbackParam: 'NULL' - enableTrigger: 'false' * BE CAREFUL MODIFYING THIS COMMENT - IT IS YAML SETTINGS FOR TOOLS **********/ /* clang-format on */ /** * @page misra_violations MISRA-C:2012 violations * * @section [global] * Violates MISRA 2012 Advisory Rule 8.7, External variable could be made static. * The external variables will be used in other source files in application code. * */ edma_state_t dmaController_State; edma_chn_state_t dmaControllerChn0_State; edma_chn_state_t dmaControllerChn1_State; edma_chn_state_t * const edmaChnStateArray[] = { &dmaControllerChn0_State, &dmaControllerChn1_State, }; edma_channel_config_t dmaControllerChn0_Config = { .channelPriority = EDMA_CHN_DEFAULT_PRIORITY, .virtChnConfig = EDMA_CHN0_NUMBER, .source = EDMA_REQ_LPUART1_RX, .callback = NULL, .callbackParam = NULL, .enableTrigger = false, }; edma_channel_config_t dmaControllerChn1_Config = { .channelPriority = EDMA_CHN_DEFAULT_PRIORITY, .virtChnConfig = EDMA_CHN1_NUMBER, .source = EDMA_REQ_LPUART1_TX, .callback = NULL, .callbackParam = NULL, .enableTrigger = false, }; const edma_channel_config_t * const edmaChnConfigArray[] = { &dmaControllerChn0_Config, &dmaControllerChn1_Config, }; const edma_user_config_t dmaController_InitConfig = { .chnArbitration = EDMA_ARBITRATION_FIXED_PRIORITY, .haltOnError = false };
UART DMA参数配置
上面已经配置好了DMA 的传输通道,我们在UART的配置中选择使用DMA及通信的通路。
至此就完成UART 的DMA 配置,相对配置起来很简单。
UART 传输性能比较
我们之前的printf 输出使用的轮询非阻塞的方式传输,我们现在修改为DMA阻塞方式传输,我们添加如下测试代码来比较两种情况下连续输出10000次时CPU的使用率。
测试代码如下:
for(int i = 0 ; i < 10000;i++) printf("hello world.\r\n"); { extern unsigned int cpuusage(char argc,char ** argv); cpuusage(1,NULL); }
其中CPU 使用率的算法参照该文章(RA8使用perfcount计算freertos任务CPU使用率)在此就不额外赘述。
使用polling 方式时CPU 的使用率为81%
使用DMA 方式对应的使用率为28%,other 为中断等所占用的CPU使用,对应的使用率需要加上other 部分
从上面的数据可以看出DMA 相对polling的方式效率还是高很多,因为本地的DMA发送数据一次也是一字节发送本质上和中断的方式是没有区别的,要想进一步降低CPU使用率需使用DMA 一次多发送字节,降低中断次数来能相对中断发送进一步提高效率。
在此基础上我们继续修改代码,按照100字节为单位使用DMA来传输(本地debug 测试发现本地的环境__write每次按照一个字节喂数据来发送,使用最朴素的方式没有按照size 来填充buff ),这时CPU 的使用率明显下降,DMA比较适合大块数据传输的场景,会大大释放CPU的压力,从而提高效率。
size_t __write(int handle, const unsigned char *buffer, size_t size) { static uint8_t buff[100]; static uint8_t index; buff[index++] = buffer[0]; if(index < 99) return 1; index = 0; status_t ret; do{ ret = LPUART_DRV_SendDataBlocking(INST_LPUART_1,buff,100,500); } while( ret != STATUS_SUCCESS); return size; }