恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

大型量产固件的工程实践(九):GPIO 模拟 SPI 驱动设计

  • 首页
  • 资讯中心
  • /
  • 大型量产固件的工程实践(九):GPIO 模拟 SPI 驱动设计

相关资讯

基于VScode搭建GD32开发环境 2026/8/26 16:27:16
autocad 2020 模型窗口输出布局窗口 2026/8/26 16:27:16
LS555 无锡曜硅|DIP8 国产 555 时基电路,定时器、振荡器、PWM 发生硬件设计 2026/8/26 16:22:16

最新资讯

C++模板编程:从泛型基础到高级实战技巧
K3/GLM5.2抢不到?四条替代路线与API接入实操指南
从褪黑素到ESP32:自制日出闹钟的完整技术指南
VOC+YOLO格式玻璃瓶塑料瓶检测数据集:8943张图片实操指南
HTML5 Canvas游戏开发实战:从零复刻经典《超级马里奥》
五线谱是音高坐标系:线与间的空间逻辑

今日推荐

Python random 模块常用函数详解:从入门到实战
Hermes接入团队协作后,我推翻了三个效率假设
免费AI大模型调教指南:打造专属网文写作助手

本周热门

Nextcloud 桌面客户端:把同步交给它,你只管改文件
如何将 HTML 转成 Word 文档且格式不丢失?html-to-docx 使用教程
Anki 批量操作卡片完整指南:一次搞定上千张,不再逐张修改

本月精选

如何用DamaiHelper实现演唱会门票的智能自动化抢购:完整技术解决方案指南
第4篇:59 倍性能差距的索引瓶颈定位——一次教科书级的全表扫描调优
终极歌词批量下载神器:5分钟解决离线音乐库歌词同步难题

大型量产固件的工程实践(九):GPIO 模拟 SPI 驱动设计

发布时间:2026/8/26 16:27:16
大型量产固件的工程实践(九):GPIO 模拟 SPI 驱动设计 大型量产固件的工程实践九GPIO 模拟 SPI 驱动设计本文是《大型量产固件的工程实践》专栏第 9 篇。上一篇第 8 篇《多款同类型芯片驱动的统一抽象》 下一篇第 9 篇实战《GPIO 模拟 SPI 实现源码与使用范例》一、为什么需要 GPIO 模拟 SPISPI 是嵌入式最常用的外设总线之一。但很多场景下硬件 SPI 不够用硬件 SPI 通道被占用或干脆没有多余通道非字节对齐地址某些芯片的寄存器地址是 9-bit、10-bit而硬件 SPI 通常只支持 8-bit 的倍数时序特殊需要自定义 CPOL/CPHA、时钟极慢、或插入特定延时。这时就要用GPIO 位绑bit-bang模拟 SPI——用普通 GPIO 引脚按 SPI 时序手动翻转时钟线、读写数据线。本文给出一个通用、可复用的 bit-bang SPI 驱动设计核心亮点是任意位宽传输 CPOL/CPHA 四套时序 驱动/协议分层。二、设计总览驱动层与协议层分离一个关键设计原则驱动层只做纯 SPI 位绑收发不掺任何具体芯片的协议。协议层每个芯片一套 驱动层通用只此一套 ┌──────────────────────┐ ┌──────────────────────────┐ │ 组装芯片特定命令格式 │ │ 只负责时钟线 数据线翻转 │ │ 如R/W 标志 地址编码│ ──▶ │ 不关心地址是 9-bit 还是 │ │ 决定先发哪些 bit │ │ 16-bit也不关心 R/W 语义 │ └──────────────────────┘ └──────────────────────────┘这样换一颗芯片只需改协议层的命令组装驱动层零改动。三、通用 transfer 接口任意位宽驱动层对外只暴露一个传输接口发送和接收的位宽可以不同/* 发送 txBitLen 位接收 rxBitLen 位可只发不收或只收不发 */voidhwspi_transfer(constuint8_t*tx_buf,uint8_ttx_bit_len,uint8_t*rx_buf,uint8_trx_bit_len);tx_bit_len/rx_bit_len支持1~32 bit 任意值不受字节对齐限制内部把位拆成完整字节 剩余 bit两段处理。/* 发送先发完整字节再发剩余 bit */full_bytestx_bit_len3;for(i0;ifull_bytes;i)for(bit0;bit8;bit)send_one_bit(...);remain_bitstx_bit_len0x07;for(i0;iremain_bits;i)send_one_bit(...);这就是非字节对齐地址能直接传输的关键。四、CPOL / CPHA 四套时序SPI 有 4 种模式CPOL×CPHA它们的时钟相位和采样边沿不同。设计上用4 个static inline函数分别实现传输前用函数指针分派选定避免在每个 bit 里做分支判断/* 输出 bit 的函数指针类型 */typedefvoid(*out_bit_fn)(constuint8_t*buf,uint8_tbyte,uint8_tbit,uint8_tlsb,bool sck_active,bool sck_idle,uint8_tdly);4.1 CPHA0数据在第一个边沿变化在第二个边沿采样/* 输出SCK 变 active 时换 MOSI变 idle 前数据保持 */staticinlinevoidout_bit_cpha0(...){gpio_write(SCK,sck_active);// 时钟翻转gpio_write(MOSI,bit_value(buf,...));// 同时送出数据delay_us(dly);gpio_write(SCK,sck_idle);// 时钟回落delay_us(dly);}/* 输入SCK 变 active 后采样 MISO */staticinlinevoidin_bit_cpha0(...){gpio_write(SCK,sck_active);if(gpio_read(MISO))bit_set(buf,...);// 采样delay_us(dly);gpio_write(SCK,sck_idle);delay_us(dly);}4.2 CPHA1数据在第一个边沿采样在第二个边沿变化/* 输出先换 MOSI再翻转 SCK */staticinlinevoidout_bit_cpha1(...){gpio_write(MOSI,bit_value(buf,...));// 先送数据gpio_write(SCK,sck_active);delay_us(dly);gpio_write(SCK,sck_idle);// 采样发生在回落沿delay_us(dly);}/* 输入SCK 回落沿采样 */staticinlinevoidin_bit_cpha1(...){gpio_write(SCK,sck_active);delay_us(dly);gpio_write(SCK,sck_idle);// 回落沿if(gpio_read(MISO))bit_set(buf,...);// 此时采样delay_us(dly);}4.3 函数指针分派传输开始时根据 CPHA 选一次函数指针之后每个 bit 都走同一个函数out_bit_fn out_bitcpha?out_bit_cpha1:out_bit_cpha0;收益static inline保证零函数调用开销函数指针分派保证循环体里没有 per-bit 的 if 分支——这对 bit-bang 这种每个 bit 都要快的场景很重要。五、关键细节5.1 CPOL 只影响空闲电平CPOL 决定时钟空闲时是高还是低但活动电平始终是空闲电平的取反可以从空闲电平推导sck_activesck_idle^1;// 活动电平 空闲电平取反所以 4 个时序函数只需关心active/idle 两个电平CPOL 通过把哪一档当成空闲来体现。5.2 LSB/MSB 可配置bit 的提取/写入方向可配/* lsb_first1 从最低位开始0 从最高位开始 */uint8_tbit_value(constuint8_t*buf,uint8_tbyte,uint8_tbit,uint8_tlsb_first){return(buf[byte](lsb_first?bit:(7-bit)))0x01;}5.3 传输流程voidhwspi_transfer(constuint8_t*tx_buf,uint8_ttx_bit_len,uint8_t*rx_buf,uint8_trx_bit_len){gpio_write(CS,active);// 拉低片选进入传输gpio_write(SCK,sck_idle);// 确保时钟在空闲电平if(tx_bit_len)tx_bits(tx_buf,tx_bit_len,...);// 发送阶段if(rx_bit_len){// 接收阶段memset(rx_buf,0,(rx_bit_len7)3);rx_bits(rx_buf,rx_bit_len,...);}gpio_write(CS,idle);// 拉高片选结束传输gpio_write(SCK,sck_idle);// 时钟恢复空闲}六、使用范例协议层组装驱动层只管收发 bit协议层负责把芯片命令组装成要发的 bit。以一颗16-bit 地址 8-bit 数据、读操作最高位置 1的芯片为例/* 协议层读寄存器 —— 组装命令然后调通用 transfer */uint16_tsensor_read_reg(uint16_taddr){uint8_ttx[3];uint8_trx[1]{0};/* 16-bit 地址读标志(R/W1)放在最高位 */uint16_tcmdaddr|0x8000;tx[0](uint8_t)(cmd8);// 地址高字节tx[1](uint8_t)(cmd);// 地址低字节/* 发 16-bit 命令收 8-bit 数据 */hwspi_transfer(tx,16,rx,8);returnrx[0];}注意R/W 标志、地址位宽、字节序这些芯片特有的东西全在协议层处理驱动层始终是那个通用的hwspi_transfer。七、小结一个通用、可复用的 bit-bang SPI 驱动四个设计要点要点说明驱动/协议分离驱动只做位绑协议组装命令换芯片零改驱动任意位宽transfer支持 1~32 bit非字节对齐地址直接传四套时序CPOL/CPHA 用 4 个static inline 函数指针分派循环体无分支电平推导CPOL 只定空闲电平活动电平取反推导这套设计最大的价值在于通用性——它不绑定任何具体芯片任何要按位精确控制时序的外设都能复用。下一篇我们把视角从位级驱动上升到状态级设计聊聊嵌入式里无处不在的状态机。本文仅介绍 GPIO 模拟 SPI 的通用驱动设计方法不涉及任何具体项目或产品的私有实现。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号