手搓瑞萨DTC – RA2L1

/ 0评 / 0

一开始看手册,DTC分类到DMA中,而高端的芯片比如4M1是DMAC/DTC两个外设的,因为STM32的经验原因,就假设了他可能也是一个基础DMA,实际上并不是这样的.

DTC本质是不能自己工作的,他要配合ICU,ICU可以发送信号到NVIC,也可以发送到DTC,当然DTC也有自己的中断可以往ICU发.

其次让我迷惑的地方是,他说的寄存器我没在头文件看到,也没正在库文件看到,原来是瑞萨给改了名字.并且这个寄存器其实是看不到的,他要存在我们SRAM里,然后必要时通过指针方式赋值到向量里,再由外设取走SRAM里的描述.

为了方便,我就把他内部寄存器定义重新按照手册定义了一遍.

// ====================== 常用取值宏 ======================
// 数据宽度(SZ[5:4])
#define DTC_SZ_8BIT   0b00   // 每次传输 1 字节;地址自然+/-1;SAR/DAR 可不强制对齐
#define DTC_SZ_16BIT  0b01   // 每次传输 2 字节;地址步长=2;SAR/DAR 需 2 字节对齐
#define DTC_SZ_32BIT  0b10   // 每次传输 4 字节;地址步长=4;SAR/DAR 需 4 字节对齐

// 地址模式(SM / DM 均适用,[3:2] 或 [3:2])
#define DTC_AM_FIX    0b00   // 固定地址(不自增/不自减)
#define DTC_AM_DEC    0b01   // 每次传输后地址自减(按 SZ 步长)
#define DTC_AM_INC    0b10   // 每次传输后地址自增(按 SZ 步长)

// 传输模式(MD[7:6])
#define DTC_MD_NORMAL 0b00   // Normal:一次激活=搬"1 个单位"(unit);CRA=本轮 unit 个数(通常设 1)
#define DTC_MD_REPEAT 0b01   // Repeat:重复区(由 DTS 指定)在每次 unit 结束后回卷/保持,CRA/CRB 的意义见下
#define DTC_MD_BLOCK  0b10   // Block:一次激活=连续搬完"一个块"(CRA 个 unit),并按 CRB 的块计数迭代

// 块/重复区选择(MRB.DTS)--"谁是块/重复区域"
#define DTC_AREA_DST  0      // 目的地址为块/重复区(DTS=0)
#define DTC_AREA_SRC  1      // 源地址为块/重复区(DTS=1)

// 中断发送时机(MRB.DISEL)
#define DTC_IRQ_ON_END   0   // 只在"完成指定量"时发一次中断(Normal: CRA→0;Block: 最后一个块完成;Repeat: 最后一次)
#define DTC_IRQ_EVERY    1   // 每完成 1 个 unit 就发中断(DTC 不会因此暂停搬运;CPU 看到时可能已搬完)

// 链式(MRB.CHNE/CHNS)
#define DTC_CHAIN_OFF    0   // CHNE=0:不链式
#define DTC_CHAIN_ON     1   // CHNE=1:链式,CHNS=0/1 决定链的时机(见 MRB 注释)

// ====================== 位域定义 ======================
typedef struct {
    // [1:0] 预留:必须写 0.DTC 会按字节读取此字节,非 0 可能触发未定义行为/异常.
    uint8_t _res0 : 2;

    // 源地址模式 SM[3:2]:决定 SAR 在每次"unit 传输"后的步进方式(步长由 SZ 决定:1/2/4)
    // - DTC_AM_FIX: 源地址固定(常见于写同一个寄存器/常数表到一片内存)
    // - DTC_AM_DEC: 源地址自减
    // - DTC_AM_INC: 源地址自增(典型的内存拷贝/读流)
    uint8_t SM    : 2;  // MRA[3:2]

    // 数据宽度 SZ[5:4]:一次"unit"搬多少字节.也决定了地址步长与对齐要求.
    // - DTC_SZ_8BIT : 1B;  SAR/DAR 任意对齐
    // - DTC_SZ_16BIT: 2B;  SAR/DAR 必须 2B 对齐
    // - DTC_SZ_32BIT: 4B;  SAR/DAR 必须 4B 对齐
    // 选择不当会导致硬件对齐错误(搬运失败/异常)
    uint8_t SZ    : 2;  // MRA[5:4]

    // 传输模式 MD[7:6]:决定 CRA/CRB 的意义,一次激活搬多少.
    // - DTC_MD_NORMAL: 一次激活只搬 1 个 unit;CRA=本轮 unit 次数(常用 CRA=1;想让 1 次激活搬 N 笔,就设 CRA=N,但这需要 N 次触发)
    // - DTC_MD_REPEAT: 重复传输;由 MRB.DTS 指定"重复区域"(SRC/DST).每次 unit 后,重复区地址按规则回卷/保持;CRA/CRB 组合定义重复次数
    // - DTC_MD_BLOCK : 块传输;一次激活会连续搬完 1 个"块"(CRA 个 unit),并按 CRB 指定的"块数"迭代
    // - 0b11: 停止/禁止(不要用)
    uint8_t MD    : 2;  // MRA[7:6]
} DTC_MRA_t;

typedef struct {
    // [1:0] 预留:必须写 0.
    uint8_t _res1 : 2;

    // 目的地址模式 DM[3:2]:同 SM,只是作用于 DAR.
    // - DTC_AM_FIX: 目的地址固定(典型:把多个 unit 写到同一寄存器地址)
    // - DTC_AM_DEC: 目的地址自减
    // - DTC_AM_INC: 目的地址自增(典型的内存拷贝/写流)
    uint8_t DM    : 2;  // MRB[3:2]

    // 块/重复区选择 DTS[4]:
    // - 在 BLOCK 模式:选择"谁是块区域"(谁在一个块内随 unit 变化,块结束时再回卷).0=目的;1=源.
    // - 在 REPEAT 模式:选择"谁是重复区域"(谁在每次 unit 结束后回卷/保持).0=目的;1=源.
    // - 在 NORMAL 模式:无实际意义,可置 0.
    uint8_t DTS   : 1;  // MRB[4]

    // 中断发送时机 DISEL[5]:
    // - 0:只在"完成指定量"时发一次(Normal: CRA 到 0;Block: 最后一个块完成;Repeat: 最后一次)
    // - 1:每完成 1 个 unit 就给 CPU 发中断请求(DTC 不暂停继续搬,CPU 可能看到的是"已搬完"的状态)
    //   是否能进到 CPU,要看 NVIC 以及你是否把该事件(或 DTC_COMPLETE)路由到某个 IRQ.
    uint8_t DISEL : 1;  // MRB[5]

    // 链式选择 CHNS[6](在 CHNE=1 时有效):
    // - 0:链式"立即/连续"--当前 TI 完成后,直接取下一条 TI 执行
    // - 1:链式"在指定次数完成时"才切到下一条(如 CRA 或 CRB 计数到 0)
    // 不同芯片文档对"指定次数"的描述略有差异,但大体是"到达模式定义的终点时再切换".
    uint8_t CHNS  : 1;  // MRB[6]

    // 链式使能 CHNE[7]:
    // - 0:不链式(只执行当前这条 TI 就结束)
    // - 1:启用链式;下一条 TI 的地址 = 当前 TI 起始地址 + 16 字节(TI 固定 16B),
    //      直到遇到某条 TI 的 CHNE=0(链尾)或达到链式终止条件(结合 CHNS).
    uint8_t CHNE  : 1;  // MRB[7]
} DTC_MRB_t;

typedef struct __attribute__((packed)) {
    // 0x00-0x01: 保留.必须写 0.DTC 会把这 16 字节按既定偏移解析,非 0 可能导致解析异常.
    uint8_t       _resv00;         // 0x00 = 0
    uint8_t       _resv01;         // 0x01 = 0

    // 0x02: MRB(见上).建议:初始化前把整个 TI 清零,再逐位设置,避免保留位污染.
    DTC_MRB_t     MRB;             // 0x02

    // 0x03: MRA(见上).
    DTC_MRA_t     MRA;             // 0x03

    // 0x04-0x07: SAR(Source Address Register)
    // - 源起始地址.对齐要求由 SZ 决定(SZ=16/32bit 时至少 2/4 字节对齐).
    // - 在 INC/DEC 模式下会随每个 unit 传输按步长变化;在 FIX 模式下保持不变.
    // - 在 BLOCK/REPEAT 且 DTS=1(源为块/重复区)时,块/重复结束会按规则回卷/保持.
    void const  * SAR;             // 0x04-0x07

    // 0x08-0x0B: DAR(Destination Address Register)
    // - 目的起始地址.对齐要求同上.
    // - INC/DEC/FIX 语义同 SM,只是作用于目的端.
    // - 在 BLOCK/REPEAT 且 DTS=0(目的为块/重复区)时,块/重复结束会回卷/保持.
    void        * DAR;             // 0x08-0x0B

    // 0x0C-0x0D: CRB(Counter B)
    // - 在 BLOCK 模式:CRB = "块计数"(要传多少块);每块大小=CRA(unit 数).
    // - 在 REPEAT 模式:CRB = "重复次数/外层计数"(DTS 指定哪一侧为重复区);到 0 结束.
    // - 在 NORMAL 模式:通常忽略/置 0.
    uint16_t      CRB;             // 0x0C-0x0D

    // 0x0E-0x0F: CRA(Counter A)
    // - 在 NORMAL 模式:CRA = "本轮 unit 次数".一次激活搬 1 个 unit;若想 1 次激活搬 N 个 unit,需要外设触发 N 次.
    // - 在 BLOCK 模式:CRA = "块大小"(unit 数).一次激活会连续搬完 CRA 个 unit;配合 CRB 决定总量.
    // - 在 REPEAT 模式:CRA = "每次激活的 unit 次数"(内层);配合 CRB 与 DTS 实现重复语义.
    // - 计数为 0 的行为:一般视为"未定义/非法配置",请至少设为 1.
    uint16_t      CRA;             // 0x0E-0x0F
} DTC_Descriptor_t;

为了测试DTC,就要找到一个可以发往ICU的中断源,我挑了GPT3_OVF这个源.

那么就像配置中断一样配置.

R_ICU->IELSR_b[0].DTCE = 0;       // 先关 DTC 触发
R_ICU->IELSR_b[0].IR   = 0;       // 清 IR:注意按手册要求,清 IR 之前 DTCE 必须为 0
R_ICU->IELSR_b[0].IELS = 0x1A;    // GPT3_OVF (Group0 的编码)
R_ICU->IELSR_b[0].DTCE = 1;       // 允许该事件触发 DTC
    

因为有DTCE,所以这时候NVIC是收不到中断的,其次就是配置定时器,这里省略,等下附上所有代码.

DTC初始化必须放在GPT初始化之前,不然就漏EVT了啊.

static DTC_Descriptor_t ti_irq0;         // 用于 IELSR[0] 的 TI

ti_irq0.MRA.MD = DTC_MD_NORMAL;
ti_irq0.MRA.SZ = DTC_SZ_32BIT;
ti_irq0.MRA.SM = DTC_AM_INC;
ti_irq0.MRB.DM = DTC_AM_INC;
ti_irq0.MRB.DTS = DTC_AREA_SRC;
ti_irq0.MRB.DISEL = DTC_IRQ_EVERY;
ti_irq0.MRB.CHNE = 0; // 链式使能(1=开,0=关)
ti_irq0.MRB.CHNS = 0; // 配合CHNE,查看注释.

ti_irq0.SAR = src;
ti_irq0.DAR = dst;
ti_irq0.CRB = 1;
ti_irq0.CRA = 4;        // 16B / 4B = 4 次

dtc_vector_table[0] = (uint32_t)&ti_irq0;  // 表项是 TI 的地址

R_DTC->DTCST = 0;                 // 停止 DTC
R_DTC->DTCCR_b.RRS = 0;           // 允许更新向量表/TI 后再置 1
R_DTC->DTCVBR = (uint32_t)dtc_vector_table; // 1KB 对齐基址
R_DTC->DTCCR_b.RRS = 1;           // 运行中可读 TI
R_DTC->DTCST = 1;                 // 启动 DTC(模块开始工作)

我现在用的是DTC_IRQ_EVERY,每次OVF都会传输一次(注意在Normal模式).

下一次中断就是这样.

最后一次中断会清空DTCE,之后DTC不管这个,OVF来的是真中断.另外强调,中断不是必须开的.

如果改成DTC_IRQ_ON_END,那么最后一次传输才触发中断.如果不是Normal,而是块模式,他会定义一次传输是一个块,所以看起来也是一次性传完,因为我的例子只传了一个Block.

比如设置为块模式,10个块,那么这时候MRB.DTS就有意义,比如src[4],而dst[40],那么MRB.DTS就设置为SRC,他会每一个块传输后指针回头部.

static uint32_t src[4] = {0x11223344, 0x55667788, 0xAABBCCDD, 0x01234567};
static uint32_t dst[40] = {0};

ti_irq0.MRA.MD = DTC_MD_BLOCK;
ti_irq0.MRA.SZ = DTC_SZ_32BIT;
ti_irq0.MRA.SM = DTC_AM_INC;
ti_irq0.MRB.DM = DTC_AM_INC;
ti_irq0.MRB.DTS = DTC_AREA_SRC;
ti_irq0.MRB.DISEL = DTC_IRQ_EVERY;
ti_irq0.MRB.CHNE = 0; // 链式使能(1=开,0=关)
ti_irq0.MRB.CHNS = 0; // 配合CHNE,查看注释.

ti_irq0.SAR = src;
ti_irq0.DAR = dst;
ti_irq0.CRB = 10;
ti_irq0.CRA = (4u << 8) | 4u; // 0x0404 Block模式下,CRA的高8,低8要一样.

每BLOCK传输都IRQ.

到最后一个Block清零DTCE.

普通DMA很多时候用软件激活,那么DTC软件激活怎么做,我想可能是ELC,但是ELC我还没看到这一章.

如果链式传输,那就是这条完成,查找向量表的下一条去传输(固定就是下一条),直到找到最后一条CHNE=0的就结束.

完整代码例子.

#include <stdint.h>
#include <stddef.h>
#include "R7FA2L1AB.h"

// ====================== 常用取值宏 ======================
// 数据宽度(SZ[5:4])
#define DTC_SZ_8BIT   0b00   // 每次传输 1 字节;地址自然+/-1;SAR/DAR 可不强制对齐
#define DTC_SZ_16BIT  0b01   // 每次传输 2 字节;地址步长=2;SAR/DAR 需 2 字节对齐
#define DTC_SZ_32BIT  0b10   // 每次传输 4 字节;地址步长=4;SAR/DAR 需 4 字节对齐
// #define DTC_SZ_RSVD 0b11   // 预留/禁止(不要用)

// 地址模式(SM / DM 均适用,[3:2] 或 [3:2])
#define DTC_AM_FIX    0b00   // 固定地址(不自增/不自减)
#define DTC_AM_DEC    0b01   // 每次传输后地址自减(按 SZ 步长)
#define DTC_AM_INC    0b10   // 每次传输后地址自增(按 SZ 步长)
// #define DTC_AM_RSVD 0b11   // 预留/禁止(不要用)

// 传输模式(MD[7:6])
#define DTC_MD_NORMAL 0b00   // Normal:一次激活=搬"1 个单位"(unit);CRA=本轮 unit 个数(通常设 1)
#define DTC_MD_REPEAT 0b01   // Repeat:重复区(由 DTS 指定)在每次 unit 结束后回卷/保持,CRA/CRB 的意义见下
#define DTC_MD_BLOCK  0b10   // Block:一次激活=连续搬完"一个块"(CRA 个 unit),并按 CRB 的块计数迭代
// #define DTC_MD_STOP 0b11   // 禁止(通常不使用)

// 块/重复区选择(MRB.DTS)--"谁是块/重复区域"
#define DTC_AREA_DST  0      // 目的地址为块/重复区(DTS=0)
#define DTC_AREA_SRC  1      // 源地址为块/重复区(DTS=1)

// 中断发送时机(MRB.DISEL)
#define DTC_IRQ_ON_END   0   // 只在"完成指定量"时发一次中断(Normal: CRA→0;Block: 最后一个块完成;Repeat: 最后一次)
#define DTC_IRQ_EVERY    1   // 每完成 1 个 unit 就发中断(DTC 不会因此暂停搬运;CPU 看到时可能已搬完)

// 链式(MRB.CHNE/CHNS)
#define DTC_CHAIN_OFF    0   // CHNE=0:不链式
#define DTC_CHAIN_ON     1   // CHNE=1:链式,CHNS=0/1 决定链的时机(见 MRB 注释)

// ====================== 位域定义(附带"干就完了"的注释) ======================
typedef struct {
    // [1:0] 预留:必须写 0.DTC 会按字节读取此字节,非 0 可能触发未定义行为/异常.
    uint8_t _res0 : 2;

    // 源地址模式 SM[3:2]:决定 SAR 在每次"unit 传输"后的步进方式(步长由 SZ 决定:1/2/4)
    // - DTC_AM_FIX: 源地址固定(常见于写同一个寄存器/常数表到一片内存)
    // - DTC_AM_DEC: 源地址自减
    // - DTC_AM_INC: 源地址自增(典型的内存拷贝/读流)
    uint8_t SM    : 2;  // MRA[3:2]

    // 数据宽度 SZ[5:4]:一次"unit"搬多少字节.也决定了地址步长与对齐要求.
    // - DTC_SZ_8BIT : 1B;  SAR/DAR 任意对齐
    // - DTC_SZ_16BIT: 2B;  SAR/DAR 必须 2B 对齐
    // - DTC_SZ_32BIT: 4B;  SAR/DAR 必须 4B 对齐
    // 选择不当会导致硬件对齐错误(搬运失败/异常)
    uint8_t SZ    : 2;  // MRA[5:4]

    // 传输模式 MD[7:6]:决定 CRA/CRB 的意义,一次激活搬多少.
    // - DTC_MD_NORMAL: 一次激活只搬 1 个 unit;CRA=本轮 unit 次数(常用 CRA=1;想让 1 次激活搬 N 笔,就设 CRA=N,但这需要 N 次触发)
    // - DTC_MD_REPEAT: 重复传输;由 MRB.DTS 指定"重复区域"(SRC/DST).每次 unit 后,重复区地址按规则回卷/保持;CRA/CRB 组合定义重复次数
    // - DTC_MD_BLOCK : 块传输;一次激活会连续搬完 1 个"块"(CRA 个 unit),并按 CRB 指定的"块数"迭代
    // - 0b11: 停止/禁止(不要用)
    uint8_t MD    : 2;  // MRA[7:6]
} DTC_MRA_t;

typedef struct {
    // [1:0] 预留:必须写 0.
    uint8_t _res1 : 2;

    // 目的地址模式 DM[3:2]:同 SM,只是作用于 DAR.
    // - DTC_AM_FIX: 目的地址固定(典型:把多个 unit 写到同一寄存器地址)
    // - DTC_AM_DEC: 目的地址自减
    // - DTC_AM_INC: 目的地址自增(典型的内存拷贝/写流)
    uint8_t DM    : 2;  // MRB[3:2]

    // 块/重复区选择 DTS[4]:
    // - 在 BLOCK 模式:选择"谁是块区域"(谁在一个块内随 unit 变化,块结束时再回卷).0=目的;1=源.
    // - 在 REPEAT 模式:选择"谁是重复区域"(谁在每次 unit 结束后回卷/保持).0=目的;1=源.
    // - 在 NORMAL 模式:无实际意义,可置 0.
    uint8_t DTS   : 1;  // MRB[4]

    // 中断发送时机 DISEL[5]:
    // - 0:只在"完成指定量"时发一次(Normal: CRA 到 0;Block: 最后一个块完成;Repeat: 最后一次)
    // - 1:每完成 1 个 unit 就给 CPU 发中断请求(DTC 不暂停继续搬,CPU 可能看到的是"已搬完"的状态)
    //   是否能进到 CPU,要看 NVIC 以及你是否把该事件(或 DTC_COMPLETE)路由到某个 IRQ.
    uint8_t DISEL : 1;  // MRB[5]

    // 链式选择 CHNS[6](在 CHNE=1 时有效):
    // - 0:链式"立即/连续"--当前 TI 完成后,直接取下一条 TI 执行
    // - 1:链式"在指定次数完成时"才切到下一条(如 CRA 或 CRB 计数到 0)
    // 不同芯片文档对"指定次数"的描述略有差异,但大体是"到达模式定义的终点时再切换".
    uint8_t CHNS  : 1;  // MRB[6]

    // 链式使能 CHNE[7]:
    // - 0:不链式(只执行当前这条 TI 就结束)
    // - 1:启用链式;下一条 TI 的地址 = 当前 TI 起始地址 + 16 字节(TI 固定 16B),
    //      直到遇到某条 TI 的 CHNE=0(链尾)或达到链式终止条件(结合 CHNS).
    uint8_t CHNE  : 1;  // MRB[7]
} DTC_MRB_t;

typedef struct __attribute__((packed)) {
    // 0x00-0x01: 保留.必须写 0.DTC 会把这 16 字节按既定偏移解析,非 0 可能导致解析异常.
    uint8_t       _resv00;         // 0x00 = 0
    uint8_t       _resv01;         // 0x01 = 0

    // 0x02: MRB(见上).建议:初始化前把整个 TI 清零,再逐位设置,避免保留位污染.
    DTC_MRB_t     MRB;             // 0x02

    // 0x03: MRA(见上).
    DTC_MRA_t     MRA;             // 0x03

    // 0x04-0x07: SAR(Source Address Register)
    // - 源起始地址.对齐要求由 SZ 决定(SZ=16/32bit 时至少 2/4 字节对齐).
    // - 在 INC/DEC 模式下会随每个 unit 传输按步长变化;在 FIX 模式下保持不变.
    // - 在 BLOCK/REPEAT 且 DTS=1(源为块/重复区)时,块/重复结束会按规则回卷/保持.
    void const  * SAR;             // 0x04-0x07

    // 0x08-0x0B: DAR(Destination Address Register)
    // - 目的起始地址.对齐要求同上.
    // - INC/DEC/FIX 语义同 SM,只是作用于目的端.
    // - 在 BLOCK/REPEAT 且 DTS=0(目的为块/重复区)时,块/重复结束会回卷/保持.
    void        * DAR;             // 0x08-0x0B

    // 0x0C-0x0D: CRB(Counter B)
    // - 在 BLOCK 模式:CRB = "块计数"(要传多少块);每块大小=CRA(unit 数).
    // - 在 REPEAT 模式:CRB = "重复次数/外层计数"(DTS 指定哪一侧为重复区);到 0 结束.
    // - 在 NORMAL 模式:通常忽略/置 0.
    uint16_t      CRB;             // 0x0C-0x0D

    // 0x0E-0x0F: CRA(Counter A)
    // - 在 NORMAL 模式:CRA = "本轮 unit 次数".一次激活搬 1 个 unit;若想 1 次激活搬 N 个 unit,需要外设触发 N 次.
    // - 在 BLOCK 模式:CRA = "块大小"(unit 数).一次激活会连续搬完 CRA 个 unit;配合 CRB 决定总量.
    // - 在 REPEAT 模式:CRA = "每次激活的 unit 次数"(内层);配合 CRB 与 DTS 实现重复语义.
    // - 计数为 0 的行为:一般视为"未定义/非法配置",请至少设为 1.
    uint16_t      CRA;             // 0x0E-0x0F
} DTC_Descriptor_t;


static uint32_t src[4] = {0x11223344, 0x55667788, 0xAABBCCDD, 0x01234567};
static uint32_t dst[40] = {0};

__attribute__((aligned(1024)))           // DTCVBR 要求 1KB 对齐
static uint32_t dtc_vector_table[32];    // 32 个 IRQ 通道,这里用GPT OVF溢出,刚好是事件0.

static DTC_Descriptor_t ti_irq0;         // 用于 IELSR[0] 的 TI

void dtc_init_for_gpt3_ovf(void)
{
    ti_irq0.MRA.MD = DTC_MD_BLOCK;
    ti_irq0.MRA.SZ = DTC_SZ_32BIT;
    ti_irq0.MRA.SM = DTC_AM_INC;
    ti_irq0.MRB.DM = DTC_AM_INC;
    ti_irq0.MRB.DTS = DTC_AREA_SRC;
    ti_irq0.MRB.DISEL = DTC_IRQ_ON_END;
    ti_irq0.MRB.CHNE = 0; // 链式使能(1=开,0=关)
    ti_irq0.MRB.CHNS = 0; // 配合CHNE,查看注释.

    ti_irq0.SAR = src;
    ti_irq0.DAR = dst;
    ti_irq0.CRB = 10;
    ti_irq0.CRA = (4u << 8) | 4u; // 0x0404 Block模式下,CRA的高8,低8要一样.

    dtc_vector_table[0] = (uint32_t)&ti_irq0;  // 表项是 TI 的地址

    R_DTC->DTCST = 0;                 // 停止 DTC
    R_DTC->DTCCR_b.RRS = 0;           // 允许更新向量表/TI 后再置 1
    R_DTC->DTCVBR = (uint32_t)dtc_vector_table; // 1KB 对齐基址
    R_DTC->DTCCR_b.RRS = 1;           // 运行中可读 TI
    R_DTC->DTCST = 1;                 // 启动 DTC(模块开始工作)

    R_ICU->IELSR_b[0].DTCE = 0;       // 先关 DTC 触发
    R_ICU->IELSR_b[0].IR   = 0;       // 清 IR:注意按手册要求,清 IR 之前 DTCE 必须为 0
    R_ICU->IELSR_b[0].IELS = 0x1A;    // GPT3_OVF (Group0 的编码)
    R_ICU->IELSR_b[0].DTCE = 1;       // 允许该事件触发 DTC

    // 如需让 CPU 中断在传输后也进来:开 NVIC;若不想进 CPU,MRB.DISEL=0 且不使能 NVIC 即可

    NVIC_SetPriority(0, 3);
    NVIC_EnableIRQ(0);
}

// ===== GPT3 配置:跟你写的差不多 =====
void gpt3_start_ovf(void)
{
    R_GPT3->GTWP = (0xA5u << 8) | 0u;
    R_GPT0->GTSTP_b.CSTOP3 = 1;
    R_GPT0->GTCLR_b.CCLR3  = 1;

    R_GPT3->GTCR_b.MD   = 0b000;
    R_GPT3->GTCR_b.TPCS = 0b101;         // PCLKD/1024
    R_GPT3->GTPR        = 10000u - 1;    // 10k 计数一次溢出
    R_GPT3->GTSSR_b.CSTRT = 1;
    R_GPT3->GTSTR_b.CSTRT3 = 1;
    R_GPT3->GTWP = (0xA5u << 8) | 1u;

    // 注意:这里**不用**配置 ELC 的 ELSRn=0x5C;我们这条链路是 ICU→DTC,不走 ELC
}

// 可选:传输后产生 CPU 中断(MRB.DISEL=1 时)
void __attribute__((interrupt)) IRQ0_Handler(void)
{
    R_ICU->IELSR_b[0].IR = 0; // DTCE自动清零,之后的中断是正常的OVF中断!

    uint32_t bit = (1u << 5);
    if (R_PORT4->PCNTR1_b.PODR & bit) {
        R_PORT4->PCNTR3_b.PORR = bit;
    } else
    {
        R_PORT4->PCNTR3_b.POSR = bit;
    }
}

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注