SPI 设备驱动开发

发布时间:2026/7/10 5:14:11
SPI 设备驱动开发 SPI 设备驱动开发SPI 是另一种常用的串行外设总线速度比 I2C 快适合高速传输。这篇讲 Linux SPI 子系统架构、spi_driver 框架、四种 SPI 模式、读写传输 API以及完整的 SPI 设备驱动示例。大家好我是黒漂技术佬。I2C 简单但速度慢SPI 速度快几十 MHz 很常见适合高速传输的场景比如显示屏、Flash、高速 ADC、OLED 屏。SPI 是四线制MOSI、MISO、SCLK、CS全双工没有地址概念靠片选信号选设备。这篇讲 SPI 驱动怎么写SPI 模式、spi_driver 框架、设备树配置、读写传输。一、SPI 总线简介四线制线名称方向说明SCLK串行时钟主机→从机时钟信号MOSI主发从收主机→从机主机输出从机输入MISO主收从发从机→主机从机输出主机输入CS / SS片选主机→从机低电平选中设备每个从机一根独立的 CS 线同一时间只有一个 CS 有效选中一个设备全双工发数据的同时也收数据四种模式CPOL CPHASPI 有时钟极性和相位两个参数组合成四种模式模式CPOL时钟极性CPHA时钟相位说明Mode 00空闲低0第一个边沿采样最常用Mode 10空闲低1第二个边沿采样Mode 21空闲高0第一个边沿采样Mode 31空闲高1第二个边沿采样也比较常用CPOL空闲时 SCLK 的电平。0低1高CPHA在第几个时钟沿采样数据。0第一个沿1第二个沿驱动里要跟从设备的 datasheet 对应上模式不对读出来的数据全是错的。速度SPI 速度比 I2C 快很多常见 10MHz、20MHz有的能到 50MHz 以上。二、Linux SPI 子系统架构跟 I2C 类似三层结构SPI 设备驱动我们写的 ↓ SPI 核心spi-core ↓ SPI 主机驱动控制器驱动SoC厂商提供spi_masterSPI 主机控制器对应 SoC 的 SPI 外设。spi_device一个 SPI 从设备包含片选、速度、模式等信息。spi_driverSPI 设备驱动我们写的部分。三、SPI 驱动框架spi_driver 结构体#includelinux/spi/spi.hstaticintmy_probe(structspi_device*spi){// 初始化return0;}staticvoidmy_remove(structspi_device*spi){// 清理}staticconststructof_device_idmy_of_match[]{{.compatiblevendor,my-spi-device},{}};MODULE_DEVICE_TABLE(of,my_of_match);staticstructspi_drivermy_driver{.probemy_probe,.removemy_remove,.driver{.namemy-spi-device,.of_match_tablemy_of_match,},};module_spi_driver(my_driver);module_spi_driver 宏自动注册注销省得写 module_init/exit。spi_deviceprobe 的参数代表一个 SPI 从设备structspi_device{structdevicedev;structspi_master*master;u32 max_speed_hz;// 最大时钟频率u8 chip_select;// 片选号u8 mode;// SPI 模式CPOL|CPHA 等u8 bits_per_word;// 每字位数一般8// ...};常用的 mode 标志SPI_MODE_0~SPI_MODE_3四种模式SPI_CS_HIGH片选高电平有效默认低有效SPI_LSB_FIRST低位先发默认高位先发SPI_3WIRE三线模式MOSI/MISO 共用一根线四、设备树中的 SPI 设备SPI 控制器节点spi0: spi20000000 { compatible vendor,spi-controller; reg 0x20000000 0x1000; #address-cells 1; #size-cells 0; status okay; };SPI 设备子节点spi0 { flash0 { compatible vendor,spi-flash; reg 0; // 片选号第几个CS spi-max-frequency 50000000; // 最大频率 50MHz spi-cpol; // 时钟极性CPOL1 spi-cpha; // 时钟相位CPHA1→ Mode3 status okay; }; lcd1 { compatible vendor,lcd; reg 1; // 第二个片选 spi-max-frequency 10000000; // 没写 spi-cpol/cpha 就是 Mode 0 dc-gpios gpio0 3 GPIO_ACTIVE_HIGH; reset-gpios gpio0 4 GPIO_ACTIVE_LOW; }; };常用属性属性说明reg片选号第几个 CSspi-max-frequency最大时钟频率Hzspi-cpolCPOL1空闲时钟高spi-cphaCPHA1第二个沿采样spi-cs-high片选高电平有效spi-lsb-first低位先发spi-3wire三线模式两个都不写就是 Mode 0都写就是 Mode 3。片选 GPIO有些 SPI 控制器的片选用 GPIO 模拟设备树里配置spi0 { cs-gpios gpio0 5 GPIO_ACTIVE_LOW, gpio0 6 GPIO_ACTIVE_LOW; };五、SPI 读写传输 API1. 最简单的读写spi_write / spi_read// 写intspi_write(structspi_device*spi,constvoid*buf,size_tlen);// 读先发0收数据intspi_read(structspi_device*spi,void*buf,size_tlen);2. 写读spi_write_then_read先写一段再读一段最常用的模式发寄存器地址读数据intspi_write_then_read(structspi_device*spi,constvoid*txbuf,unsignedn_tx,void*rxbuf,unsignedn_rx);先发送 n_tx 字节 txbuf紧接着接收 n_rx 字节到 rxbuf片选一直保持有效传感器、寄存器型设备常用这个。3. 通用传输spi_sync最灵活的方式构造 spi_message#includelinux/spi/spi.hstructspi_transfert{.tx_buftx_buf,.rx_bufrx_buf,.lenlen,.speed_hz10000000,// 可以单独指定这次的速度};structspi_messagemsg;spi_message_init(msg);spi_message_add_tail(t,msg);intretspi_sync(spi,msg);多个 transfer 组合一次消息可以包含多个 transfer片选保持structspi_transfert1{.tx_bufcmd,.len1};structspi_transfert2{.rx_bufdata,.len4};spi_message_init(msg);spi_message_add_tail(t1,msg);spi_message_add_tail(t2,msg);spi_sync(spi,msg);spi_transfer 常用字段字段说明tx_buf发送缓冲区rx_buf接收缓冲区len传输长度speed_hz本次速度可选默认用设备的bits_per_word每字位数delay_usecs传输后延时cs_change传输后是否改变片选4. 异步传输spi_async异步版本不阻塞完成后回调intspi_async(structspi_device*spi,structspi_message*msg);msg 里设置 complete 回调函数。一般驱动用 spi_sync 同步的就够了。六、封装寄存器读写跟 I2C 类似SPI 设备一般也封装成读写寄存器的函数// 写寄存器cmd datastaticintreg_write(structspi_device*spi,u8 reg,u8 val){u8 buf[2]{reg,val};returnspi_write(spi,buf,sizeof(buf));}// 读寄存器先发reg再读datastaticintreg_read(structspi_device*spi,u8 reg,u8*val){u8 cmdreg|0x80;// 假设读标志是最高位returnspi_write_then_read(spi,cmd,1,val,1);}// 连续读多个staticintreg_read_bulk(structspi_device*spi,u8 reg,u8*buf,intlen){u8 cmdreg|0x80;returnspi_write_then_read(spi,cmd,1,buf,len);}具体的命令格式要看设备 datasheet每个芯片不一样。七、完整示例SPI 传感器驱动设备树spi0 { sensor0 { compatible vendor,spi-sensor; reg 0; spi-max-frequency 10000000; status okay; }; };驱动代码#includelinux/module.h#includelinux/spi/spi.h#includelinux/of.h#defineREG_ID0x00#defineREG_DATA0x01#defineREG_CTRL0x02#defineREAD_FLAG0x80structsensor_data{structspi_device*spi;// ...};staticintreg_read(structspi_device*spi,u8 reg,u8*val){u8 cmdreg|READ_FLAG;returnspi_write_then_read(spi,cmd,1,val,1);}staticintreg_write(structspi_device*spi,u8 reg,u8 val){u8 buf[2]{reg,val};returnspi_write(spi,buf,2);}staticintsensor_read_data(structsensor_data*data,u16*result){u8 buf[2];u8 cmdREG_DATA|READ_FLAG;intret;retspi_write_then_read(data-spi,cmd,1,buf,2);if(ret)returnret;*result(buf[0]8)|buf[1];return0;}staticintsensor_probe(structspi_device*spi){structsensor_data*data;u8 id;intret;datadevm_kzalloc(spi-dev,sizeof(*data),GFP_KERNEL);if(!data)return-ENOMEM;data-spispi;spi_set_drvdata(spi,data);// 设置 SPI 模式设备树也可以配这里也可以再设置spi-modeSPI_MODE_0;spi-bits_per_word8;retspi_setup(spi);if(ret)returnret;// 读 ID 寄存器验证芯片retreg_read(spi,REG_ID,id);if(ret){dev_err(spi-dev,读取ID失败\n);returnret;}dev_info(spi-dev,传感器 ID: 0x%02x\n,id);// 初始化配置reg_write(spi,REG_CTRL,0x01);return0;}staticvoidsensor_remove(structspi_device*spi){// 清理}staticconststructof_device_idsensor_of_match[]{{.compatiblevendor,spi-sensor},{}};MODULE_DEVICE_TABLE(of,sensor_of_match);staticstructspi_driversensor_driver{.probesensor_probe,.removesensor_remove,.driver{.namespi-sensor,.of_match_tablesensor_of_match,},};module_spi_driver(sensor_driver);MODULE_LICENSE(GPL);MODULE_AUTHOR(Heipiao);MODULE_DESCRIPTION(SPI 传感器驱动示例);八、SPI 驱动和 I2C 驱动对比I2CSPI线数2根SCLSDA4根SCLKMOSIMISOCS寻址7/10位地址片选线每设备一根速度100k/400k几M到几十M全双工半双工全双工多设备地址区分总线共用每设备独立CS驱动结构i2c_driver / i2c_clientspi_driver / spi_device常用读写i2c_smbus_*spi_write_then_read整体框架很像就是 API 不一样。会写 I2C 驱动SPI 也差不多。九、常见坑坑 1SPI 模式不对CPOL/CPHA 跟设备不匹配读出来全是乱码。仔细看 datasheet确认模式。坑 2速度太快设的频率超过设备支持的最大值通信不稳定。从低速开始调慢慢往上加。坑 3片选极性搞反默认低电平有效设备是高有效就加 spi-cs-high 属性。坑 4读命令格式不对不同芯片读命令不一样有的是最高位设1有的是地址0x80有的先发dummy字节。严格按 datasheet 来。坑 5DMA 对齐问题用 spi_sync 的时候tx_buf/rx_buf 最好用 DMA 安全的内存dma_alloc 或者 kmalloc 对齐的不然可能出问题。坑 6CS 脚没配对设备树 reg 是片选号不是 GPIO 号。cs-gpios 里第几个对应 reg 是几。十、本篇小结SPI四线制全双工串行总线速度快靠片选选设备四种模式CPOL时钟极性 CPHA时钟相位组合Mode 0 最常用Linux SPI 子系统spi_master 主机、spi_device 设备、spi_driver 驱动设备树SPI 设备作为子节点reg 是片选号spi-max-frequency 设速度spi-cpol/cpha 设模式常用 APIspi_write、spi_read、spi_write_then_read最常用、spi_sync灵活spi_message spi_transfer 可以组合多次传输片选保持驱动框架跟 I2C 很像probe/remove of_match_table module_spi_driver常见问题模式不对、速度太快、片选极性、命令格式错误下一篇是系列收尾实战篇传感器驱动从 0 到 1把前面讲的字符设备、中断、定时器、I2C/SPI 串起来写一个完整的传感器驱动。我是黒漂技术佬。