zoukankan      html  css  js  c++  java
  • ATMEGA的SPI总线

    参考:

    1.https://www.yiboard.com/thread-783-1-1.html

    2.https://mansfield-devine.com/speculatrix/2018/01/avr-basics-spi-on-the-atmega-part-2/

    3.https://blog.csdn.net/woshi_ziyu/article/details/79451095

    第1部分中,我们在AVR ATMEGA328P微控制器上已经设置好SPI总线。现在我们就可以开始使用SPI了。

    <ignore_js_op>

    设置相关引脚

    在开始之前,我们需要在AVR上设置SPI总线的引脚(在主机模式下使用)。我在这里使用的是ATMEGA328P,所以需要定义一些宏来使代码变得更清晰。如果您使用不同的微控制器,则可以根据您的需要进行调整。

    1. #define SPI_SS_GPIO PB2
    2. #define SPI_SS_PORT PORTB
    3. #define SPI_SS_DDR DDRB
    4. #define SPI_MOSI_GPIO PB3
    5. #define SPI_MOSI_PORT PORTB
    6. #define SPI_MOSI_DDR DDRB
    7. #define SPI_MISO_GPIO PB4
    8. #define SPI_MISO_PORT PORTB
    9. #define SPI_MISO_DDR DDRB
    10. #define SPI_SCK_GPIO PB5
    11. #define SPI_SCK_PORT PORTB
    12. #define SPI_SCK_DDR DDRB
    复制代码

    现在让我们用这些宏来设置引脚。

    1. SPI_MOSI_DDR |= (1 << SPI_MOSI_GPIO);   // MOSI as output
    2. SPI_SS_DDR |= (1 << SPI_SS_GPIO);       // SS as output
    3. SPI_SCK_DDR |= (1 << SPI_SCK_GPIO);     // SCK as output
    4. // MISO should be configured automatically as input as that's default state
    5. // on GPIOs, but if you want to be emphatic
    6. SPI_MISO_DDR &= ~(1 << SPI_MISO_GPIO);  // MISO as input
    7. SPI_SS_PORT |= (1 << SPI_SS_GPIO);      // take SS high to deselect slave
    8. SPI_MISO_PORT |= (1 << SPI_MISO_GPIO);  // set pullup on MISO
    复制代码

    为了稍后简化事宜,我们可能还想设置一些宏来控制SS线。

    1. #define SPI_SLAVE_SELECTED SPI_SS_PORT &= ~(1 << SPI_SS_GPIO)
    2. #define SPI_SLAVE_DESELECTED SPI_SS_PORT |= (1 << SPI_SS_GPIO)
    复制代码

    现在我们已经设置完成,让我们看看实现这些艰苦工作的寄存器。

    SPDR - SPI数据寄存器

    这是关键的SPI寄存器的第三个,它像魔术一样工作。只需在该寄存器中写入一个字节,AVR就会启动SPI,并将数据的整个字节发送到电缆上,而无需您进一步操作。由于第1部分提到的移位寄存器之间的传输,当这样做时,SPDR包含传入的数据。所以主从机之间的数据交换由主机完成:

    ●     将SS线拉低以启用从机设备。

    ●     写一个字节到SPDR。

    ●     等待交换完成。

    ●     再次把SS线拉高。

    ●     读取SPDR中的内容。

    就是这样。我们如何知道交换是否完成?很简单 - SPSR寄存器中的SPIF位被置位。所以你所需要做的就是等待。在下面的例子中,我们只是在一个while循环中停留。这是阻塞的,如果你的微控制器很难控制,你可能会发现浪费时钟周期。如果是这样,您将需要考虑启用SPI中断,SPIF被设置时会触发。在SPI数据交换发生时,您可以继续其他事情。

    所以这里有一些代码可以通过SPI发送一个字节的数据,然后返回一个字节。

    1. uint8_t value = 0xFA; // randomly chosen value for demo purposes
    2. SPI_SLAVE_SELECTED;
    3. SPDR = value;                  // initiates transfer
    4. while( !(SPSR & (1 << SPIF))); // wait for SPIF bit to be set
    5. SPI_SLAVE_DESELECTED;
    复制代码

    而就是这样! SPDR现在包含从机发回的任何内容 - 很可能没有任何兴趣。

    正是SPI的这一点可以让你头疼。主机刚刚发送给从机的字节可能是某种类型的命令。为了得到从机的响应,只需发送另一个任意值的字节,从机就会发送它的响应。

    实际上,您可能必须发送和接收多个字节。下面是如何写一个值到该芯片的内存位置的方法:

    1.    将SS线拉低。

    2.    发送写入命令(0x02)。忽略从芯片回来的数据。

    3.    发送16位内存地址的高字节。忽略从芯片回来的数据。

    4.    发送16位内存地址的低字节。忽略从芯片回来的数据。

    5.    发送一个字节的需要存储在该位置的值。忽略从芯片回来的数据。

    6.    将SS线置位。

    所以在这种情况下,所有的流量都是单向的,所有从芯片返回的数据(实际上只是零)都被丢弃了。现在让我们从内存位置读取一个值。方法是:

    1.    将SS线拉低。

    2.    发送读命令(0x03)。忽略从芯片回来的数据。

    3.    发送16位内存地址的高字节。忽略从芯片回来的数据。

    4.    发送16位内存地址的低字节。忽略从芯片回来的数据。

    5.    发送您喜欢的任何单字节值,芯片将忽略这些值。然而,通过上述操作,芯片将已经存储在其移位寄存器中的存储位置的值发送回去。

    6.    将SS线置位。

    内存位置的值现在在SPDR中。

    在示波器上显示如下:

    <ignore_js_op>

    从内存中获取一个字节的数据似乎有很多工作要做。但是23LCV512是这些SPI器件的典型特征,一旦完成初始连接,不断地触发,它将返回更多的信息。

    所以,举例来说,如果你想得到32个字节的数据,你可以使用与上面描述的相同的过程,只是发送起始地址,但是在步骤5中,为了促使从机返回数据,而不是发送一个字节的垃圾数据 - 你会发送32个。但这还不够。在每个字节之后,您需要检查SPDR中设置的内容并将其存储在某处,因为在发送下一个垃圾字节后,该值将会被覆盖。

    这就是基本的SPI使用方法。

  • 相关阅读:
    使用SELECT语句检索数据
    redis的安装和使用【2】redis的java操作
    Python之数据结构改造
    InnoDB undo log物理结构的初始化
    Redis-RDB持久化设置
    MySql(四)Select条件查询
    Node.js TLS/SSL
    Node.js 定时器
    Node.js 系统
    Node.js 字符串解码器
  • 原文地址:https://www.cnblogs.com/MCSFX/p/10867866.html
Copyright © 2011-2022 走看看