当前位置:   article > 正文

Linux驱动开发:SPI子系统_linux spi驱动开发

linux spi驱动开发

1、SPI简介

1.1 四根线

MISO:主设备数据输入,从设备数据输出。
MOSI:主设备数据输出,从设备数据输入。
SCLK:时钟信号,由主设备产生。
CS:    从设备片选信号,由主设备控制。

1.2 四种模式

CPOL(时钟极性) :   0:时钟起始位低电平      1:时钟起始为高电平  
CPHA(时钟相位) :0:第一个时钟周期采样   1:第二个时钟周期采样

1、CPOL=0,CPHA=0:此时空闲态时,SCLK处于低电平,数据采样是在第1个边沿,也就是 SCLK由低电平到高电平的跳变,所以数据采样是在上升沿,数据发送是在下降沿

2、CPOL=0,CPHA=1:此时空闲态时,SCLK处于低电平,数据发送是在第1个边沿,也就是 SCLK由低电平到高电平的跳变,所以数据采样是在下降沿,数据发送是在上升沿

3、CPOL=1,CPHA=0:此时空闲态时,SCLK处于高电平,数据采集是在第1个边沿,也就是 SCLK由高电平到低电平的跳变,所以数据采集是在下降沿,数据发送是在上升沿

4、CPOL=1,CPHA=1:此时空闲态时,SCLK处于高电平,数据发送是在第1个边沿,也就是 SCLK由高电平到低电平的跳变,所以数据采集是在上升沿,数据发送是在下降沿

1.3 SPI数据传输

        

        从图中可以看出,主机和从机都有一个串行移位寄存器,主机通过向它的SPI串行寄存器写入一个字节来发起一次传输。寄存器通过MOSI信号线将字节传送给从机,从机也将自己的移位寄存器中的内容通过MISO信号线返回给主机。这样,两个移位寄存器中的内容就被交换。外设的写操作和读操作是同步完成的。如果只进行写操作,主机只需忽略接收到的字节;反之,若主机要读取从机的一个字节,就必须发送一个空字节来引发从机的传输。

1.4 SPI驱动框架简介

        SPI 驱动框架和 I2C 很类似,都分为主机控制器驱动和设备驱动,主机控制器也就是 SOC 的 SPI 控制器接口。样。和 I2C适配器驱动一样,SPI主机驱动一般都是 SOC 厂商去编写的,所以我们作为 SOC的使用者,这一部分的驱动就不用操心了。

2、SPI设备驱动

2.1 SPI相关API

2.1.1 spi_driver

  1. struct spi_driver {
  2. int (*probe)(struct spi_device *spi);
  3. int (*remove)(struct spi_device *spi);
  4. struct device_driver driver;
  5. };
  6. struct device_driver {
  7. const char *name;
  8. const struct of_device_id *of_match_table;
  9. }

2.1.2 注册:spi_register_driver

  1. #define spi_register_driver(driver)
  2. __spi_register_driver(THIS_MODULE, driver)

2.1.3 注销:spi_unregister_driver

static inline void spi_unregister_driver(struct spi_driver *sdrv)

2.1.4 module_spi_driver:一键注册,不需要以上注册注销的过程

  1. #define module_spi_driver(__spi_driver)
  2. module_driver(__spi_driver, spi_register_driver,
  3. spi_unregister_driver)
  4. #define module_driver(__driver, __register, __unregister, ...)
  5. static int __init __driver##_init(void)
  6. {
  7. return __register(&(__driver) , ##__VA_ARGS__);
  8. }
  9. module_init(__driver##_init);
  10. static void __exit __driver##_exit(void)
  11. {
  12. __unregister(&(__driver) , ##__VA_ARGS__);
  13. }
  14. module_exit(__driver##_exit);
  1. module_spi_driver(myspi);
  2. #define module_spi_driver(myspi)
  3. module_driver(myspi, spi_register_driver,
  4. spi_unregister_driver)
  5. #define module_driver(myspi, spi_register_driver, spi_unregister_driver)
  6. static int __init myspi_init(void)
  7. {
  8. return spi_register_driver(&myspi);
  9. }
  10. static void __exit myspi_exit(void)
  11. {
  12. spi_unregister_driver(&myspi);
  13. }
  14. module_init(myspi_init);
  15. module_exit(myspi_exit);

2.1.5 spi_write

  1. spi_write(struct spi_device *spi, const void *buf, size_t len)
  2. {
  3. struct spi_transfer t = {
  4. .tx_buf = buf,
  5. .len = len,
  6. };
  7. return spi_sync_transfer(spi, &t, 1);
  8. }

2.1.6 spi_read

  1. spi_read(struct spi_device *spi, void *buf, size_t len)
  2. {
  3. struct spi_transfer t = {
  4. .rx_buf = buf,
  5. .len = len,
  6. };
  7. return spi_sync_transfer(spi, &t, 1);
  8. }

2.1.7 spi_write_then_read 

  1. extern int spi_write_then_read(struct spi_device *spi,
  2. const void *txbuf, unsigned n_tx,
  3. void *rxbuf, unsigned n_rx);

2.1.8 以上三种spi传输函数解析,实际接收发送只需spi_write、spi_read

  1. struct spi_transfer {
  2. const void *tx_buf; //用于保存发送的数据
  3. void *rx_buf; //用于保存接收到的数据
  4. unsigned len; //是要进行传输的数据长度, SPI是全双工通信,
  5. //因此在一次通信中发送和接收的字节数都是一样的,
  6. //所以 spi_transfer中也就没有发送长度和接收长度之分。
  7. };
  8. spi_sync_transfer(struct spi_device *spi, struct spi_transfer *xfers,
  9. unsigned int num_xfers)
  10. {
  11. struct spi_message msg;
  12. spi_message_init_with_transfers(&msg, xfers, num_xfers);
  13. return spi_sync(spi, &msg);
  14. }
  15. struct spi_message {
  16. struct list_head transfers;
  17. struct spi_device *spi;
  18. };
  19. spi_message_init_with_transfers(struct spi_message *m,
  20. struct spi_transfer *xfers, unsigned int num_xfers)
  21. {
  22. unsigned int i;
  23. spi_message_init(m);
  24. for (i = 0; i < num_xfers; ++i)
  25. spi_message_add_tail(&xfers[i], m);
  26. }

总体流程:

①、申请并初 始化 spi_transfer,设置 spi_transfer的 tx_buf成员变量, tx_buf为要发送的数
据。然后设置 rx_buf成员变量, rx_buf保存着接收到的数据。最后设置 len成员变量,也就是要进行数据通信的长度。
②、使用 spi_message_init函数初始化 spi_message。
③、使用 spi_message_add_tail函数将前面设置好的 spi_transfer添加到 spi_message队列中。
④、使用 spi_sync函数完成 SPI数据同步传输。

 2.1.9 spi_message_init

  1. int spi_sync(struct spi_device *spi, struct spi_message *message)
  2. /*
  3. 功能:初始化spi_message
  4. 参数:
  5. @spi :要进行数据传输的 spi_device
  6. @message:要传输的 spi_message
  7. 返回值:无
  8. */

2.1.10 spi_message_add_tail

  1. void spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)
  2. /*
  3. 功能:初始化完成以后需要将 spi_transfer添加到 spi_message队列中
  4. 参数:
  5. @t:要添加到队列中的 spi_transfer
  6. @m:spi_transfer要加入的 spi_message
  7. 返回值: 无
  8. */

2.1.11 spi_sync:同步传输

  1. int spi_sync(struct spi_device *spi, struct spi_message *message)
  2. /*
  3. 功能:同步传输,会阻塞的等待 SPI 数据传输完成
  4. 参数:
  5. @spi:要进行数据传输的 spi_device
  6. @message:要传输的 spi_message
  7. 返回值:成功返回0,失败返回错误码
  8. */

2.1.12 spi_async:异步传输

  1. int spi_async(struct spi_device *spi, struct spi_message *message)
  2. /*
  3. 功能:异步传输不会阻塞的等到 SPI数据传输完成,异步传输需要设置 spi_message中的 complete成员变量,
  4. complete是一个回调函数,当 SPI异步传输完成以后此函数就会被调用。
  5. 参数:
  6. @spi:要进行数据传输的 spi_device
  7. @message:要传输的 spi_message
  8. 返回值:成功返回0,失败返回错误码
  9. */

3、驱动程序

3.1 修改设备树

  1. &spi4{
  2. pinctrl-names = "default", "sleep";
  3. pinctrl-0 = <&spi4_pins_b>;
  4. pinctrl-1 = <&spi4_sleep_pins_b>;
  5. cs-gpios = <&gpioe 11 0>;
  6. status = "okay";
  7. m74hc595@0{
  8. compatible = "aaa,m74hc595";
  9. reg = <0>;
  10. spi-max-frequency = <10000000>; //10Mhz
  11. };
  12. };

3.2 驱动程序编写

  1. #ifndef __M74HC595_H__
  2. #define __M74HC595_H__
  3. #define SEG_WHICH _IOW('k',0,int)
  4. #define SEG_DAT _IOW('k',1,int)
  5. #endif
  1. #define NAME "m74hc595"
  2. int major = 0;
  3. struct class *cls;
  4. struct device *dev;
  5. struct spi_device *gspi;
  6. u8 code[] = {
  7. 0x3f, 0x06, 0x5b, 0x4f, 0x6d, 0x7d, 0x07, 0x7f,
  8. 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71,
  9. };
  10. u8 which[] = {
  11. 0x1, 0x2, 0x4, 0x8,
  12. };
  13. int m74hc595_open(struct inode *inode, struct file *file)
  14. {
  15. return 0;
  16. }
  17. long m74hc595_ioctl(struct file *file,
  18. unsigned int cmd, unsigned long args)
  19. {
  20. switch(cmd){
  21. case SEG_WHICH:
  22. spi_write(gspi,&which[args],1);
  23. break;
  24. case SEG_DAT:
  25. spi_write(gspi,&code[args],1);
  26. break;
  27. default: printk("ioctl error\n");break;
  28. }
  29. return 0;
  30. }
  31. int m74hc595_close(struct inode *inode, struct file *file)
  32. {
  33. return 0;
  34. }
  35. struct file_operations fops = {
  36. .open = m74hc595_open,
  37. .unlocked_ioctl = m74hc595_ioctl,
  38. .release = m74hc595_close,
  39. };
  40. int m74hc595_probe(struct spi_device *spi)
  41. {
  42. u8 buf[2] = {0xf,0x0};
  43. gspi = spi;
  44. spi_write(gspi,buf,ARRAY_SIZE(buf));
  45. major = register_chrdev(0,NAME,&fops);
  46. cls = class_create(THIS_MODULE,NAME);
  47. dev = device_create(cls,NULL,MKDEV(major,0),NULL,NAME);
  48. return 0;
  49. }
  50. int m74hc595_remove(struct spi_device *spi)
  51. {
  52. device_destroy(cls,MKDEV(major,0));
  53. class_destroy(cls);
  54. unregister_chrdev(major,NAME);
  55. return 0;
  56. }
  57. const struct of_device_id of_match[] = {
  58. {.compatible = "aaa,m74hc595",},
  59. {},
  60. };
  61. struct spi_driver m74hc595 = {
  62. .probe = m74hc595_probe,
  63. .remove = m74hc595_remove,
  64. .driver = {
  65. .name = "bbb",
  66. .of_match_table = of_match,
  67. },
  68. };
  69. module_spi_driver(m74hc595);

4、应用程序
 

  1. int main(int argc, const char *argv[])
  2. {
  3. int which=0;
  4. int data=0;
  5. int fd;
  6. fd = open("/dev/m74hc595",O_RDWR);
  7. if(fd < 0){
  8. perror("open error");
  9. return -1;
  10. }
  11. while(1){
  12. ioctl(fd,SEG_WHICH,which++);
  13. ioctl(fd,SEG_DAT,data++);
  14. if(which >= 4)which=0;
  15. if(data >= 16)data = 0;
  16. sleep(1);
  17. }
  18. close(fd);
  19. return 0;
  20. }
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/712341
推荐阅读
相关标签
  

闽ICP备14008679号