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.

[参考译文] CCS/F28M35H52C:ADC设置的最佳策略

Guru**** 2578945 points
Other Parts Discussed in Thread: F28M35H52C, CONTROLSUITE

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

https://e2e.ti.com/support/microcontrollers/c2000-microcontrollers-group/c2000/f/c2000-microcontrollers-forum/645647/ccs-f28m35h52c-best-strategy-for-setup-of-adcs

部件号:F28M35H52C
主题: controlSUITE中讨论的其他部件

工具/软件:Code Composer Studio

我已经调整了2个示例程序,以构成我的代码项目的基础:

1.我已将setup_m3和UART_ECHO_m3合并为M3代码,它根据我的新 set_pinout f28m35x.c &.h文件设置GPIO,此M3主程序还指示运行c28内核。

2.我已调整 CPU_TIMER_c28以运行计时器中断,以便Timer0每秒触发2.16万 时间,我已在外部LED闪烁的情况下进行了测试

接下来我要做的是触发一对(理想情况下)并发ADC转换,一个在ADC1上,另一个在ADC2上,每秒2.16万 次数。  每个通道的结果需要进入滚动结果缓冲区(压缩为每个通道3个8k共享RAM块)。  我认为SOC将在timer0_isr中触发,然后EOC ISR将获取每个读数,压缩并存储到缓冲RAM中。

还有8个其他ADC通道需要不经常读取,可能每秒读取一次,因此这些通道需要在每秒2个2.16万 采样之间交叉存取,因为我只有2个ADC通道。  在这里,我认为timer0_isr将运行一个计数器,它会触发不常发生的SOC,如果这些SOC是在每秒主2.16万 样本之后编码的。

当c28的时钟频率为150 MHz时,SDC时钟(ASYSCLK)可以运行的最大频率为37.5 MHz,而这些是我选择的时钟编号。  ACQPS为25的ADC周期需要大约1 us,因此两个采样之间有足够的时间(46.3 us at 2.16万 Hz)来采集更多的采样,但我希望主要的2.16万 Hz采样是在其他采样的同时进行的第一个采样。

另一个问题是有关通道使用的问题;在CPU的RM中,(spru22h.pdf) para 10.3 .3.3 (p861)表示"ADCINA0与VREFHI共享,因此在使用时不能用作可变输入源

外部参考电压模式。"这是否意味着,如果使用外部Vref,我根本不能使用ADCINA0?

感谢您的任何意见和建议等

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

    使用ePWM而不是计时器来触发ADC转换可能更可靠。 ePWM模块可以同步,以便您可以更好地控制多个触发器之间的计时关系。 您可以调整SOC优先级方案以指定高频率转换优先级。

    另一个选项是以相同的21.6kHz速率转换这8个不常使用的信道,但只能根据需要读取结果。 这将为您提供最简单的ADC SOC方案,并且只需极少的工作量。

    我建议在EOC上生成ADCINT时使用DMA将ADCRESULT值传输到RAM。 您可以在分派CPU处理数据之前在后台建立结果缓冲区。 这将减少CPU开销并降低ACIB延迟。

    我认为VREFHI注释是从另一个设备复制/粘贴错误,其中VREFHI和ADCINA0实际共享同一个引脚。 数据表信号说明显示F28M35H52C的情况并非如此。 我将更正此问题。

    有关已知问题,请务必查阅F28M35H52C勘误表文档。

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

    感谢您的详细回复。 我研究了controlSUITE (v220) ADC_SOC_c28程序中的代码,该程序包括设置ePWM以触发SOC,如您建议的那样,但您能否帮助我了解该代码中的GPIO设置的用途? 该代码似乎将GPIO0和GPIO8设置为输出,然后将GPIO0和GPIO0多路复用为EPWM1A,GPIO8设置为ADCSOCAn,但没有说明用户应该如何处理这些输出。 如果我修改了代码,是否可以运行ePWM而不将其与输出引脚关联? GPIO8的作用是什么? 事实真相已经存在,但文档中没有!

    此外,我还想使用共享RAM将结果从c28传递到m3处理器,以便我可以通过我在m3上运行的UART写出这些结果;我不清楚如何做到这一点:
    1.如何使结果变量驻留在共享RAM中?
    2.如果c28将结果存储到共享的RAM位置,如何使m3解决这些相同的变量,是通过将变量映射到同一个共享位置吗? 如果是,如何做到这一点?
    3.如何让c28向m3芯发送一条消息,表示结果已准备就绪,可以读取?

    抱歉所有问题:(

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

    Ted Mawson 3.0 GPIO8 说:
    和GPIO8的作用是什么?

    我认为GPIO8配置仅用于调试,因此可以查看何时生成EPWM1SOCA信号。  不是必需的。

    1. 如何使结果变量驻留在共享RAM中?[/QUOT]

    ADC结果始终存储在ADCRESULT寄存器中。 某些总线主控(M3/C28/DMA)需要将值显式复制到RAM。

    最简单的开始方法是将CPU结果从ADC1_ISR()复制到共享RAM。  这可以通过使用 #pragma data_section(VariableName,"LinkerCMDSection")在共享RAM中声明Voltage1和Voltage2数组快速完成;

    指针在处理器之间共享数据时可能更灵活。

    也可以完全消除ADC1_ISR(),并将DMA配置为在每次ADC EOC中断触发时直接从ADCRESULT寄存器复制值到共享RAM。

    [报价用户="Ted Mawson 3.0 "] 2. 如果c28将结果存储到共享的RAM位置,我如何使m3解决这些相同的变量,是通过将变量映射到相同的共享位置吗? 如果是,如何完成?[/QUOT]

    是的,可以将M3和C28变量相互声明。  另一种方法是使用指针。  每个处理器将使用各自的地址访问相同的内存位置。

    [报价用户="Ted Mawson 3.0 "] 3. 我如何让c28向m3芯发送一条消息,表示可以读取结果?[/QUOT]

    您可能希望有多个缓冲区,以便M3和C28不会尝试同时对相同的字进行读写。

    该设备具有IPC模块,可促进处理器之间的邮箱通信。  您可以通过IPC将ADC结果所在的内存地址或预定义缓冲区标识从C28发送到M3。

    Ted Mawson 3.0 说:
    抱歉所有问题:(


    这些都是我们不介意回答的好问题。

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

    Tommy,

    我被偏离了轨道,但现在我又回到了这一步。  我现在已经掌握了C28和M3程序使用的.cmd文件,并且我一直在研究其中的代码格式。  建议使用#pragma语句,如下所示...

    #pragma data_section(VariableName,"LinkerCMDSection";

    我是否正确地理解这依赖于我在.cmd文件的Sections下创建一个新的部分,因为在.cmd文件(标准 F28M35x_generic_C28_flash.cmd文件)中没有称为"LinkerCMDSection"的部分。 如果我说得对,请您帮助了解该节项的位置的语法,以及我如何明确地将其指定为共享RAM?

    指针的概念是有意义的,因为结果会被环绕(并被覆盖),只需增加指针,然后在达到maxium时将其重置为零。  理想情况下,我需要定义两个12位数的数组(实际电压和电流) ,每个数组的索引范围为0- 1.2599万 ,存储在两个3 x 8k共享RAM块中。  发生事件时,C28可以停止记录结果,并通知M3事件发生在阵列中的索引位置,以便M3可以读取事件发生前的历史数据并将其发送到远程系统。

    我知道2 x 12位数可以存储为3 x 8位字节,因此需要1.89万 字节来存储1.26万 12位数,并且我之前做过这件事, 我使用代码对12位数进行了切碎,并将它们存储在char数组中,然后在从char数组读取数据时重新生成这些数字; 您知道是否有可能(也许通过联合和位字段)在共享RAM中创建一个由12位数组成的数组,我可以直接建立索引?

    谢谢!

    TED

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

    我认为我可以创建一个uint16阵列,这样编译器(和C2000)就可以更高效地处理阵列,而不是将12位打包成字节。尽管我会在每个位置浪费4位; 只要我可以对可用的共享RAM进行部分分配,这将有效。

    我发现这个线程 e2e.ti.com/.../40.3964万 与我想做的非常相似。 我还在F28M35H52C数据表第154页的表6-6中发现了这一点...

    C地址 控制柱塞 尺寸M地址
    0000 C000–0000 CFFF S0 RAM (奇偶校验,共享) 8K 2000 8000–2000 9FFF
    0000 D000–0000 DFFF S1 RAM (奇偶校验,共享) 8K 2000 A000–2000 BFFF
    0000 E000–0000 EFFF S2 RAM (奇偶校验,共享) 8K 2000 C000–2000 DFFF
    0000 F000–0000 FFFF S3 RAM (奇偶校验,共享) 8K 2000 E000–2000 FFFF
    0001 0000–0001 0FFF S4 RAM (奇偶校验,共享) 8K 2001 0000–2001 1FFF
    0001 1000–0001 1FFF S5 RAM (奇偶校验,共享) 8K 2001 2000–2001 3FFF
    0001 2000–0001 2FFF S6 RAM (奇偶校验,共享) 8K 2001 4000–2001 5FFF
    0001 3000–0001 3FFF S7 RAM (奇偶校验,共享) 8K 2001 6000–2001 7FFF

    是否可以创建一个跨多个8k块但不使用所有最后一个块的数组,然后创建一个空间来放置剩余的内容? 喜欢这里...

    C28S0-3RAM :原点= 0x0C000,长度= 0x0.3138万 /* S0到S3 RAM,但不能一直到S3*/的末尾
    C28Endofs3. :原点= 0x0F138,长度= 0x000EC8 /* S3 RAM的其余部分-一直到S3*/的末尾

    然后,我将使用 #pragma ,如下所示...
    #pragma data_section("C28S0-3RAM")
    UINT16 myVoltsArray[0x3138];
    #pragma data_section("C28EndofS3")
    UINT16 myOtherVariable;

    这看起来是否有效?

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

    Ted,

    以下是 链接程序命令文件data_section pragma的一些快速参考

    对于data_sectionpragma,应定义一个自定义节,以便其它任何程序都不使用该节。  通过使用跨多个书帖的长度值,您当然可以将相邻的连续存储器合并到自定义书帖中。  与使用"|"运算符合并不同部分相比,我更幸运地使用这种方法。  请注意长度值,因为它们是以本机CPU地址的单位指定的(C28x为16位/地址,M3为8位/地址)。

    您可以在F28M35x_GlobalVariableDefs.c + F28M35x_Headers_nonBIOS.cmd中看到一些C28x示例。

    生成项目后,您还可以查看*.map输出文件,以验证编译器放置变量的位置。

    在遇到内存限制之前,建议使用本机16b字。  如果您确实需要位包,则可以参考F28M35x_headers/include/中的任何C28x寄存器覆盖结构。

    汤米

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

    Tommy,

    感谢您的建议。  我在M3 .cmd文件的内存部分创建了一个共享RAM位置,如下所示

    /* TM共享RAM -请参阅数据表*/第154页的表6-6
    SHRD_VRAM:Origin = 0x2000.8万,length = 0x6280 /* TM跨S0_S3 RAM,2.5216万 bytes */
    SHRD_RAM1 : Origin = 0x2000E280,length = 0x1D80 /* TM S3 RAM的余数- S3的最后7552 字节*/

    SHRD_IRAM:原点= 0x2001万,长度= 0x6280 /* TM跨S4_S7 RAM,2.5216万 字节*/
    SHRD_RAM2:原点= 0x2001.628万,长度= 0x1D80 /* TM S7 RAM的余数- S7 */的最后7552 字节

    然后我在下面的章节里用一个计法来跟踪

    .vArray:{}> SHRD_VRAM/* TM新的伏特RAM阵列部分*/
    iArray :{}> SHRD_IRAM/* TM关于AMPS RAM阵列*/的新章节

    然后,在我的M3 . c文件中,我创建了2个数组变量,并为它们指定了如下值

    // TM用户变量
    #pragma data_section(myVoltsArray,".vArray");
    unsigned short myVoltsArray[0x3140];//定义1.2608万 x 16位unsigned整数的数组
    
    #pragma data_section(myAmpsArray,".iArray");
    unsigned short myAmpsArray[0x3140;//定义1.2608万 {16位unsigned整数组
    
    
    
    
    (void
    
    
    myVoltsArray[1]= 1234;
    myAmpsArray[1]= 1234; 

    然后我能够编译和查看M3 .map文件,其中包括以下两个项目

    2001万 myAmpsArray
    2000.8万 myVoltsArray

    您认为这样做是否正常? 有一件事我不确定,那就是当我在#pragma语句中写入时

    #pragma data_section(myAmpsArray,".iArray";

    是对数组变量的引用正确吗-它不接受myAmpsArray[],所以我假设它是作为指向数组开头的指针,但我不确定。

    我还收到关于我的M3编译的警告...

    警告:创建不带书帖规范的输出书帖"ramfuncs"

    这似乎表明我正在使用的.cmd文件中缺少某些内容-您是否可以建议解决此问题?

    谢谢!

    TED

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

    您的编译看起来正确。 C编译器通常将数组视为指针,因此只需传递不带括号的基名。

    对于编程为闪存的版本,必须使用ramfuncs部分。 闪存不能以全CPU速度运行,因此通常会将某些函数重新定位到RAM中的ramfuncs部分,以加快执行速度。 您可以查看名称中带有"flash"的CMD文件以供参考。

    您还可以将CCS项目的“生成配置”从“闪存”更改为“RAM”,以便程序加载到“RAM”。 建议在初始开发时使用此选项,因为程序加载/执行速度更快,并且您将拥有更好的调试功能。

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

    Tommy,

    谢谢,我能够编译和运行C28和M3程序,并且没有错误,并且可以将变量从C28数组传递到相同的M3数组。

    我发现我使用的.cmd文件包含以下内容:

    #ifdef __TI_Compiler_version__
    #if __TI_Compiler_version__>= 1500.9万
    .TI.ramfunc :{} load = FLASH1 | FLASH2,
    RUN = C0 | C1 | C2 | C3,
    load_start (RamfuncsLoadStart),
    load_size (RamfuncsLoadSize),
    Load_End (RamfuncsLoadEnd),
    run_start (RamfuncsRunStart),
    Run_Size (RamfuncsRunSize),
    Run_End (RamfuncsRunEnd),
    crc_table (AppCrc,算法=CRC32_Prime),
    页= 0,对齐(8)
    #else
    Ramfuns :负载= FLASH1 | FLASH2,
    RUN = C0 | C1 | C2 | C3,
    load_start (RamfuncsLoadStart),
    load_size (RamfuncsLoadSize),
    Load_End (RamfuncsLoadEnd),
    run_start (RamfuncsRunStart),
    Run_Size (RamfuncsRunSize),
    Run_End (RamfuncsRunEnd),
    crc_table (AppCrc,算法=CRC32_Prime),
    页面= 0,对齐(8)
    #endif
    #endif 

    我对它进行了如下编辑

    Ramfuns :负载= FLASH1 | FLASH2,
    RUN = C0 | C1 | C2 | C3,
    load_start (RamfuncsLoadStart),
    load_size (RamfuncsLoadSize),
    Load_End (RamfuncsLoadEnd),
    run_start (RamfuncsRunStart),
    Run_Size (RamfuncsRunSize),
    Run_End (RamfuncsRunEnd),
    crc_table (AppCrc,算法=CRC32_Prime),
    页面= 0,对齐(8)
    

    然后我就可以在没有错误或警告的情况下进行编译了。

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

    Ted,

    这看起来是合理的。  我很高兴您现在可以使用它。

    汤米

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

    Tommy,

    今天我运行了ADC读取,但我仍有一些问题,我要创建一个新线程,但主题相同,所以我认为可以将其放在这里。  在这个主题中(我想是)同一个CPU,海报显示了配置ADC输入引脚的代码,如下所示...

    GpioG2CtrlRegs.AIOMUX1.bit.AIO2 = 2;//为A2 (模拟输入)操作配置AIO2
    GpioG2CtrlRegs.AIOMUX1.bit.AIO4 = 2;//为A4 (模拟输入)操作配置AIO4 

    我很确定我需要指定用作模拟输入而不是数字输入的模拟输入,但是我找不到说明“GpioG2CtrlRegs”搜索在数据表或参考手册中找不到任何内容的文档。  你能不能给我一个解释这一点的地方吗?

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

    模拟输入功能是ADC引脚的默认状态。 AIOMUX寄存器用于在数字GPIO引脚耗尽时将数字IO功能作为最后手段。 我相信模拟输入路径即使在启用AIO缓冲器后仍保持连接。

    汤米
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    因此,如果我想将它们用作模拟输入,是否可以将它们保留在默认状态?
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    是的,您可以将其保留为默认值。