C语言描述分析时序,结合手册

/*
 * 说明:这里用 nop_delay() 模拟汇编里的多条 NOP。
 * 真实项目里如果对时序极其敏感,建议改成定时器或 DSB/ISB 组合。
 */
static inline void nop_delay(int count) {
    while (count--) {
        __asm volatile ("nop");
    }
}

/*
 * CIS_Scan
 * 作用:
 * 1) 产生 CIS 的 SP/CP/MCLK/VSMP 等时序
 * 2) 驱动 LED 通道
 * 3) 从并口/ADC 读取三路通道数据并写入缓冲区
 *
 * 参数:
 * buff      - 数据缓冲区首地址
 * channel   - LED/通道选择位(具体位含义由硬件定义)
 * led_delay - 预留参数(原汇编未使用)
 * port_addr - LED 控制端口偏移(相对 AHB1 基址)
 */
void CIS_Scan(uint8_t *buff, uint8_t channel, uint16_t led_delay, uint32_t port_addr)
{
    (void)led_delay; // 该参数在汇编里未使用,这里明确忽略

    /* AHB1 基地址(STM32 常见:0x40020000,实际以芯片为准) */
    volatile uint32_t *ahb1 = (volatile uint32_t *)0x40020000u;

    /*
     * 0x0414: GPIOB_ODR 寄存器偏移
     * 用于控制 PB 引脚电平(CP/SP/RS/MCLK/VSMP 等信号都挂在 GPIOB)
     */
    volatile uint32_t *GPIOB_ODR = (volatile uint32_t *)((uint8_t *)ahb1 + 0x0414u);

    /*
     * 0x0C10: GPIOD_IDR 寄存器偏移
     * 用于读取 PD0-15 输入(并口/ADC 输出数据)
     */
    volatile uint32_t *GPIOD_IDR = (volatile uint32_t *)((uint8_t *)ahb1 + 0x0C10u);

    /* 外部可调的时钟循环次数 */
    extern uint8_t scan_dpi_sp_clk;     // SP 阶段循环次数
    extern uint8_t scan_cp_clk_fort;    // 前置 PCLK 次数
    extern uint8_t scan_cp_clk_offset;  // OFFSET 校正阶段次数

    /* =========================
     * 1) SP 时序阶段
     * =========================
     * 该阶段用于启动/同步 CIS,产生 SP、CP、RS 等起始时序
     */
    for (uint8_t i = scan_dpi_sp_clk; i > 0; --i) {
        /*
         * 0x0118:
         * PB4=CP=1, PB3=SP=1, PB9=RS=1 (示例板子注释)
         * 这一步让 SP/CP/RS 进入高电平组合,开始一轮时序
         */
        *GPIOB_ODR = 0x0118u;
        nop_delay(7); // 延时保持高电平宽度

        /*
         * 0x0108:
         * RS/SP=1, CP=0
         * 将 CP 拉低,形成 SP/CP 的边沿
         */
        *GPIOB_ODR = 0x0108u;
        nop_delay(4); // 低电平保持时间
    }

    /*
     * 清零 CP/SP/RS
     * 防止后续阶段误触发
     */
    *GPIOB_ODR = 0x0000u;
    nop_delay(2);

    /* =========================
     * 2) LED 开灯(通道选择)
     * =========================
     * port_addr 指定 LED 控制端口偏移。
     * 如果 port_addr == GPIOB_ODR,直接在 GPIOB 上合并 channel 位。
     * 否则写到其它端口(如 GPIOA/GPIOC)。
     */
    if (port_addr == 0x0414u) {
        /*
         * 0x0010: PB4=CP=1
         * OR channel: 打开指定 LED 通道
         */
        *GPIOB_ODR = (0x0010u | channel);
    } else {
        /*
         * 直接向其它 GPIO 端口写 channel
         */
        *(volatile uint32_t *)((uint8_t *)ahb1 + port_addr) = channel;
    }

    /* =========================
     * 3) 前置 PCLK 产生
     * =========================
     * 产生一定次数的 CP 时序,让 CIS/ADC 进入稳定工作状态
     */
    for (uint8_t i = scan_cp_clk_fort; i > 0; --i) {
        *GPIOB_ODR = 0x0010u; // CP=1
        nop_delay(7);

        *GPIOB_ODR = 0x0000u; // CP=0
        *(volatile uint32_t *)((uint8_t *)ahb1 + port_addr) = channel; // 维持 LED 通道
        nop_delay(5);
    }

    /* =========================
     * 4) 关闭 LED
     * =========================
     * 0x00010000 通常是 BSRR 形式的复位位
     */
    *(volatile uint32_t *)((uint8_t *)ahb1 + port_addr) = 0x00010000u;

    /* =========================
     * 5) OFFSET 校正阶段
     * =========================
     * 通过特定 ADCCLK/VSMP/CP 组合,采集偏移值并稳定传感器
     */
    for (uint8_t i = scan_cp_clk_offset; i > 0; --i) {
        /*
         * 0x0040:
         * PB7=MCLK=0, PB6=VSMP=1, PB4=CP=0
         * 先拉低 MCLK、打开采样保持 VSMP
         */
        *GPIOB_ODR = 0x0040u;
        nop_delay(12);

        /*
         * 0x0090:
         * PB7=MCLK=1, PB6=VSMP=0, PB4=CP=1
         * 上升沿采样
         */
        *GPIOB_ODR = 0x0090u;
        nop_delay(4);

        /*
         * 0x0010:
         * PB7=MCLK=0, PB6=VSMP=0, PB4=CP=1
         * 结束采样周期
         */
        *GPIOB_ODR = 0x0010u;
        nop_delay(8);

        /* 读通道1:从 PD8-15 取 8 位数据 */
        buff[0] = (uint8_t)((*GPIOD_IDR) >> 8);

        /* 同样的节拍,读通道2 */
        *GPIOB_ODR = 0x0090u; nop_delay(4);
        *GPIOB_ODR = 0x0010u; nop_delay(8);
        buff[CISMclk] = (uint8_t)((*GPIOD_IDR) >> 8);

        /* 同样的节拍,读通道3 */
        *GPIOB_ODR = 0x0090u; nop_delay(4);
        *GPIOB_ODR = 0x0010u; nop_delay(8);
        buff[CISMclk2X] = (uint8_t)((*GPIOD_IDR) >> 8);

        /* 间隔延时,给传感器恢复时间 */
        *GPIOB_ODR = 0x0000u;
        nop_delay(10);
    }

    /* =========================
     * 6) 正式采样阶段(864像素点)
     * =========================
     * 每次循环采 3 个通道数据,buf 向前移动 1 字节
     */
    for (uint16_t i = CISMclk; i > 0; --i) {
        *GPIOB_ODR = 0x0040u; nop_delay(12);
        *GPIOB_ODR = 0x0090u; nop_delay(4);
        *GPIOB_ODR = 0x0010u; nop_delay(8);

        /* 通道1 */
        buff[0] = (uint8_t)((*GPIOD_IDR) >> 8);

        *GPIOB_ODR = 0x0090u; nop_delay(4);
        *GPIOB_ODR = 0x0010u; nop_delay(8);

        /* 通道2 */
        buff[CISMclk] = (uint8_t)((*GPIOD_IDR) >> 8);

        *GPIOB_ODR = 0x0090u; nop_delay(4);
        *GPIOB_ODR = 0x0010u; nop_delay(8);

        /* 通道3 */
        buff[CISMclk2X] = (uint8_t)((*GPIOD_IDR) >> 8);

        /* 间隔延时,稳定采样节奏 */
        *GPIOB_ODR = 0x0000u;
        nop_delay(20);

        /* 下一个像素位置 */
        buff += 1;
    }

    /* =========================
     * 7) 完成
     * =========================
     * 原汇编中在此直接 POP/BX 返回
     */
}

更多推荐