如何理解这段编程DSP的代码?

How to understand this code of programming a DSP?

我的 "Introduction to Digital Signal Processor" 课程使用 C2000 Piccolo Launchpad 教授数字信号处理器。

目前,我完全迷失了。因为,我的导师似乎没有兴趣为我们提供任何 material 在家学习。

例如,

以下代码来自德州仪器的 controlSUITE 包。

//########################################################################
//
//  File:   f2802x_examples/timed_led_blink/Example_F2802xLedBlink.c
//
//  Title:  F2802x LED Blink Getting Started Program.
//
//  Group:          C2000
//  Target Device:  TMS320F2802x
//
//! \addtogroup example_list
//!  <h1>LED Blink</h1>
//!
//!   This example configures CPU Timer0 for a 500 msec period, and toggles 
//!   the GPIO0-4 LEDs  once per interrupt. For testing purposes, this example
//!   also increments a counter each time the timer asserts an interrupt.
//!
//!   Watch Variables:
//!   - interruptCount
//!
//!   Monitor the GPIO0-4 LEDs blink on (for 500 msec) and off (for 500 msec) 
//!   on the 2802x0 control card.
//
//  (C) Copyright 2012, Texas Instruments, Inc.
//#############################################################################
// $TI Release: PACKAGE NAME $
// $Release Date: PACKAGE RELEASE DATE $
//#############################################################################

#include "DSP28x_Project.h"   // Device Headerfile and Examples Include File

#include "f2802x_common/include/adc.h"
#include "f2802x_common/include/clk.h"
#include "f2802x_common/include/flash.h"
#include "f2802x_common/include/gpio.h"
#include "f2802x_common/include/pie.h"
#include "f2802x_common/include/pll.h"
#include "f2802x_common/include/timer.h"
#include "f2802x_common/include/wdog.h"

// Prototype statements for functions found within this file.
__interrupt void cpu_timer0_isr(void);

uint16_t interruptCount = 0;

ADC_Handle myAdc;
CLK_Handle myClk;
FLASH_Handle myFlash;
GPIO_Handle myGpio;
PIE_Handle myPie;
TIMER_Handle myTimer;

void main(void)
{

    CPU_Handle myCpu;
    PLL_Handle myPll;
    WDOG_Handle myWDog;

    // Initialize all the handles needed for this application    
    myAdc = ADC_init((void *)ADC_BASE_ADDR, sizeof(ADC_Obj));
    myClk = CLK_init((void *)CLK_BASE_ADDR, sizeof(CLK_Obj));
    myCpu = CPU_init((void *)NULL, sizeof(CPU_Obj));
    myFlash = FLASH_init((void *)FLASH_BASE_ADDR, sizeof(FLASH_Obj));
    myGpio = GPIO_init((void *)GPIO_BASE_ADDR, sizeof(GPIO_Obj));
    myPie = PIE_init((void *)PIE_BASE_ADDR, sizeof(PIE_Obj));
    myPll = PLL_init((void *)PLL_BASE_ADDR, sizeof(PLL_Obj));
    myTimer = TIMER_init((void *)TIMER0_BASE_ADDR, sizeof(TIMER_Obj));
    myWDog = WDOG_init((void *)WDOG_BASE_ADDR, sizeof(WDOG_Obj));

    // Perform basic system initialization    
    WDOG_disable(myWDog);
    CLK_enableAdcClock(myClk);
    (*Device_cal)();

    //Select the internal oscillator 1 as the clock source
    CLK_setOscSrc(myClk, CLK_OscSrc_Internal);

    // Setup the PLL for x10 /2 which will yield 50Mhz = 10Mhz * 10 / 2
    PLL_setup(myPll, PLL_Multiplier_10, PLL_DivideSelect_ClkIn_by_2);

    // Disable the PIE and all interrupts
    PIE_disable(myPie);
    PIE_disableAllInts(myPie);
    CPU_disableGlobalInts(myCpu);
    CPU_clearIntFlags(myCpu);

    // If running from flash copy RAM only functions to RAM   
#ifdef _FLASH
    memcpy(&RamfuncsRunStart, &RamfuncsLoadStart, (size_t)&RamfuncsLoadSize);
#endif      

    // Setup a debug vector table and enable the PIE
    PIE_setDebugIntVectorTable(myPie);
    PIE_enable(myPie);

    // Register interrupt handlers in the PIE vector table
    PIE_registerPieIntHandler(myPie, PIE_GroupNumber_1, PIE_SubGroupNumber_7, (intVec_t)&cpu_timer0_isr);

    // Configure CPU-Timer 0 to interrupt every 500 milliseconds:
    // 60MHz CPU Freq, 50 millisecond Period (in uSeconds)
    //    ConfigCpuTimer(&CpuTimer0, 60, 500000);
    TIMER_stop(myTimer);
    TIMER_setPeriod(myTimer, 50 * 500000);
    TIMER_setPreScaler(myTimer, 0);
    TIMER_reload(myTimer);
    TIMER_setEmulationMode(myTimer, TIMER_EmulationMode_StopAfterNextDecrement);
    TIMER_enableInt(myTimer);

    TIMER_start(myTimer);    

    // Configure GPIO 0-3 as outputs
    GPIO_setMode(myGpio, GPIO_Number_0, GPIO_0_Mode_GeneralPurpose);
    GPIO_setMode(myGpio, GPIO_Number_1, GPIO_0_Mode_GeneralPurpose);
    GPIO_setMode(myGpio, GPIO_Number_2, GPIO_0_Mode_GeneralPurpose);
    GPIO_setMode(myGpio, GPIO_Number_3, GPIO_0_Mode_GeneralPurpose);

    GPIO_setDirection(myGpio, GPIO_Number_0, GPIO_Direction_Output);
    GPIO_setDirection(myGpio, GPIO_Number_1, GPIO_Direction_Output);
    GPIO_setDirection(myGpio, GPIO_Number_2, GPIO_Direction_Output);
    GPIO_setDirection(myGpio, GPIO_Number_3, GPIO_Direction_Output);

    GPIO_setLow(myGpio, GPIO_Number_0);
    GPIO_setHigh(myGpio, GPIO_Number_1);
    GPIO_setLow(myGpio, GPIO_Number_2);
    GPIO_setHigh(myGpio, GPIO_Number_3);

    // Enable CPU INT1 which is connected to CPU-Timer 0:
    CPU_enableInt(myCpu, CPU_IntNumber_1);

    // Enable TINT0 in the PIE: Group 1 interrupt 7
    PIE_enableTimer0Int(myPie);

    // Enable global Interrupts and higher priority real-time debug events
    CPU_enableGlobalInts(myCpu);
    CPU_enableDebugInt(myCpu);

    for(;;){
        __asm(" NOP");
    }

}    

__interrupt void cpu_timer0_isr(void)
{
    interruptCount++;

    // Toggle GPIOs
    GPIO_toggle(myGpio, GPIO_Number_0);
    GPIO_toggle(myGpio, GPIO_Number_1);
    GPIO_toggle(myGpio, GPIO_Number_2);
    GPIO_toggle(myGpio, GPIO_Number_3);

    // Acknowledge this interrupt to receive more interrupts from group 1
    PIE_clearInt(myPie, PIE_GroupNumber_1);
} 

//===========================================================================
// No more.
//===========================================================================

如我所见,这里发生了很多事情。但是,我找不到学习 C2000 编码基础知识的地方。

我有一些基本问题:

(1) __interrupt__asm 关键字有什么作用?我在哪里可以找到这些关键字的参考资料?

(2) 我怎么知道我需要多少句柄?例如,对于这个 LED 闪烁应用程序,他们声明了 9 个句柄。为什么?

(3) 什么是"Base Address"?

(4) 为什么 WachDog 被禁用了?为什么 PIE 和 CPU 及其中断被禁用?

(5) 什么是定时器预分频器?

(6) 什么是定时器仿真模式?

(7) GPIO_setmode 是做什么的?模式是关于什么的?

(8) 我可以从哪里开始了解所有这些细节?

这是一个很好的开始 material 还是浪费时间? http://www.ti.com/lit/ug/spru430f/spru430f.pdf

  1. __interrupt 和 __asm 关键字有什么作用?我在哪里可以找到这些关键字的参考资料?

__interrupt 告诉编译器用适合平台的代码包装函数,通常是为了保存和恢复函数使用的寄存器,这样中断的代码不受影响,并且 return 用"return from interrupt" 指令而不是 "return from subroutine" 指令

__asm 告诉编译器在发送到汇编程序之前在编译器的输出中插入一条机器级指令

  1. 我怎么知道我需要多少手柄?例如,对于这个 LED 闪烁应用程序,他们声明了 9 个句柄。为什么?

因为代码使用了 TI 库和头文件定义的那九个硬件子系统

  1. 什么是 "Base Address"?

在硬件中,有几组寄存器控制I/O、定时器等硬件功能,每组寄存器分配一个内存范围,从基地址开始。可能有几个相同的组,例如,几个定时器。通过为每个组使用基地址,可以在所有实例之间共享处理该功能的代码,例如计时器。

  1. 为什么 WachDog 被禁用了?为什么 PIE 和 CPU 及其中断被禁用?

我怀疑正在执行的任何初始化都需要比看门狗间隔更长的时间,因此禁用它以防止 CPU 被看门狗重置

  1. 什么是定时器预分频器?

它是定时器源时钟的分频器,用于为定时器创建适当的分辨率和范围

  1. 什么是定时器仿真模式?

我不知道。也许这只是 "mode." 的一个奇怪的名字,或者它可能是一种遗留模式。

  1. GPIO_setmode 是做什么的?模式是关于什么的?

GPIO 硬件必须有选项,例如,将中断附加到边缘,或更改转换率或迟滞,或许多事情中的任何一个。

  1. 我可以从哪里开始了解所有这些细节?

C2000 Piccolo 参考手册 C2000 短笛数据 Sheet 编译器文档 controlSUITE 包文档

您提出的所有问题都与编写此代码的处理器的具体细节有关。因此,获得问题答案的最佳方法是查看 'C2000 Piccolo TMS320F28027 MCU' 的数据表,因为这是 C2000 Piccolo Launchpad 评估平台使用的微控制器。数据表可以通过 link 直接从德州仪器的网页下载:http://www.ti.com/tool/launchxl-f28027.

我将根据我对微控制器的一般了解,对您的一些问题提供一些简短的答案。正如我之前所写,我不保证此信息是正确的,以确保您必须阅读数据表。该文档还应该可以帮助您入门:http://www.ti.com/lit/ug/spruhh2a/spruhh2a.pdf.

1: __interrupt 关键字可能将标记函数设置为指定中断的 中断服务例程 (ISR),请参阅 https://en.wikipedia.org/wiki/Interrupt_handler.

__asm 关键字激活内联汇编,使您能够直接在 C 代码中编写特定于处理器的汇编指令。

3: 基地址可能指的是内存映射i/o-devices和控制寄存器的内存地址。

4: 看门狗是一种计时器,您必须定期对其进行重置。如果没有这样做,则表明您的程序可能被冻结,在这种情况下,看门狗可以触发微控制器的软复位。中断可能被禁用只是因为此演示应用程序不需要它们。

8: 如果你 google 一点点,网上有很多资源 ;) 我将从阅读 'Technical Documents' 下列出的所有 material 开始:http://www.ti.com/tool/launchxl-f28027.

1.

__interrupt__asm 是表示中断函数 cpu_timer0_isr(中断服务例程)和汇编代码的代码标记。

2.

这取决于你想要实现什么,你需要从设备读取什么。

3.

...BASE_ADDR 是内存中要read/write 驱动设备的地址。特别是 ADC_BASE_ADDR defines 模数转换器 (ADC) 寄存器的基地址。

4.

Watchdogs用来监视一些activity。在 TMS320F2802x 和 TMS320F2802xx 系列中

Each device contains two watchdogs: CPU-Watchdog that monitors the core and NMI-Watchdog that is a missing clock-detect circuit. The user software must regularly reset the CPU-watchdog counter within a certain time frame; otherwise, the CPU-watchdog generates a reset to the processor. The CPU-watchdog can be disabled if necessary. The NMI-Watchdog engages only in case of a clock failure and can either generate an interrupt or a device reset.

NMI 代表不可屏蔽中断:

cat /proc/interrupts

在 Linux 上查看每个 CPU 上的计数器。

5.

请阅读 以获取其余答案。