杰发科技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触发+规则组

测试有异常,遗留问题

相关推荐
想放学的刺客1 天前
整理了120道单片机嵌入式面试题与答案,覆盖了硬件电路和C语言等核心领域。
c语言·c++·stm32·单片机·嵌入式硬件·mcu·51单片机
小野嵌入式2 天前
3小时精通嵌入式串口通信!从零玩转ESP32+Modbus+OTA(1)
c语言·单片机·嵌入式硬件·mcu·物联网
送外卖的工程师2 天前
STM32 驱动五线四相步进电机(28BYJ-48+ULN2003)教程
stm32·单片机·嵌入式硬件·mcu·物联网·51单片机·proteus
jyhappy1234 天前
深入理解 STM32 的 GPIO — 从零开始点亮第一颗 LED
c语言·stm32·单片机·嵌入式硬件·mcu
『往事』&白驹过隙;5 天前
在ARM开发中 volatile与const关键字的关键用途
c语言·arm开发·mcu·物联网·学习·iot
fox08157 天前
RTThread-Studio中,使用5.2.0版本默认配置生成工程,进行编译报警告的部分解决方法。
mcu·rt-thread·rtthread-studio
LeoZY_8 天前
CH347/339W开源项目:集SPI、I2C、JTAG、SWD、UART、GPIO多功能为一体(5)
stm32·mcu·fpga开发·开源·硬件架构·硬件工程
Megahertz669 天前
记一次失败的 FreeRTOS 移植
mcu·py32·py32f003
The️10 天前
Linux驱动开发之Makefile
linux·驱动开发·mcu·ubuntu
LeoZY_10 天前
CH347/339W开源项目:集SPI、I2C、JTAG、SWD、UART、GPIO多功能为一体(3)
stm32·单片机·嵌入式硬件·mcu·开源