ZHDA102 March   2026 MSPM0G3519

 

  1.   1
  2.   摘要
  3.   商标
  4. 1 简介
  5. 2 空闲低电平状态:PWM 输出通道低电平状态配置
  6. 3 非对称 PWM:具有相移控制功能的双路同步 PWM 生成
    1. 3.1 使用相位加载功能
      1. 3.1.1 主要计时器(主计时器)的配置
      2. 3.1.2 辅助计时器的配置
      3. 3.1.3 交叉触发功能的实现
    2. 3.2 使用辅助捕获/比较通道
  7. 4 位操作 (Bit-Banging) 仿真:基于软件的通信协议实现
    1. 4.1 使用 TIMA 仿真 UART Rx
    2. 4.2 使用 TIMA 仿真 UART Tx
  8. 5 基于反馈的 PWM 生成
    1. 5.1 基于反馈的 PWM 信号复制
    2. 5.2 使用输入基准生成延迟的 PWM 信号
  9. 6 延迟计时器启动:具有可配置延迟的同步计时器实例启动
  10. 7 基于硬件事件停止正在运行的计时器
  11. 8 动态 PWM 更新:占空比和时间周期调整
    1. 8.1 影子加载和影子比较功能
    2. 8.2 使用 DMA 生成任意信号
  12. 9 总结
  13. 10参考资料

使用 TIMA 仿真 UART Tx

TIMA 通过利用其输出生成功能、影子 CCACT 功能和重复计数器机制,为 UART 发送器仿真提供稳健的解决方案。通过整合 DMA 传输可以显著提升此实现的性能,从而创建优化系统资源的端到端解决方案。通过 DMA 处理数据移动,可以更大限度地减少 CPU 干预,同时确保精确的位时序和可靠的数据传输。

这种集成的计时器 DMA 方法消除了对中断优先级的依赖,并保证一致的时序精度。该解决方案可确保确定性的行为,使其成为位操作仿真应用的理想选择。图 4-3 展示了使用 TIMA 作为 UART Tx 的流程图。

 展示 UART Tx 的流程图图 4-3 展示 UART Tx 的流程图

配置序列如下详述:

  • 配置计数模式、CLKSEL、CLKDIV 等参数。根据位持续时间配置加载值。
  • 将 CC 值配置为“LOAD/4”或“LOAD/4+1”,这将在总位持续时间四分之一处生成 CC 事件,需要此事件以将计时器配置为发送下一个位值。
  • 由于 UART 空闲状态为高电平,因此将输出初始值 OCTL.CCPIV 配置为高电平。
  • 配置 CCACT 以使 ZACT 配置为将输出驱动为低电平,这是因为在启用计数器后应生成低电平 START 位。
  • 将 CCACT 更新机制配置为立即更新。
  • 启用重载和零事件抑制功能,这样仅当 RC=0 时才会生成零事件。
  • 启用计时器零中断
  • TIMA 可以通过事件互联矩阵生成事件并触发 DMA 等其他外设。需要将 TIMA 配置为事件发布者,以在 CC0 事件生成时触发 DMA 来启动数据传输。
  • 需要将 DMA 配置为事件订阅者,以使 DMA 可以根据 TIMA 事件执行数据传输。
  • 在重复单一传输模式下配置 DMA,宽度为 32 位且源地址递增
  • 将 DMA 目标地址配置为 TIMA.CCACT 寄存器。每次发生 CC0 事件时,都需要更新 CCACT 寄存器才能发送下一个位值。此更新机制将由 DMA 执行。
  • 预处理要传输的数据,以在要传输的位值为 0 时,将 0x2 存储在缓冲区中,这会将计时器输出驱动为低电平。并且,当要发送的位值为 1 时,将 0x1 存储在缓冲区中,这会将计时器输出驱动为高电平。
  • 将 DMA 源地址配置为包含相关 CCACT 配置的预处理缓冲区
  • 将 RCLD 配置为帧中预期的 UART 位数
  • 要发送数据时启用计数器。
  • 在零中断 ISR 中,禁用计数器,并将 ZACT 设置为 0x2 用于下一次 UART 帧传输。
/* Timer Configuration for Emulating UART Tx with DMA */
//For 9600 baud rate, single bit duration is 104.167us, hence configuring LOAD value as 3333, with 32MHz clock
static const DL_TimerA_ClockConfig gPWM_0ClockConfig = {
    .clockSel = DL_TIMER_CLOCK_BUSCLK,
    .divideRatio = DL_TIMER_CLOCK_DIVIDE_1,
    .prescale = 0U
};
static const DL_TimerA_PWMConfig gPWM_0Config = {
    .pwmMode = DL_TIMER_PWM_MODE_EDGE_ALIGN_UP,
    .period = 3333,
    .isTimerWithFourCC = false,
    .startTimer = DL_TIMER_STOP,
};

SYSCONFIG_WEAK void SYSCFG_DL_PWM_0_init(void) {
    DL_TimerA_setClockConfig(
        PWM_0_INST, (DL_TimerA_ClockConfig *) &gPWM_0ClockConfig);
    DL_TimerA_initPWMMode(
        PWM_0_INST, (DL_TimerA_PWMConfig *) &gPWM_0Config);
    DL_TimerA_setCounterControl(PWM_0_INST,DL_TIMER_CZC_CCCTL0_ZCOND,DL_TIMER_CAC_CCCTL0_ACOND,DL_TIMER_CLC_CCCTL0_LCOND);
    DL_TimerA_setCaptureCompareOutCtl(PWM_0_INST, DL_TIMER_CC_OCTL_INIT_VAL_HIGH, //Keeping CC0 initial value as HIGH as UART idle state should be HIGH
		DL_TIMER_CC_OCTL_INV_OUT_DISABLED, DL_TIMER_CC_OCTL_SRC_FUNCVAL,
		DL_TIMERA_CAPTURE_COMPARE_0_INDEX);
    PWM_0_INST->COUNTERREGS.CCACT_01[0]=0x2;//Configuring Zero action as CC Output LOW, as START bit in UART is always LOW
    DL_TimerA_setCaptCompUpdateMethod(PWM_0_INST, DL_TIMER_CC_UPDATE_METHOD_IMMEDIATE, DL_TIMERA_CAPTURE_COMPARE_0_INDEX);
    DL_Timer_setCaptCompActUpdateMethod(PWM_0_INST,DL_TIMER_CCACT_UPDATE_METHOD_IMMEDIATE, DL_TIMERA_CAPTURE_COMPARE_0_INDEX);
    DL_TimerA_setCaptureCompareValue(PWM_0_INST, 834, DL_TIMER_CC_0_INDEX);//Configuring CC0 as almost 1/4th the bit width
    PWM_0_INST->COUNTERREGS.CCACT_01[0]=0x2;//Configuring Zero action as CC Output LOW, as START bit in UART is always LOW
    DL_Timer_enableInterrupt(PWM_0_INST, DL_TIMER_INTERRUPT_ZERO_EVENT);
    DL_Timer_enableLZEventSuppression(PWM_0_INST);//This will suppress zero events until RC is equal to 0

    DL_Timer_enableEvent(PWM_0_INST, DL_TIMER_EVENT_ROUTE_1, DL_TIMER_INTERRUPT_CC0_UP_EVENT);//CC0_UP Event triggers the DMA CHAN0
    DL_Timer_setPublisherChanID(PWM_0_INST, DL_TIMER_PUBLISHER_INDEX_0, 1);//Timer publishing event to DMA to carry out CCACT update
    DL_TimerA_enableClock(PWM_0_INST);
    DL_TimerA_setCCPDirection(PWM_0_INST , DL_TIMER_CC0_OUTPUT );
}
// Below is DMA configuration
static const DL_DMA_Config gDMA_CH0Config = {
    .transferMode   = DL_DMA_FULL_CH_REPEAT_SINGLE_TRANSFER_MODE,
    .extendedMode   = DL_DMA_NORMAL_MODE,
    .destIncrement  = DL_DMA_ADDR_UNCHANGED,
    .srcIncrement   = DL_DMA_ADDR_INCREMENT,
    .destWidth      = DL_DMA_WIDTH_WORD,
    .srcWidth       = DL_DMA_WIDTH_WORD,
    .trigger        = DMA_GENERIC_SUB0_TRIG,
    .triggerType    = DL_DMA_TRIGGER_TYPE_EXTERNAL,
};
void SYSCFG_DL_DMA_CH0_init(void)
{
    DL_DMA_initChannel(DMA, DMA_CH0_CHAN_ID , (DL_DMA_Config *) &gDMA_CH0Config);
    DL_DMA_clearInterruptStatus(DMA, DL_DMA_INTERRUPT_CHANNEL0);
        DL_DMA_enableInterrupt(DMA, DL_DMA_INTERRUPT_CHANNEL0);
        DL_DMA_setSubscriberChanID(DMA, DL_DMA_SUBSCRIBER_INDEX_0, 0x01);//DMA subscribing to Timer event
}

void SYSCFG_DL_DMA_init(void){
    SYSCFG_DL_DMA_CH0_init();
}
/* Application Code for Emulating UART Tx Using Timer */
uint8_t data_to_transmit[8]={1,1,0,1,0,1,0,0};//this equals to 0x2B
uint8_t uart_frame_to_transmit[9];//8 data bits + STOP Bit
uint8_t number_of_bits_in_each_frame=0;
uint8_t number_of_bits_in_uart_frame=0;
uint32_t DMA_frame_to_transmit[9];
int number_of_CC_interrupts=0;
int number_of_zero_int=0;
//int number_of_load_int=0;
volatile bool gIsTimerExpired;
int main(void)
{
    bool isRetentionError;

    NVIC_EnableIRQ(PWM_0_INST_INT_IRQN);

    SYSCFG_DL_init();
    DL_DMA_setDestAddr(DMA, DMA_CH0_CHAN_ID, (uint32_t)&PWM_0_INST->COUNTERREGS.CCACT_01[0]);//Set DMA destination address as TIM_CCACT MMR
    DL_DMA_setSrcAddr(DMA, DMA_CH0_CHAN_ID, (uint32_t)&DMA_frame_to_transmit );//Set DMA source address as the array which contains CCACT config for each bit
    DL_DMA_setTransferSize( DMA, DMA_CH0_CHAN_ID,number_of_bits_in_uart_frame);//Configure DMA SZ for the number of bits in each frame

    DL_DMA_enableChannel(DMA, DMA_CH0_CHAN_ID);//Enable DMA channel

    number_of_bits_in_each_frame=sizeof(data_to_transmit);
    uart_frame_to_transmit[number_of_bits_in_each_frame+1]=1;//STOP bit is always 1

    for(int i=0;i<number_of_bits_in_each_frame;i++){
        uart_frame_to_transmit[i]=data_to_transmit[i];
        }

    number_of_bits_in_uart_frame=sizeof(uart_frame_to_transmit); //Adding 1 Stop bit Frame

    DL_Timer_setRepeatCounter(PWM_0_INST,number_of_bits_in_uart_frame);//Configuring RCLD as the number_of_bits_in_uart_frame
/////////PRE-PROCESS THE ARRAY WHICH CONTAINS THE CORRESPONDING CCACT CONFIGURATION FOR EACH BIT//////////////////////////
    for( number_of_CC_interrupts=0;number_of_CC_interrupts<(number_of_bits_in_uart_frame+1);number_of_CC_interrupts++){
    if(uart_frame_to_transmit[number_of_CC_interrupts]==0){
        DMA_frame_to_transmit[number_of_CC_interrupts]=0x2;//if bit value is 0 configure ZACT as CC Output LOW
    }
    else if(uart_frame_to_transmit[number_of_CC_interrupts]==1){
        DMA_frame_to_transmit[number_of_CC_interrupts]=0x1;//if bit value is 1 configure ZACT as CC Output HIGH
    }
    else{
    }
    if(number_of_CC_interrupts==number_of_bits_in_uart_frame-1){
        DMA_frame_to_transmit[number_of_CC_interrupts]=0x1;//Always generate a HIGH stop bit
    }
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    DL_TimerA_startCounter(PWM_0_INST);//Start Counter
    while(1);
}

void PWM_0_INST_IRQHandler(void)
{
    switch (DL_TimerA_getPendingInterrupt(PWM_0_INST)) {
        case DL_TIMER_IIDX_ZERO:
            number_of_zero_int++;
            DL_TimerA_stopCounter(PWM_0_INST);//Stop the counter
            PWM_0_INST->COUNTERREGS.CCACT_01[0]=0x2;//Configure ZACT for the next UART Transmission
            delay_cycles(3333);//Adding 1 bit of delay between 2 UART frames
            number_of_CC_interrupts=0;
            DL_TimerA_startCounter(PWM_0_INST);//Start counter for the next UART Transmission
            break;
        default:
            break;
    }
}

 使用 TIMA 以 UART 格式发送 0x2B图 4-4 使用 TIMA 以 UART 格式发送 0x2B
注:

对于上述应用,可以使用 TIMG 而不是 TIMA 作为替代实现。但是,TIMG 不支持重复计数器功能。因此,使用 TIMG 时,需要通过 ISR 内的软件干预手动重新加载计数器。