This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

[参考译文] 编译器/F28M35H52C:同步ADC转换的最佳方法

Guru**** 2587345 points


请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/microcontrollers/c2000-microcontrollers-group/c2000/f/c2000-microcontrollers-forum/654169/compiler-f28m35h52c-best-approach-to-simultaneous-adc-conversions

部件号:F28M35H52C

工具/软件:TI C/C++编译器

我有一个基于ADC_SoC示例运行的C28程序,该示例使用ePWM,其代码如下所示,但目前它仅运行ADC1

void main (void)
{
//步骤1。 初始化系统控制:
// PLL,看门狗,启用外设时钟
//此示例功能可在F28M35x_sysctrl.c文件中找到。
InitSysCtrl();

#ifdef _flash
//将时间关键代码和Flash设置代码复制到RAM
//这包括以下函数:InitFlash();
//
链接器创建RamfuncsLoadStart,RamfuncsLoadSize和RamfuncsRunStart //符号。 请参阅设备.cmd文件。
memcpy (&RamfuncsRunStart,&RamfuncsLoadStart,(size_t)&RamfuncsLoadSize);

//调用闪存初始化以设置闪存等待//
此函数必须驻留在RAM中
InitFlash();
#endif

//步骤2。 初始化GPIO:
//初始化GPIO
InitGpio();//将GPIO设置为其默认状态。
EALLOW;
GpioG1CtrlRegs.GPADIR.bit.GPIO29 = 1;//将PE5_GPIO29设置为输出TM -这适用于我添加到PCB的直接接线LED
EDIS;

//步骤3。 清除所有中断并初始化PIE矢量表:
//禁用CPU中断
dint;

//将PIE控制寄存器初始化为其默认状态。
//默认状态是禁用所有PIE中断,
并清除标志//。
//此函数位于F28M35x_PIECTRL.c文件中。
InitPieCtrl();//

禁用CPU中断并清除所有CPU中断标志:
IER = 0x0000;
IFR = 0x0000;

//使用指向shell Interrupt
//服务例程(ISR)的指针初始化PIE矢量表。
//这将填充整个表,即使在此
示例中未使用中断//。 这对于调试非常有用。
// shell ISR例程位于F28M35x_DefaultIsr.C.中
//此函数位于F28M35x_PieVect.C.中
本

例中使用的InitPieVectorTable();//中断被重新映射到此
文件中找到的// ISR函数。
EALLOW;//这是写入EALLOW保护寄存器所必需的
PieVectorTable.ADCINT1 =&ADC1_ISR; // ADC_SOC_c28.c中的TM指向ADC转换后运行的本地ISR代码
EDIS;//这是禁用写入EALLOW保护寄存器的必需

项//步骤4。 初始化设备外围设备。 此功能可以是
// 在F28M35x_CpuTimers.c
//# InitCpuTimers();中找到 // TM初始化CPU计时器(不使用已禁用的计时器)
InitAdc1(); // TM初始化ADC1 (当我们到达该位置时需要添加ADC2)

//在PIE中启用ADCINT1
PieCtrlRegs.PIEIER1.bit.INTx1 =1;//在饼图中启用INT 1.1
IER |= M_INT1; //启用CPU中断1
EINT; //启用全局中断INTM
ERTM; //启用全局实时中断DBGM

LoopCount = 0; // TM不确定我们是否需要此功能
ConversionCount = 0;// TM不确定我们是否需要此


//配置ADC
EALLOW;
Adc1Regs.ADCCTL2.bit.ADCNONOVERLAP = 1; //启用非重叠模式,即
//转换和未来
//采样事件不重叠
Adc1Regs.ADCCTL1.bit.INTPULSEPOS = 1; // ADCINT1在AdcResults锁定后跳闸

Adc1Regs.INTSEL1N2.bit.INT1E = 1; //已启用ADCINT1
Adc1Regs.INTSEL1N2.bit.INT1CONT = 0; //禁用ADCINT1连续模式

Adc1Regs.INTSEL1N2.bit.INT1SEL = 0; //设置EOC0以触发ADCINT1

Adc1Regs.ADCSOC0CTL.bit.CHSEL = 2; //将SOC0通道选择设置为ADC1A2 ### Current SS

Adc1Regs.ADCSOC1CTL.bit.CHSEL = 3; //将SOC1通道选择设置为ADC1A3 ###故障电流

AnalogSysctrlRegs.TRIG1sel.all = 5; //将EPWM1SOCA分配给ADC模块的ADC触发器1
Adc1Regs.ADCSOC0CTL.bit.TRIGSEL = 5; //将SOC0启动触发器设置为ADC的ADC触发器1 (EPWM1 SOCA)
Adc1Regs.ADCSOC1CTL.bit.TRIGSEL = 5; //将SOC1启动触发器设置为ADC的ADC触发器1 (EPWM1 SOCA)

Adc1Regs.ADCSOC0CTL.bit.ACQPS = 6; //将SOC0 S/H窗口设置为7个ADC时钟周期,(6 ACQPS +1)

ADc1Regs.ADCSOC1CTL.bit.ACQPS = 6; //将SOC1 S/H窗口设置为7个ADC时钟周期,(6 ACQPS +1)

EDIS;

////假定ePWM1时钟已在InitSysCtrl()中启用;

//为ADC SOC1设置事件触发器(SOCA)
EPwm1Regs.ETSEL.bit.SOCAEN = 1; //在组上启用SOC
EPwm1Regs.ETSEL.bit.SOCASEL = ET_CTRU_CMPA;//从CMPA中选择SOC ON
//增加
EPwm1Regs.ETPS.bit.SOCAPRD = 3; //每隔三次生成一次脉冲
//事件
//时基寄存器
EPwm1Regs.TBPRD =周期; //设置定时器周期,PWM
//频率= 1 /周期
EPwm1Regs.TBPHS.ALL = 0; //时基相位寄存器
EPwm1Regs.TBCTR = 0; //时基计数器寄存器
EPwm1Regs.TBCTL.bit.PRDLD = TB_immediate;//设置即时负载
EPwm1Regs.TBCTL.bit.CTRMODE = TB_COUNT_UP;//计数模式:用于
//非对称PWM
EPwm1Regs.TBCTL.bit.PHSEN = TB_DISABLE; //禁用相位加载
EPwm1Regs.TBCTL.bit.SYNCOSEL = TB_SYNC_DISABLE;
EPwm1Regs.TBCTL.bit.HSPCLKDIV = TB_DIV1;
EPwm1Regs.TBCTL.bit.CLKDIV = TB_DIV1;

//将阴影寄存器加载设置为零

EPwm1Regs.CMPCTL.bit.SHDWAMODE = CC_SHAME;
EPwm1Regs.CMPCTL.bit.SHDWBMODE = CC_SHADO;
EPwm1Regs.CMPCTL.bit.LOADAMODE = CC_CTR_ZERO;//在CTR上加载=零
EPwm1Regs.CMPCTL.bit.LOADBMODE = CC_CTR_ZERO;//在CTR上加载=零

//设置比较值
EPwm1Regs.CMPA.Half.CMPA =工作周期A; //初始设置占空比50 %
EPwm1Regs.CMBB =工作周期B; //初始设置占空比50 %

//设置操作
EPwm1Regs.AQCTLA.bit.ZRO = AQ_SET; //将PWM2A设置为零
EPwm1Regs.AQCTLA.bit.CAU = AQ_CLEAR; //在事件A上清除PWM2A
//计数

EPwm1Regs.AQCTLB.bit.ZRO = AQ_CLEAR; //将PWM2B设置为零
EPwm1Regs.AQCTLB.bit.CBU = AQ_SET; //清除事件B上的PWM2B,向上
//计数

//步骤6。 空闲循环。 只需坐下来永远循环(可选):
用于(;;);
} //主

端__interrupt void ADC1_ISR(void)
{
// TM我相信这些是在进行ADC转换之后发生的

当我们进入ADC ISR时,GpioG1DataRegs.GPASET.bit.GPIO29=1;// LED亮起

myVoltsArray[myIndex]=来自ADC结果0的Adc1Result.ADCRESULT0;// TM设置电压数组元素
myAmpsArray[myIndex]=来自ADC结果1的Adc1Result.ADCRESULT1;// TM温度设置安培数组元素
myIndex++;
如果(myIndex > 10){// TM,则10为1.2607万,为测试而缩小
myIndex =0;
}
//如果记录了10个转换,请重新开始
IF (ConversionCount == 9)
{
ConversionCount = 0;
}
Else ConversionCount++;

当我们退出ADC ISR时,GpioG1DataRegs.GPACLEAR.bit.GPIO29=1;// LED熄灭

adc1Regs.ADCINTFLGCLL.bit.ADCINT1 = 1;//清除ADCINT1标志重新初始化
//用于下一个SOC
PIECtrlRegs.PIEACK/ALL = PIEACK_Group1;//确认中断到PIE

返回;
}

我希望每秒对ADC1INA2和ADC2INA2 2.16万 执行同时转换,然后获取这些结果并将其存储在我的循环数组缓冲区中。  我在参考手册(spruh22h.pdf)中阅读了有关同时采样模式的内容,但我不清楚结果是如何返回的-我想我可以将ADC1INA2和ADC2INA2设置为由ePWM信号触发,但我是否需要2个ISR来获取结果?

如果ADC1INA2值超时,我需要替换ADC1INA3中的读数(读取的相同源的灵敏度比敏感程度低10倍),这一要求会使问题更加复杂。 所以我真正想要的是,如果读数高于(然后 低于)设置值,则更改ADC1INAx值的来源。  解决此问题的另一种方法可能是始终获取 ADC1INA2和ADC2INA2读数,然后立即跟进ADC1INA3读数(将在< 1个美国后出现,但没关系); 在这种方法中,我将测试ADC1INA2值,如果该值已达到最大值,我只需使用ADC1INA3值除以10,并将其存储在结果数组中。  

请注意,我正在获取12位结果并将其存储在16位寄存器中,因此如果我要存储第二个结果x10,它仍将适合于16位结果阵列-通过这种方式,我可以捕获过流的值。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    哎呀,发布太早了,我忘记问的问题是什么是实现我的要求的最佳/推荐方法?

    提前感谢。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    Ted,

    我认为您将这些与同一触发源绑定的方法是正确的。  实际上,您也可以将ADCIN1A3与此相同的触发源绑定;在默认循环方案下,ADCIN1A2将始终采样,然后ADCIN1A3将跟随。  唯一的例外是,如果您自己对ADCIN1A2进行采样,这将更改循环指针的状态,但从上面看,我认为您不会这样做。

    至于ISR,由于模拟子系统是从同一时钟派生的,并且您是从同一PWM触发器触发ADC1和ADC2,因此您可以依靠转换的任一端来告诉您这两个都是完整的。  事实上,如果您想关闭ADCIN1A3的ISR,您可以读取全部3个结果(ADCIN1A2,ADCIN2A2,ADCIN1A3),并知道它们是最新的。  如果您无法吸收控制循环中ADCIN1A3的采样时间,您可以触发任一A2, 根据您的ISR代码,ADCIN1A3可能在某个时间点完成,而不需要另一个ISR (该ISR会消耗一些周期,因为它实际上与任何正在运行的代码不连续)。

    最后,即使它不适用于此处,我也想提及每个ADC中的同步模式位,以确保完整性。  此模式将同时对给定ADC中的AX和Bx补体进行采样,唯一的缺点是按顺序转换样本。  您使用不同ADC模块的方法速度更快,因为它为您提供的两种结果的总体时间更短,在这种情况下无需使用此位或功能。

    此致,

    马特

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    Matt,

    感谢您的建议。 我上面的代码取自SOC_ADC示例,因此我仍不清楚某些方面;如果可以...

    我想你说我可以为我的3个值设置3个SOC,SOC0,SOC1和SOC2,一个在ADC1上,另一个在ADC2上, 然后,将它们全部设置为从ePWM触发,但只有SOC2是触发ISR的结果,那么所有3个结果都将是最新和有效的,因为它们将按SOC#顺序完成,对吗?

    我目前只尝试测量ADC1A2和ADC1A3,它们使用单独的SOC (0+1),但看起来只有SOC1被此代码设置为触发

    //为ADC SOC1设置事件触发器(SOCA)
    EPwm1Regs.ETSEL.bit.SOCAEN = 1; //在组上启用SOC
    EPwm1Regs.ETSEL.bit.SOCASEL = ET_CTRU_CMPA;//从CMPA中选择SOC ON
    //增加 

    是这样吗? 如果是,我还需要触发SOC0吗?  如果这是通过循环操作完成的,请解释。

    上面写的代码是否执行循环转换?如果是,哪些语句执行循环转换?结果是否会记录 Adc1Result.ADCRESULT0和 Adc1Result.ADCRESULT1,它们始终与SOC编号的配置方式相关?

    查看GPIO29上的LED驱动器时,我看到750 NS级脉冲,2.16万 次/秒。  我想说的是,就在我的代码的顶部

    #define C28_FREQ 150 //CPU频率(以MHz为单位
    
    )#define Duty_cycle_A 1106#define
    Duty_cycle_B 1106#define
    period 2212. //这些似乎是2倍,但2212的周期为我提供了22.6 kHz采样 

    但我不明白这些设备属于什么设备,如果它们是我们,那么44.24 Us将是2.26万 Hz的正确时期,因此这让我感到困惑(或者ISR被叫了两次或其他事情)?  请您澄清一下吗?  (我很确定m3的姊妹代码设置为以150 MHz运行C28)

    您能否概述我需要执行哪些额外步骤来添加ADC2?

    提前感谢。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    Ted,

    PWM:

    PWM周期和占空比的数字应仅以计数器值表示,因此它将基于ePWM时钟频率。  如果PWM为150MHz,我们应该会看到67.8kHz (2212 * 1/150MHz)。  让我们检查M3设置代码,确保我们处于C28x = 150MHz/ M3 = 75MHz。  M3 = 100MHz有alt最大时钟,但这使得C28x = 100MHz (因为它们来自相同的时钟源)。  用数字除以2,我得到50MHz,似乎与您的输出切换相匹配。

    ADC:

    下面是我要添加/更改的代码修改,以便按照我所述对信道进行采样/触发和ISR。  然后,您可以将ADC2的读数添加到ISR,如下所示:

    myOtherVoltArray[myIndex]= Adc2Result.ADCRESULT0

    设置代码

    //修改的ADC代码
    
    // ADC1
    
    EALLOW;
    
    Adc1Regs.ADCCTL2.bit.ADCNONOVERLAP = 1; //启用非重叠模式,即
    
    //转换和未来
    
    //采样事件不重叠
    
    Adc1Regs.ADCCTL1.bit.INTPULSEPOS = 1; // ADCINT1在AdcResults锁定后跳闸
    
    
    
    Adc1Regs.INTSEL1N2.bit.INT1E = 1; //已启用ADCINT1
    
    Adc1Regs.INTSEL1N2.bit.INT1CONT = 0; //禁用ADCINT1连续模式
    
    
    //将其更改为使用EOC1,ADC1A3来触发ISR
    Adc1Regs.INTSEL1N2.bit.INT1SEL = 1; //设置EOC1以触发ADCINT1
    
    
    
    Adc1Regs.ADCSOC0CTL.bit.CHSEL = 2; //将SOC0通道选择设置为ADC1A2 ### Current SS
    
    
    
    Adc1Regs.ADCSOC1CTL.bit.CHSEL = 3; //将SOC1通道选择设置为ADC1A3 ###故障电流
    
    
    
    AnalogSysctrlRegs.TRIG1sel.all = 5; //将EPWM1SOCA分配给ADC模块的ADC触发器1
    
    Adc1Regs.ADCSOC0CTL.bit.TRIGSEL = 5; //将SOC0启动触发器设置为ADC的ADC触发器1 (EPWM1 SOCA)
    
    Adc1Regs.ADCSOC1CTL.bit.TRIGSEL = 5; //将SOC1启动触发器设置为ADC的ADC触发器1 (EPWM1 SOCA)
    
    
    
    Adc1Regs.ADCSOC0CTL.bit.ACQPS = 6; //将SOC0 S/H窗口设置为7个ADC时钟周期,(6 ACQPS +1)
    
    
    
    ADc1Regs.ADCSOC1CTL.bit.ACQPS = 6; //将SOC1 S/H窗口设置为7个ADC时钟周期,(6 ACQPS +1)
    
    
    	
    	// ADC2
    
    
    Adc2Regs.ADCCTL2.bit.ADCNONOVERLAP = 1; //启用非重叠模式,即
    
    //转换和未来
    
    //采样事件不重叠
    
    Adc2Regs.ADCCTL1.bit.INTPULSEPOS = 1; // ADCINT1在AdcResults锁定后跳闸
    
    
    
    
    
    Adc2Regs.ADCSOC0CTL.bit.CHSEL = 2; //将SOC0通道选择设置为ADC2A2
    
    
    
    Adc2Regs.ADCSOC0CTL.bit.TRIGSEL = 5; //将SOC0启动触发器设置为ADC的ADC触发器1 (EPWM1 SOCA)
    
    
    
    
    
    Adc2Regs.ADCSOC0CTL.bit.ACQPS = 6; //将SOC0 S/H窗口设置为7个ADC时钟周期,(6 ACQPS +1)
    
    
    	
    
    EDIS; 

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    Matt,

    非常感谢您为我做的这件事。  我今天没有机会尝试,但我会在本周结束之前尝试。  我的M3的设置代码如下所示:

    // TM用户变量
    #pragma data_section(myVoltsArray,".vArray");
    unsigned short myVoltsArray[0x3140);//定义1.2608万 x 16位unsigned整数#pragma
    
    data_section(myAmpsArray,".iArray")的数组;
    Msshort myAmpsArray[0x3140;//定义
    
    
    unsigned mygrets64]的短整型数组;{16位unsigned 1.2608万
    
    
    
    
    
    
    INT I = 0;
    
    //允许写入受保护的寄存器。
    HWREG (sysctl_MWRALLOW)= 0xA5A5A5A5;
    
    //设置PLL,M3以75MHz运行和C28以150MHz运行
    SysCltlClockConfigSet(sysctl_use_PLL |(sysctl_SPLLIMULT_M & 0xF)|
    sysctl_SYSDIV_1 | sysctl_M3SSDIV_2 |
    sysctl_XCLKDIV_4);
    
    // TM将共享RAM的S0分配到S7 (全部)供来自RAM_Management controlSUITE的c28 TM使用示例
    // c28如何使用这些内存部分的详细信息在c28链接器文件中定义。(28M35H52C1_RAM_lnk.cmd)
    // TM需要#include "driverh"
    RAMMReqSharedMemAccess(SF_access | S2_access | S3_access | S4_access | S5_access | S6_access | S7_access),SX_C28MASTER);
    
    #ifdef _flash
    //将时间关键代码和闪存设置代码复制到RAM
    //这包括以下函数:InitFlash();
    // RamfuncsStart
    ,LoadfunsStart,RamsStart符号。 请参阅设备.cmd文件。
    memcpy (&RamfuncsRunStart,&RamfuncsLoadStart,(size_t)&RamfuncsLoadSize);
    
    //调用闪存初始化以设置闪存等待//
    此函数必须驻留在RAM中
    FlashInit();
    #endif
    
    
    //启用处理器中断。
    IntMasterEnable();
    
    //启用所有GPIO
    PinoutSet();// TM这将运行与DSTR PCB相对应的引脚输出设置
    //设置位于set_pinout _f28m35x.c和set_pinout_f28m35x.h中(由TM修改以匹配DSTR PCB)
    
    //启用UART外设
    SysCtlPeripheralEnable(sysctl_Periph_UART0);
    SysCtlPeripheralEnable(sysctl_Periph_GPIOA);
    SysCtlPeripheralEnable(LED_0_Periph);// LED0位于从Blinky_dc_m3.c示例中提取的PE5上
    
    //为11.52万 ,8-N-1操作配置UART。
    UARTConfigSetExpClk (UART0_BASE,SysClockGet (system_clock_speed),11.52万,
    (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE |
    UART_CONFIG_PAR_NONE));
    
    //启用UART中断。
    IntRegister (INT_UART0,UARTIntHandler);
    IntEnable (INT_UART0);
    UARTIntEnable (UART0_BASE,UART_INT_RX | UART_INT_RT); 

    我会报告并告诉你我是怎么做的。

    TED

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    Matt,

    我使用了您的代码,现在在ADC2A2和ADC1A2上进行转换,我的ISR仍被调用为2.16万 Hz。  尚不清楚的是ADC1_ISR的情况;有两行,一行用于ADC1,一行用于ADC2,表示acd1_ISR在ADC结果锁定后触发...

    Adc1Regs.ADCCTL1.bit.INTPULSEPOS = 1; // ADCINT1在AdcResults锁定后跳闸
    Adc2Regs.ADCCTL1.bit.INTPULSEPOS = 1; // ADCINT1在AdcResults锁定后跳闸 

    但是ADC1有两个SOC,0和1,而ADC2只有一个,SOC 0 (ADC1和ADC2有SOC0是正确的吗?) 因此ADC1转换将需要两倍的时间并导致2次触发,而ADC2转换只发生一次并导致1次触发? 或者,评论“ADCInt1 TRIPS after AdcResults LATCH”是否会误导我,因为只有一行实际导致ADC1_ISR触发?

    Adc1Regs.INTSEL1N2.bit.INT1SEL = 1; //设置EOC1以触发ADCINT1 

    感谢您澄清我的困惑。  我将继续并将您之前的回答标记为已解决问题,再次感谢您的帮助。

    TED