ZHDA102 March   2026 MSPM0G3519

 

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