SPI通讯

文章介绍了SPI通信协议的特点,包括其高速、全双工和同步特性,并通过MISO、MOSI、SCLK和CS四根线进行数据传输。文中详细解释了CPOL和CPHA对时钟极性和相位的影响,并通过LA2016波形仪器展示了正确的配置方法和通信流程。文章还提供了SPI在单片机应用中的实例,包括代码示例和波形分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、介绍

SPI是高速、全双工、同步的通信总线。

SPI应用于存储芯片、AD转换器及LCD中。

同步、异步区别:是否有时钟线,例如SPI、I2C是同步通信,需要用到时钟线,串口是异步通信,没有时钟线。

SPI通信需要四根线:MISO MOSI SCLK(时钟) CS(片选)

 

MISO和MOSI有没有容易搞混的小伙伴,直接看英文记忆。

例如:MISO 英文全称为 Master input slave output,缩写MISO,主机输入从机输出。

2、时序

2、1如何配置LA2016显示波形

以之前开源项目里的触摸屏工程为例,那个工程触摸屏驱动是厂家写好的,把程序上传单片机,连接屏幕,看一下波形。

在LA2016波形仪器上选择四个通道,依次为MISO MOSI SCLK(时钟) CS(片选)。

 

连接四个通道至单片机的对应引脚,点击软件LA2016等待触发,点击屏幕触发,波形如下(没有设置):

 

调试了好久,都没有得到MISO MOSI 两个数据线上的波形,原因:

没有设置LA2016软件关于CPOL和CPHA!!!!!

这里CPOL和CPHA需要找到程序,看程序里是如何编写的,在LA2016波形显示软件上进行对应设置。

找到程序关于SPI初始化部分的函数,如下:

void SPI2_Init(void)    
{
    SPI_InitTypeDef  SPI_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;
     
    //配置SPI2管脚
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOB, ENABLE);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_15;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
​
    GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_14;    
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; 
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  
    GPIO_Init(GPIOB, &GPIO_InitStructure);  
    
    //SPI2配置选项
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2 ,ENABLE);
       
    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_InitStructure.SPI_CRCPolynomial = 7;
    SPI_Init(SPI2, &SPI_InitStructure);
​
    //使能SPI2
    SPI_Cmd(SPI2, ENABLE);   
}

****时钟极性******

  1. CPOL = 0:时钟空闲IDLE为低电平 0;

  2. CPOL= 1:时钟空闲IDLE为高电平1;

    ****时钟相位******

    1. CPHA= 0:在时钟信号SCK的第一个跳变沿采样;

    2. CPHA= 1:在时钟信号SCK的第二个跳变沿采样;

看程序得知,CPOL =0;CPHA=1,软件上对应配置如下:

 

连接调试仪器与单片机对应引脚,上电,LA2016软件点击等待触发,触摸屏幕产生SPI通信,正确波形如下:

 

 注意:片选信号下降沿触发有效!

选取一小块波形,我们来看下通讯流程:

 

 流程:

1、主设备发起信号,将CS片选信号拉低。

2、主设备发送时钟信号,告诉从设备进行读数据写数据下降沿有效(图中的波形,有的是上升沿)。

3、主设备将要发送的数据发送到数据缓存区,缓存区通过移位寄存器、串行移位寄存器通过MOSI信号线一位一位发送出去,同时MISO将接收到的数据通过移位寄存器一位一位的移到接收缓存区。

4、从机通过自己的通过MISO线将内容发送出去,MOSI接收。

2、2实战看波形

找到正点原子HAL库SPI例子的代码,直接在上面改main函数。

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./USMART/usmart.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./BSP/NORFLASH/norflash.h"
#include "./BSP/SPI/spi.h"
​
int main(void)
{
    uint8_t key;
    uint16_t i = 0;
    uint8_t rec_data = 0;
    
    HAL_Init();                         /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    delay_init(72);                     /* 延时初始化 */
    usart_init(115200);                 /* 串口初始化为115200 */
    usmart_dev.init(72);                /* 初始化USMART */
    led_init();                         /* 初始化LED */
    lcd_init();                         /* 初始化LCD */
    key_init();                         /* 初始化按键 */
    norflash_init();
    
    while (1)
    {
        key = key_scan(0);
​
        if (key == KEY1_PRES) /* KEY1按下,写入 */
        {
            NORFLASH_CS(0);
            spi2_read_write_byte(0x06);
            NORFLASH_CS(1);
            
        }
​
    }
}
​

注意看SPI初始化!!!!

 

看程序配置CPOL和CPHA,连接LA2016时配置软件。

测试下我们在main函数中写的代码,把程序上传到单片机,并连接LA2016和单片机引脚,点击触发没看波形如下:

 

最喜欢正点原子写的这个函数,可以同时读写:

uint8_t spi2_read_write_byte(uint8_t data)
{
    uint8_t rec_data = 0;
    
    HAL_SPI_TransmitReceive(&g_spi2_handler, &data, &rec_data, 1, 1000);
    
    return rec_data;
}

测试一下读数据,用一根杜邦线把MISO和MOSI连接起来。

看波形有数据收到!

 

### SPI通信中INT引脚的功能与用法 在SPI(Serial Peripheral Interface)通信协议中,`INT`(Interrupt,中断)引脚并不属于标准的四线制SPI接口(MOSI、MISO、SCLK、SS/CS),但它是一个常见且重要的补充信号,主要用于外设向主机发起事件通知。以下是关于`INT`引脚的具体功能及其应用场景的详细介绍。 #### INT引脚的功能 1. **事件通知**:`INT`引脚通常由从设备使用,用来向主设备发出中断请求,表明发生了某种特定事件或状态变化。例如,传感器检测到阈值越界、数据准备完毕或其他重要条件达成时,可以从设备通过拉低或拉高`INT`引脚来通知主设备[^5]。 2. **降低轮询负担**:相比于主设备不断查询从设备的状态(称为轮询机制),采用`INT`引脚可以让从设备主动告知主设备何时需要关注其状态更新。这种方式能够显著减少不必要的通信流量并提高系统的效率[^5]。 3. **多路复用支持**:在一个系统中有多个带有独立`INT`输出的从属单元时,每条线路都可以对应唯一的源地址编码,使得处理器可以根据哪个输入发生变化迅速定位具体哪一部分产生了新的信息待处理[^5]。 #### 使用场景分析 - 当前很多现代集成电路产品如加速度计、陀螺仪等运动感知类MEMS元件都会配备这样的标志位以便实时反馈测量结果超出预定义范围之类的情况给MCU知道进而采取进一步行动; - 存储卡读写完成后也会借助类似的手段提醒宿主计算机可以安全移除介质而不至于丢失未保存文件; - 还有一些复杂模组比如蓝牙模块也可能利用这一特性传递连接建立成功与否的消息等等. 下面是有关如何编程控制GPIO接收来自外部硬件产生的脉冲信号的一个简单例子演示: ```c #include "stm32f4xx_hal.h" void EXTI0_IRQHandler(void){ HAL_GPIO_EXTI_Callback(GPIO_PIN_0); } void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){ if (GPIO_Pin == GPIO_PIN_0){ /* Handle interrupt here */ printf("Interrupt received\n"); } } int main(){ MX_GPIO_Init(); // Configure pin PA0 as input with external interrupt capability GPIO_InitTypeDef gpio_init = {0}; __HAL_RCC_GPIOA_CLK_ENABLE(); gpio_init.Pin = GPIO_PIN_0; gpio_init.Mode = GPIO_MODE_IT_RISING_FALLING; gpio_init.Pull = GPIO_NOPULL ; HAL_GPIO_Init(GPIOA,&gpio_init); // Start NVIC configuration for EXTI line 0 HAL_NVIC_SetPriority(EXTI0_IRQn, 0 ,0 ); HAL_NVIC_EnableIRQ(EXTI0_IRQn ); while(1){} } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哆啦哆小魔仙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值