CIS扫描传感器驱动
·
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 返回
*/
}
更多推荐
所有评论(0)