天天看點

SPI Bit-banging方法的實作

前言

SPI(Serial Peripheral Interface)是一種應用廣泛的通信總線,通常微處理器上會內建SPI子產品以支援該通信協定,輸出正确的信号的時序,并保證時序間同步,實作與外部SPI裝置正常通信。當需要使用微處理器上SPI子產品,但發現引腳被占用時,那麼可以通過SPI Bit-banging這個方法,通過GPIO端口模拟SPI接口引腳(CS、MOSI、MISO、CLK)上的時序信号來實作SPI協定,與對應SPI裝置進行通信。以下為MT7688晶片的SPI讀寫時序圖。本文主要介紹如何在嵌入式linux發行版平台上實作SPI Bit-banging功能。

SPI Bit-banging方法的實作

裝置樹文法和格式

  1. 嵌入式linux系統引入了裝置樹(Device Tree)機制,采用這種資料結構将硬體資訊組織成DTS(Device Tree source)檔案用于描述闆級裝置。裝置樹由基本單元——節點(node)組織成樹狀結構,一個裝置樹隻有一個根節點(root node),根節點中可包含若幹子節點,每個子節點可以同時包含若幹屬性和下一級子節點,屬性用于描述了節點的具體特征。
  2. 以下為.dts檔案中最基本的樹結構,“/”表示根節點,根節點下的兩個子節點分别為“node1”和“node2”,“node1”和“node2”又各自包含了子節點,如“child-node1”和“child-node2”。檔案中若幹鍵-值對,為分散在裝置樹中的屬性。
  3. / {
  4. node1 {
  5. a- string- property = "A string";
  6. a- string-list- property = "first string", "second string";
  7. a-byte-data- property = [ ];
  8. child-node1 {
  9. first-child- property;
  10. second-child- property = < >;
  11. a- string- property = "Hello, world";
  12. };
  13. child-node2 {
  14. };
  15. };
  16. node2 {
  17. an- empty- property;
  18. a-cell- property = < >; /* each number (cell) is a uint32 */
  19. child-node1 {
  20. };
  21. };
  22. };

由于以上的.dts檔案并沒有描述任何硬體裝置的特征,下面以openwrt發行版上的MT7628dts檔案為例對裝置樹進行具體說明。每個節點以“<名稱>[@<裝置位址>]”形式命名,當該節點描述的裝置存在裝置位址時需在名稱後加上主位址,同時節點中以reg=<位址1 長度1] [位址2 長度2] [位址3 長度3] ... >方式列出裝置使用的位址範圍,父節點的 #address-cells 和 #size-cells 屬性聲明reg中各字段的數量。節點中的compatible屬性表示使用哪個裝置驅動綁定到目前節點描述的裝置上。

  1. Mt7628an.dtsi檔案部分代碼:
  2. / {
  3. #address-cells = <1>;
  4. #size-cells = <1>;
  5. compatible = "ralink,mtk7628an-soc";
  6. cpus {
  7. cpu@ {
  8. compatible = "mips,mips24KEc";
  9. };
  10. };
  11. chosen {
  12. bootargs = "console=ttyS0,57600";
  13. };
  14. ......
  15. palmbus: palmbus@ {
  16. compatible = "palmbus";
  17. reg = < >;
  18. ranges = < >;
  19. #address-cells = <1>;
  20. #size-cells = <1>;
  21. sysc: sysc@ {
  22. compatible = "ralink,mt7620a-sysc";
  23. reg = < >;
  24. };
  25. gpio@ {
  26. #address-cells = <1>;
  27. #size-cells = <0>;
  28. compatible = "mtk,mt7628-gpio", "mtk,mt7621-gpio";
  29. reg = < >;
  30. interrupt-parent = <&intc>;
  31. interrupts = < >;
  32. gpio0: bank@ {
  33. reg = < >;
  34. compatible = "mtk,mt7621-gpio-bank";
  35. gpio-controller;
  36. #gpio-cells = <2>;
  37. };
  38. gpio1: bank@ {
  39. reg = < >;
  40. compatible = "mtk,mt7621-gpio-bank";
  41. gpio-controller;
  42. #gpio-cells = <2>;
  43. };
  44. gpio2: bank@ {
  45. reg = < >;
  46. compatible = "mtk,mt7621-gpio-bank";
  47. gpio-controller;
  48. #gpio-cells = <2>;
  49. };
  50. };
  51. spi0: [email protected] {
  52. compatible = "ralink,mt7621-spi";
  53. reg = < >;
  54. resets = <&rstctrl >;
  55. reset-names = "spi";
  56. #address-cells = <1>;
  57. #size-cells = <0>;
  58. pinctrl-names = "default";
  59. pinctrl = <&spi_pins>;
  60. status = "disabled";
  61. };
  62. ......
  63. };
  64. pinctrl: pinctrl {
  65. compatible = "ralink,rt2880-pinmux";
  66. pinctrl-names = "default";
  67. pinctrl = <&state_default>;
  68. state_default: pinctrl0 {
  69. };
  70. spi_pins: spi {
  71. spi {
  72. ralink,group = "spi";
  73. ralink,function = "spi";
  74. };
  75. };
  76. spi_cs1_pins: spi_cs1 {
  77. spi_cs1 {
  78. ralink,group = "spi cs1";
  79. ralink,function = "spi cs1";
  80. };
  81. };
  82. ......
  83. };
  84. };

SPI Bit-banging實作

嵌入式Linux核心中已提供SPI Bit-banging驅動代碼,隻需進行對應的配置即可使用。

首先在openwrt_widora-master/target/linux/ramips/dts路徑下修改Widora.dts檔案,将配置代碼加入根節點中,并且在pinctrl中将Spis功能的引腳申明為GPIO引腳,驅動檔案為drivers/spi路徑下的spi-gpio.c,函數中注冊的驅動名稱為spi_gpio,與 compatible屬性對應。

  1. dts檔案部分代碼:
  2. gpio-spi {
  3. status = "okay";
  4. compatible = "spi-gpio";
  5. #address-cells = <0x1>;
  6. ranges;
  7. gpio-sck = <&gpio0 >;
  8. gpio-miso = <&gpio0 >;
  9. gpio-mosi = <&gpio0 >;
  10. cs-gpios = <&gpio0 >;
  11. num-chipselects = < >;
  12. }
  13. spi-gpio.c檔案部分代碼:
  14. #define DRIVER_NAME "spi_gpio"
  15. static struct platform_driver spi_gpio_driver = {
  16. .driver = {
  17. .name = DRIVER_NAME,
  18. .owner = THIS_MODULE,
  19. .of_match_table = of_match_ptr(spi_gpio_dt_ids),
  20. },
  21. .probe = spi_gpio_probe,
  22. .remove = spi_gpio_remove,
  23. };
  24. module_platform_driver(spi_gpio_driver);

其次在OpenWrt的配置界面中選擇Kernel modules–> SPI Support –>kmod-spi-gpio,選中後會自動關聯 kmod-spi-bitbang子產品。

最後編譯核心并燒錄固件。核心啟動後通過lsmod指令檢視已經加載到核心中的子產品的狀态資訊,可發現核心已經加載了spi_bitbang和spi_gpio子產品。

SPI Bit-banging方法的實作

在spi-gpio.c檔案的spi_gpio_probe函數加入printk語句觀察可獲悉到spi-gpio驅動在啟動時被加載運作,并且從dts檔案中擷取到定義為SPI Bit-banging功能的GPIO引腳。

SPI Bit-banging方法的實作

結論

SPI的bit-bang方式實作SPI協定雖然可以不依賴于控制器上的SPI外設子產品,但是需要代碼完成時序邏輯和同步要求,相對來說性能低于控制器自身的SPI子產品,是以僅适用于低速要求的應用場合。