bigbat 发表于 2020-4-8 12:28

STM32MP157A-DK1测评+SPI设备

<div class='showpostmsg'><p>前一阶段由于不熟悉linux的设备驱动程序的编写又找不到资料。所以就暂时把SPI设备的实验给搁置了。前几天看到网友的测评很受启发就又开始了解SPI的驱动。初步的对linux驱动有了一些认识后,开始SPI设备的实验。如果想使用SPI设备就要从设备树的配置开始。linux的设备树其实就是一种设备信息的配置文件。有点象JSON、XML文件的那种。设备树文件的主要思想就是让设备驱动和设备配置分开。这样编写设备驱动就和具体设备的BPS没有关系了。驱动程序只针对这类设备编写就可以。不同要求的具体板卡如果想使用某种外设,只要在设备的&ldquo;配置文件&rdquo;把这种外设的信息配置一下,驱动程序就可以工作了。就象我们测试的STM32MP157A-DK1板卡默认是不打开SPI5设备的,SPI5的PIN默认为GPIO。你的设备如果需要默认使用SPI5,打开SPI5的配置就可以。不需要针对具体设备编写驱动程序。设备树这种机制又可以将同类型的设备抽象成一种驱动,通过配置修改一下寄存器地址就可以驱动不同的同类设备。比如SPI、I2C、CAN、Uart等等字符设备。又如Uart设备的驱动操作方法是一样的,只是配置不一样而已。这样就没必要写多个驱动程序了。不同的uart使用同一个驱动只是各自的地址不一样而已。<br />
如果想使用STM32MP157A-DK1的SPI5设备。首先需要在arch/arm/boot/dts/stm32mp157a-dk1.dts中进行修改配置。</p>

<pre>
<code class="language-cs">&amp;spi5 {
      pinctrl-names = "default", "sleep";
      pinctrl-0 = &lt;&amp;spi5_pins_a&gt;;
      pinctrl-1 = &lt;&amp;spi5_sleep_pins_a&gt;;
      cs-gpios = &lt;&amp;gpiog 15 GPIO_ACTIVE_LOW&gt;;
      cd-gpios = &lt;&amp;gpiod 7GPIO_ACTIVE_LOW&gt;;
      status = "okay";

      spidev0: spidev@0 {
                compatible = "rohm,dh2228fv";
                reg = &lt;0&gt;;
                spi-max-frequency = &lt;50000000&gt;;
      };
};
</code></pre>

<p>spi5引脚配置项pinctrl-0和pinctrl-1在包含文件.dtsi中 ,这些是SPI的引脚配置。我的SD1306还需要CS和CD两个控制引脚。同时也在这次声明中配置。其实cs-gpios和cd-gpios这两个引脚使用默认配置也可以。arch/arm/boot/dts/stm32mp157-pinctrl.dtsi</p>

<pre>
<code class="language-cs">spi5_sleep_pins_a: spi5-sleep-0 {
        pins {
        pinmux = &lt;STM32_PINMUX('F', 7, ANALOG)&gt;, /* SPI5_SCK */
        &lt;STM32_PINMUX('F', 8, ANALOG)&gt;, /* SPI5_MISO */
        &lt;STM32_PINMUX('F', 9, ANALOG)&gt;; /* SPI5_MOSI */
       
          };
};

gpiod: gpio@50005000 {
        gpio-controller;
        #gpio-cells = &lt;2&gt;;
        interrupt-controller;
        #interrupt-cells = &lt;2&gt;;
        reg = &lt;0x3000 0x400&gt;;
        clocks = &lt;&amp;rcc GPIOD&gt;;
        st,bank-name = "GPIOD";
        status = "disabled";
        };

gpiog: gpio@50008000 {
        gpio-controller;
        #gpio-cells = &lt;2&gt;;
        interrupt-controller;
        #interrupt-cells = &lt;2&gt;;
        reg = &lt;0x6000 0x400&gt;;
        clocks = &lt;&amp;rcc GPIOG&gt;;
        st,bank-name = "GPIOG";
        status = "disabled";
        };

</code></pre>

<p>可以看到SPI5的引脚配置(spi5_sleep_pins_a)与资料上的一致。为PF7、 PF8、PF9这些是芯片引脚功能编号。设备树还配置了其它参数。这些配置可以在spidev.c驱动中找到。如:spidev0: spidev@0设置了驱动文件spidev.c和驱动中的功能compatible 参考下面的代码</p>

<pre>
<code class="language-cpp">static struct class *spidev_class;

#ifdef CONFIG_OF
static const struct of_device_id spidev_dt_ids[] = {
        { .compatible = "rohm,dh2228fv" },
        { .compatible = "lineartechnology,ltc2488" },
        { .compatible = "ge,achc" },
        { .compatible = "semtech,sx1301" },
        {},
};
MODULE_DEVICE_TABLE(of, spidev_dt_ids);
#endif

#ifdef CONFIG_ACPI</code></pre>

<p>驱动程序中并没有看到使用这些引脚的代码,驱动只是使用了配置中的寄存器而已。SPI4和SPI5的驱动没有什么差别。只要配置好SPI5的设置,SPI5功能就可以使用了,且SPI的三个主要功能引脚全部设置完成。驱动程序配置完成后只需要编写应用层驱动程序了,也可以说只要文件向/dev/spidev0.0里面灌数据了就可以驱动SPI5了。我用的是SD1306 OLED SPI串口屏,除了SPI引脚还需要两个控制引脚(CS和CD),注意CS引脚默认为&quot;低&quot;,使能打开。CD引脚的功能是命令选择。程序主要参考了tools\spi\spidev_test.c文件,其中主要的API有两个,<br />
#include &lt;linux/gpio.h&gt; 其中的API函数 gpio_data.values用来设置cs-gpios引脚的状态<br />
#include &lt;linux/spi/spidev.h&gt;主要使用了其中的宏定义,如:ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &amp;speed);当中的SPI_IOC_WR_MAX_SPEED_HZ等定义。<br />
主要的文件两个<br />
const char gpio_dev_cs = &quot;/dev/gpiochip3&quot;;<br />
const char gpio_dev_ds = &quot;/dev/gpiochip5&quot;;<br />
const char spidev_name = &quot;/dev/spidev0.0&quot;;</p>

<pre>
<code class="language-cpp">
int cd_gpio_set(void)
{
        int ret;
       
        ret = ioctl(cd_fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &amp;gpio_data);
        if (ret == -1) {
                ret = -errno;
                fprintf(stderr, "Failed to issue %s (%d)\n",
                        "GPIOHANDLE_SET_LINE_VALUES_IOCTL", ret);
        }
               
        return ret;
}

void send_byte_spi(unsigned char data)
{
        int status;

        spi_tx = data;

        status = ioctl(fd, SPI_IOC_MESSAGE(1), xfer);
        if (status &lt; 0)
        {
                perror("SPI_IOC_MESSAGE");
                return;
        }
}</code></pre>

<p>用户程序参考SD1306的程序就可以。</p>

<p><br />
<b><font color="#5e7384">此内容由EEWORLD论坛网友<font size="3">bigbat</font>原创,如需转载或用于商业用途需征得作者同意并注明出处</font></b></p>
</div><script>                                        var loginstr = '<div class="locked">查看本帖全部内容,请<a href="javascript:;"   style="color:#e60000" class="loginf">登录</a>或者<a href="https://bbs.eeworld.com.cn/member.php?mod=register_eeworld.php&action=wechat" style="color:#e60000" target="_blank">注册</a></div>';
                                       
                                        if(parseInt(discuz_uid)==0){
                                                                                                (function($){
                                                        var postHeight = getTextHeight(400);
                                                        $(".showpostmsg").html($(".showpostmsg").html());
                                                        $(".showpostmsg").after(loginstr);
                                                        $(".showpostmsg").css({height:postHeight,overflow:"hidden"});
                                                })(jQuery);
                                        }                </script><script type="text/javascript">(function(d,c){var a=d.createElement("script"),m=d.getElementsByTagName("script"),eewurl="//counter.eeworld.com.cn/pv/count/";a.src=eewurl+c;m.parentNode.insertBefore(a,m)})(document,523)</script>

兰博 发表于 2020-4-8 13:42

<p><img height="50" src="https://bbs.eeworld.com.cn/static/editor/plugins/hkemoji/sticker/onion/Onion--12.gif" width="50" /></p>

<p>感谢楼主。教程太棒了</p>

freebsder 发表于 2020-4-8 21:31

好的开始
页: [1]
查看完整版本: STM32MP157A-DK1测评+SPI设备