杰发科技AC7840——PWM触发多路ADC采样

紧接上文

pwm可以做到触发adc采样,并且是我们想要的触发,接下来看看实际应用情况

0. demo板测试引脚

led:pc6 pc7

pwm:pa11

1. 规则组单通道单次转换

如果我是想在每个pwm的最高点触发一次adc采样

三次采样分别在三个pwm的中间点,可以使用mode1这种方式(实测无法使用,会一次全部触发)

2. adc/pwm/ctu的初始化顺序

这三个模块的初始化顺序会影响到adc第一次触发的采样点

在前面我们2k左右的pwm,看起来还行

当我们吧pwm改成20k的时候

我们把ctu放在最后,刚好卡在第一个pwm的中间点的后面,算是卡了个bug来确保第一次采样点是正确的

在初始化时序不同,pwm相同的情况下,会出现以下采样情况

adc-pwm-ctu顺序

pwm-ctu-adc顺序

4. 三个模块的配置代码

前面的代码都不是很完整,这里贴一下完整的代码

cpp 复制代码
/*!
 * @file adc_sample.c
 *
 * @brief This file provides ADC functions.
 */

/* ===============================  Includes  =============================== */
#include "adc_sample.h"
#include "adc_hw.h"
#include "ctu_drv.h"
#include "ctu_hw.h"
#include "gpio.h"

/* ===============================   Define   =============================== */
#define ADC_INDEX (0U)
#define CTU_INDEX (0U)

/* ===============================   Typedef  =============================== */

/* ===============================  Variables =============================== */
uint16_t g_ADC_value = 0;

/* =========================  Functions declaration  ======================== */

/* =========================  Functions definition  ========================= */

/*!
 * @brief   ADC中断回调函数
 *          ADC转换完成后,在中断里翻转LED电平状态,并读取ADC转换结果
 * @param   none
 * @return  none
 */
void ADC_Callback(adc_interrupt_info_t *info, void *parameter)
{
		  
		if (info->event & ADC_EVENT_EOC) /*! EOC标志位,指示ADC转换完成*/
    {
        LED4_TOGGLE;                                     /*! 翻转LED电平*/
        //ADC_DRV_GetSeqResult(ADC_INDEX, ADC_RSEQ_0, &g_ADC_value); /*! 获取ADC转换的数值 */
    }
}

/*!
 * @brief ADC数据打印.
 *
 * @param[in]  none
 *
 * @return none
 */
void ADC_PrintTest(void)
{
    /*! 打印ADC通道转换结果*/
    printf("ADC_value:   VR1 = 0x%x \r\n", g_ADC_value);
}

/*!
 * @brief 	CTU模块配置
 *          为ADC0的规则组硬件触发源选择为PWM_Init触发
 * @param 	none
 * @return	none
 */
void CTU_ADCTrig_Init(void)
{
    CTU_DRV_Init(CTU_INDEX); /*打开CTU模块的接口时钟*/

    /*! ADC0_REGULAR0硬件触发源选择PWM1_Init*/
    TRGMUX_DRV_SetTrigSourceForTargetModule(CTU_INDEX, TRGMUX_TRIG_SOURCE_PWM1_INIT_TRIG, TRGMUX_TARGET_MODULE_ADC0_REGULAR0);
    TRGMUX_DRV_SetLockForTargetModule(CTU_INDEX, TRGMUX_TARGET_MODULE_ADC0_REGULAR0); /*! 配置寄存器锁定,锁定后无法修改该寄存器中的触发源选择,直到下一次系统复位*/
}

/*!
 * @brief 配置ADC通道
 *
 * @param[in] instance: ADC instance number
 * @param[in] seq: adc sequence
 * @param[in] channel: adc channel
 * @return none
 */
void ADC_ConfigChannel(const uint32_t instance, const adc_sequence_t seq, adc_inputchannel_t channel)
{
    adc_chan_config_t adcChConfig;

    /*!
    ADC采样转换时间计算:
        FCLK 时钟源为SPLLDIV2_CLK,频率为60MHz
        ADCCLK = FCLK/clockDivide = 60/6 = 10MHz
        总转换时间 = (SPT+ resolution(12/10/8))×ADCCLK周期+5个FCLK = (5+12)/10+5/60 ≈ 1.78us

    注意:12bit分辨率最大转换率为1Msps,10bit分辨率最大转换率为1.2Msps,8bit分辨率最大转换率为1.4Msps
    */

    ADC_DRV_InitChanStruct(&adcChConfig);            /*! 结构体初始化为默认值*/
    adcChConfig.channel     = channel;               /*! 输入通道选择*/
    adcChConfig.spt         = ADC_SPT_CLK_5;         /*! 采样时间*/
    adcChConfig.interruptEn = true;                 /*! EOC中断使能*/
    ADC_DRV_ConfigChan(instance, seq, &adcChConfig); /*! 应用通道配置*/
}


/*!
 * @brief ADC模块初始化
 *        ADC规则组转换,使用硬件触发
 *
 * @param[in] none
 *
 * @return none
 */
void ADC_Init(void)
{
    adc_converter_config_t adcConfig;
    adc_chan_config_t adcChConfig;

    /*! ADC输入端口(电位器)配置*/
    GPIO_DRV_SetMuxModeSel(VR1_PORT, VR1_PIN, PORT_PIN_DISABLED); /*! 电位器VR1*/

    /*! ADC配置
    配置ADC为12位分辨率,工作模式为MODE1 规则组单通道单次转换。
    规则组外部触发,每触发一次进行一次ADC转换,每次转换规则组中的1个通道。
    */
    ADC_DRV_Init(ADC_INDEX);
    ADC_DRV_InitConverterStruct(&adcConfig);
    adcConfig.clockDivide                = ADC_CLK_DIVIDE_6;     /*! ADC时钟分频。ADC时钟源选择在时钟初始化里配置,此例程选择为SPLLDIV2_CLK,频率为60MHz*/
    adcConfig.resolution                 = ADC_RESOLUTION_12BIT; /*! ADC分辨率设置*/
    adcConfig.regularTrigger             = ADC_TRIGGER_EXTERNAL; /*! 规则组触发源类型选择*/
    adcConfig.injectTrigger              = ADC_TRIGGER_EXTERNAL; /*! 注入组触发源类型选择*/
    adcConfig.dmaEnable                  = false;                /*! DMA使能位*/
    adcConfig.voltageRef                 = ADC_VOLTAGEREF_VREF;  /*! ADC参考电压源选择,VREF/VDDA*/
    adcConfig.scanModeEn                 = true;                /*! 工作模式配置:扫描模式使能位,多通道时需要使能扫描模式,单个通道时不需要使能扫描*/
    adcConfig.continuousModeEn           = false;                /*! 工作模式配置:连续模式使能位,1:触发后可连续转换,0:触发一次只转换一次,转换完成后停止*/
    adcConfig.regularDiscontinuousModeEn = true;                /*! 工作模式配置:规则组不连续模式使能位*/
    adcConfig.injectDiscontinuousModeEn  = false;                /*! 工作模式配置:注入组不连续模式使能位*/
    adcConfig.injectAutoModeEn           = false;                /*! 工作模式配置:自动注入模式使能位*/
    adcConfig.intervalModeEn             = false;                 /*! 工作模式配置:间隔模式使能位*/
    adcConfig.regularDiscontinuousNum    = 0;                    /*! 规则组子组长度 (用于mode7)*/
    adcConfig.regularSequenceLength      = 1;                    /*! 规则组长度设置*/
    adcConfig.injectSequenceLength       = 0;                    /*! 注入组长度设置*/
    adcConfig.powerEn                    = true;                 /*! ADC上电*/
    ADC_DRV_ConfigConverter(ADC_INDEX, &adcConfig);              /*! 初始化ADC配置*/
		
			ADC_ConfigChannel(ADC_INDEX, ADC_RSEQ_0, VR1_ADC_CH);     /*! 注入组0,电位器VR2对应通道*/
//		ADC_SetInjectConversionChannel(ADC_INDEX, ADC_ISEQ_0, VR1_ADC_CH);
//		ADC_SetInjectEOCInterruptEnableFlag(ADC_INDEX, ADC_ISEQ_0, 1);

    ADC_DRV_InstallCallback(ADC_INDEX, &ADC_Callback, NULL); /*! 配置ADC中断回调函数*/
}

/* =================================   EOF  ================================= */
cpp 复制代码
/*!
 * @file pwm_sample.c
 *
 * @brief This file provides interface for PWM functions.
 */

/* ===============================  Includes  =============================== */
#include "pwm_sample.h"
#include "pwm_common.h"
#include "pwm_hw.h"
#include "pwm_input.h"
#include "pwm_output.h"
#include "gpio_drv.h"
#include "gpio_hw.h"
#include "string.h"
#include "gpio.h"
/* ===============================   Define   =============================== */
#define PWM1_INDEX (1U)

/* ===============================   Typedef  =============================== */

/* ===============================  Variables =============================== */

/* =========================  Functions declaration  ======================== */

/* =========================  Functions definition  ========================= */

/*!
 * @brief PWM1中断回调函数
 *
 * @param[in] none
 * @return none
 */
 unsigned char aaa = 0;
void PWM1_Callback(uint8_t instance, uint32_t status, void *userData)
{
    if ((status & PWM_INIT_CNTOF_Msk) != 0) /*! 溢出中断 */
    {
				//LED3_TOGGLE;
					aaa++;
				if(aaa==5)
				{
					//PWM_DRV_Deinit(PWM1_INDEX);
				}	
    }
}

void PWM1_CH_Callback(uint8_t instance, uint32_t status, void *userData)
{
	
		if (PWM_CH5SCR_CHIF_Msk) /*! 匹配中断 */
    {
			//LED3_TOGGLE;

    }
}

/*!
 * @brief PWM 模块初始.
 *        将PWM1_CH4和PWM1_CH5配置为独立通道输出PWM,向上计数模式,频率为1KHZ,CH4占空比为30%,CH5占空比为60%
 *        使能Init触发,用于ADC模块的硬件触发源
 * @param[in] none
 * @return none
 */
void PWM1_OutputIndependent_Init(void)
{
    pwm_independent_ch_config_t independentChConfig[1];
    pwm_modulation_config_t pwmConfig;
    pwm_config_t config;

    /*! GPIO Configuration for PWM1 */
    GPIO_DRV_SetMuxModeSel(PORTA, (gpio_channel_type_t)11U, PORT_MUX_ALT2); /*! PA11: PWM1_CH5 */

    /*! Config PWM Interface */
    memset(independentChConfig, 0U, sizeof(independentChConfig));
    memset(&pwmConfig, 0U, sizeof(pwm_modulation_config_t));
    memset(&config, 0U, sizeof(pwm_config_t));

    /*!
    边沿对齐PWM模式:
    周期=(MCVR-CNTIN+1)*PWM计数器时钟周期
    占空比=(CHnV-CNTIN+1)*PWM计数器时钟周期
    中心对齐PWM模式:
    周期=2*(MCVR-CNTIN)*PWM计数器时钟周期
    占空比=2*(CH(n)V-CNTIN)*PWM计数器时钟周期
    */
    /*! channel config*/
    independentChConfig[0].channel        = PWM_CH_5;                       /*! 独立输出通道选择,这里选择CH5*/
    independentChConfig[0].chValue        = 500-1;                        /*! 通道Channel值,决定占空比,输出占空比 = chValue/config.maxValue = 18000/(60000-1) = 30%*/
    independentChConfig[0].chDitherValue  = 0;                              /*! 通道抖动值,可使PWM信号的分辨率更精确*/
    independentChConfig[0].levelMode      = PWM_HIGH_TRUE;                  /*! 输出PWM高有效,可配置高有效或低有效 */
    independentChConfig[0].polarity       = PWM_OUTPUT_POLARITY_ACTIVE_HIGH; /*! 通道输出极性配置为高有效,PWM mask后输出为低电平 */
    independentChConfig[0].interruptEn    = false;                           /*! PWM通道匹配中断使能位*/
    independentChConfig[0].initLevel      = PWM_LOW_LEVEL;                 /*! 通道初始电平输出配置,该配置受initChOutputEn 控制,决定PWM计数器未工作前PWM口的输出电平*/
    independentChConfig[0].matchTriggerEn = false;                           /*! 通道匹配触发使能位,在通道值匹配时可产生触发信号用于其他模块的触发信号 */
    independentChConfig[0].chEventDmaEn   = false;                          /*! 通道的DMA请求使能位*/

    /*! modulation mode config*/
    pwmConfig.countMode             = PWM_UP_DOWN_COUNT;        /*! PWM计数器模式, PWM_UP_COUNT--向上计数模式,PWM_UP_DOWN_COUNT--向上向下计数模式*/
    pwmConfig.independentChannelNum = 1;                   /*! 独立通道数目 */
    pwmConfig.combineChannelNum     = 0;                   /*! 组合通道对数*/
    pwmConfig.triggerRatio          = 0;                   /*! 起始值/最大值/匹配触发的比率,范围为0~7,0是每1个PWM周期产生一次触发,1是每2个PWM周期产生一次触发,以此类推 */
    pwmConfig.independentChConfig   = independentChConfig; /*! 独立通道配置赋值*/
    pwmConfig.combineChConfig       = NULL;                /*! 组合通道配置赋值*/
    pwmConfig.initChOutputEn        = true;                /*! 初始化通道输出使能位,使能后独立PWM模式的initLevel和组合PWM模式的ch1stInitLevel和ch2ndPolarity配置才会生效*/
    pwmConfig.initTriggerEn         = true;                /*! PWM起始值触发使能位*/
    pwmConfig.maxTriggerEn          = true;               	/*! 最大值触发使能位 */
    pwmConfig.centerAlignDutyType   = PWM_DUTY_MODE_0;     /*! 中央对齐的PWM占空比模式 */

    /*!
    向上计数模式下,频率计算为:
    freq = pwm_clk/(maxValue - initValue +1))
    60000000/(60000-1+1) = 1KHz
    */
    config.mode               = PWM_MODE_MODULATION;   /*! PWM模式配置,PWM_MODE_MODULATION--PWM调制模式 */
    config.initModeStruct     = &pwmConfig;            /*! 不同模式相应初始化配置结构体赋值 */
    config.clkSource          = PWM_CLK_SOURCE_SYSTEM; /*! PWM时钟源选择 */
    config.clkPsc             = 1;                     /*! 时钟分频 */
    config.initValue          = 0;                     /*! 初始计数值 */
    config.maxValue           = (1500 - 1);           /*! 最大计数值,决定输出波形频率 */
    config.modDitherValue     = 0;                     /*! 抖动值 */
    config.overflowEventEn    = true;                  /*! 向上溢出事件使能位 */
    config.underflowEventEn   = false;                 /*! 向下溢出事件使能位 */
    config.overflowInterrupEn = true;                  /*! 溢出中断使能位*/
    config.overflowDmaEn      = false;                 /*! 溢出事件的DMA请求使能位 */
    config.dmaLghEn           = false;                 /*! DMA传输长度使能位 */
    config.dmaLgh             = 0;                     /*! 配置一次PWM DMA req请求清除的DMA传输长度(0~31),*/
    config.cntOverflowFreq    = 0;                     /*! 溢出中断产生的频率与计数器频率的关系,0表示每次计数器溢出都产生中断,1表示间隔1次 */
    config.interruptEn        = true;                  /*! PWM总中断使能位 */
    config.overflowCallback   = PWM1_Callback;         /*! 溢出中断回调函数 */
    config.channelCallback    = PWM1_CH_Callback;      /*! 通道中断回调函数 */

		

    PWM_DRV_Init(PWM1_INDEX, &config);
		
		PWM_SetChannelMatchDir(PWM1,PWM_CH_5,PWM_MATCH_DIR_DOWN);
}

/* =================================   EOF  ================================= */
cpp 复制代码
#include "debugout_ac7840x.h"
#include "pwm_sample.h"
#include "clock_config.h"
#include "gpio.h"
#include "adc_sample.h"
#include "osif.h"
/*!
 *例程说明:
 *1、使用PWM硬件触发ADC采集电位器电压值
 */

/*!
 * @brief 时钟配置函数.
 *
 * @param[in] none
 *
 * @return none
 */
void SystemClock_Config(void)
{
    CKGEN_SYS_Init(g_clockManConfigsArr, CLOCK_MANAGER_CONFIG_CNT,
                   g_clockManCallbacksArr, CLOCK_MANAGER_CALLBACK_CNT);
    CKGEN_DRV_UpdateConfiguration(0, CLOCK_MANAGER_POLICY_AGREEMENT);
}

/*!
 * @brief main
 *
 * @param[in]  none
 *
 * @return 0: success, other: error value
 */
int main(void)
{
    SystemClock_Config(); /*! 时钟初始化*/
    InitDebug();          /*! 初始化串口打印*/
    printf("ADC External signal Trigger Test\r\n");

		
		
		
		
		ADC_Init();                    /*! AD初始化*/
    PWM1_OutputIndependent_Init(); /*! PWM配置,使能Init触发*/
		CTU_ADCTrig_Init();            /*! CTU配置ADC0规则组的硬件触发源为PWM1_Init*/
    
    
    GPIO_LedInit();                /*! LED初始化*/

    while (1)
    {
        OSIF_TimeDelay(500); /*! tick延时500ms*/

        //ADC_PrintTest(); /*! 打印AD数据 */
        //RGB_Toggle();    /*! RGB彩灯翻转,指示程序运行*/
    }
}

5. 增加一路adc

注入组测试,adc配置和ctu配置如下

官方接口使用时候,注入组会丢中断(已反馈,等修复),于是直接使用寄存器来读取

按照正常的流程初始化adc和ctu还有pwm

波形如下

初始化时候会有两次adc值,这个可以在计算时候去掉,后续的adc中断都在pwm的高电平有效期之内

2个中断之间的间隔大概600ns左右,就是一个io翻转的时间

6. 向上计数+init触发+注入组

以上波形看着还是有点奇怪,采样不是在中间采样,像是开结束时候开始采样,如果有多个通道需要采样,就会有部分在低电平时候开始采样。

因此调整一下触发方式和pwm的产生方式

pwm改成向上计数

adc改成init触发

adc配置如下

至少从视觉上能看到是在中间位置了

7. 向上计数+init触发+规则组

测试有异常,遗留问题

相关推荐
硅基手札10 小时前
【串口技术系列文档 07】UART控制器寄存器详解
驱动开发·stm32·单片机·嵌入式硬件·mcu·计算机外设
硅基手札10 小时前
【串口技术系列文档 09】FIFO管理与流控
驱动开发·stm32·单片机·嵌入式硬件·mcu·计算机外设
硅基手札10 小时前
【串口技术系列文档 08】中断驱动vs轮询vs DMA
驱动开发·stm32·单片机·嵌入式硬件·mcu·计算机外设
硅基手札1 天前
【串口技术系列文档 04】UART协议深度解析
驱动开发·stm32·单片机·嵌入式硬件·mcu·计算机外设
意法半导体STM322 天前
【官方原创】LAT1724 STM32CubeIDE for VSCode 修改链接文件中的起始地址无效?
vscode·stm32·单片机·嵌入式硬件·mcu·stm32cube
蓝天居士2 天前
PY32F系列MCU在OTA时App区概率性跑不起来的根因分析(3)
mcu·嵌入式
国科安芯4 天前
航天器健康管理系统中嵌入式控制器的多源状态监测与故障诊断能力评估
mcu·健康管理·抗辐射·空间信息·航天器
Brilliantwxx4 天前
【STM32】基础定时器 TIM 详解:从极简原理到寄存器全解析
stm32·单片机·嵌入式硬件·mcu
小刘max4 天前
从半导体视角理解 NVM 与 Flash:存储单元、阵列架构与应用差异
mcu
蓝天居士5 天前
PY32F系列MCU在OTA时App区概率性跑不起来的根因分析(2)
mcu·嵌入式