OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 2714|回复: 2

SPI Configuration(学习笔记)

[复制链接]

8

主题

65

帖子

0

精华

初级会员

Rank: 2

积分
121
金钱
121
注册时间
2019-6-18
在线时间
25 小时
发表于 2019-9-9 19:44:38 | 显示全部楼层 |阅读模式
SPI初始化代码    功能:初始化
    参数:无
    返回:无
void BSP_SPI1_Init(void)
{
    SPI_InitTypeDef s_SPI_InitStructure;
    GPIO_InitTypeDef s_GPIO_InitStructure;

    /* 开启SPI1时钟,相关GPIO时钟 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);

/* 配置SPI1管脚:SCK  MOSI  MISO */
    s_GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
    s_GPIO_InitStructure.e_GPIO_Speed = GPIO_Speed_50MHz;
    s_GPIO_InitStructure.e_GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &s_GPIO_InitStructure);

/* 配置SPI1管脚:CS */
    s_GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
    s_GPIO_InitStructure.e_GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(GPIOA, &s_GPIO_InitStructure);

/* 取消片选 */
    SPI1_CS_HIGH();

/* SPI1配置 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);

    s_SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;   /* 设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工 */
    s_SPI_InitStructure.SPI_Mode = SPI_Mode_Master;                                 /* 设置SPI工作模式:设置为主SPI */
    s_SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;                             /* 设置SPI的数据大小:SPI发送接收8位帧结构 */
    s_SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;                                    /* 串行同步时钟的空闲状态为高电平 */
    s_SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;                                 /* 串行同步时钟的第1个跳变沿(上升或下降)数据被采样 */
    s_SPI_InitStructure,SPI_NSS = SPI_NSS_Soft;                                         /* NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制 */
    s_SPI_InitStructure.BaudRatePrescaler = SPI_BaudRatePrescaler_64;    /* 定义波特率预分频的值:波特率预分频值为16 */
    s_SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;                                /* 指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始 */
    s_SPI_InitStructure.SPI_CRCPolynomial = 7U;                                          /* CRC值计算的多项式 */
    SPI_Init(SPI1, &s_SPI_InitStructure);                                                         /* 根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器 */
    SPI_Cmd(SPI1, DISABLE);                                                                         /* 先禁止SPI */
    SPI_Cmd(SPI1, ENABLE);                                                                          /* 使能SPI外设 */
}
有几点需要注意的,首先要初始化时钟,比GPIO多了一个下面的时钟,一定要记得初始化:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
因为使用SPI需要对AFIO寄存器进行配置,进行端口复用的功能。
另外有的芯片是时钟上升沿读取数据,有的是时钟下降沿读取,M0的时候直接可以调用库函数,有图形化的界面可以选择
这里需要配置以下两段代码:
    s_SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;                                     /* 串行同步时钟的空闲状态为高电平 */
    s_SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;                                  /* 串行同步时钟的第1个跳变沿(上升或下降)数据被采样 */
通过配置时钟空闲状态的电平和数据在第几个边沿跳变的组合来实现高电平或者低电平读取数据。
SPI的读写则比较容易,代码如下:
/**************************************************
    文件名:SPI.c
    模块:SPI1和SPI3驱动模块
    详述:
        ① SPI1配置;
        ② SPI1数据收发。
        ③ SPI1管脚定义:
            PA4-SPI1-NSS:AD7194-CS
            PA5-SPI1-SCK:AD7194-CLK
            PA6-SPI1-MISO:AD7194-DOUT
            PA7-SPI1-MOSI:AD7194-DIN
        ① SPI3配置;
        ② SPI3数据收发。
        ③ SPI3管脚定义:

            PA4-SPI3-NSS:AD7194-CS
            PC10-SPI3-SCK:AD7194-CLK
            PC11-SPI3-MISO:AD7194-DOUT
            PC12-SPI3-MOSI:AD7194-DIN
**************************************************/
    #include "SPI.h"
    uint8 SPI1_u8ReadWriteByte(uint8 u8TxData)
    {
        uint8 u8Retry = 0U;
/* 发送一个字节 */
        while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET)   /**/
        {
            u8Retry++;
            if(u8Retry > 200U)
            {
                return (uint8)0U;
            }
            else   /* 其他 */
            {
/* nothing */
            }
        }
        SPI_I2S_SendData(SPI1, (uint16)u8TxData);   /* 通过外设SPIx发送一个数据 */
        /* 接收一个字节 */
        u8Retry = 0U;
        while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET)   /**/
        {
            u8Retry++;
            if(u8Retry > 200U)
            {
                return (uint8)0U;
            }
            else   /* 其他 */
            {
/* nothing */
            }
        }
        return (uint8)SPI_I2S_ReceiveData(SPI1);   /* 返回通过SPIx最近接收的数据 */
    }
uint8 SPI3_u8ReadWriteByte(uint8 u8TxData)
    {
        uint8 u8Retry = 0U;
/* 发送一个字节 */
        while(SPI_I2S_GetFlagStatus(SPI3, SPI_I2S_FLAG_TXE) == RESET)   /* 检查指定的SPI标志位设置与否:发送缓存空标志位 */
        {
            u8Retry++;
            if(u8Retry > 200U)
            {
                return (uint8)0U;
            }
            else   /* 其他 */
            {
/* nothing */
            }
        }
        SPI_I2S_SendData(SPI3, (uint16)u8TxData);   /* 通过外设SPIx发送一个数据 */
        /* 接收一个字节 */
        u8Retry = 0U;
        while(SPI_I2S_GetFlagStatus(SPI3, SPI_I2S_FLAG_RXNE) == RESET)   /* 检查指定的SPI标志位设置与否:接受缓存非空标志位 */
        {
            u8Retry++;
            if(u8Retry > 200U)
            {
                return (uint8)0U;
            }
            else   /* 其他 */
            {
/* nothing */
            }
        }
        return (uint8)SPI_I2S_ReceiveData(SPI3);   /* 返回通过SPIx最近接收的数据 */
    }
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

8

主题

65

帖子

0

精华

初级会员

Rank: 2

积分
121
金钱
121
注册时间
2019-6-18
在线时间
25 小时
 楼主| 发表于 2019-9-9 20:56:09 | 显示全部楼层
SPI是一个四条线的串行通信协议,包括一条时钟线,两条数据线和一条使能线,如果在一组SPI上复用多个SPI器件,则会有多个使能线。
AD5441是一个SPI控制的DAC,它的数据类型如下:

上升沿发送数据,低电平使能,符合库函数TPYE1,附上程序如下:
#include "SPI_U27_U28.h"
void initialize_SPI(void)
{
    DrvGPIO_InitFunction(E_FUNC_SPI1);    /* PC.8, PC.9, PC.11 设置为SPI */
    DrvGPIO_InitFunction(E_FUNC_SPI1_SS1);   /* PB.9 设置为SPI */

    DrvSYS_UnlockProtectedReg();   /* 解锁受保护的系统寄存器 */
    DrvSYS_SetIPClock(E_SYS_SPI1_CLK, 1);   /* 使能SPI端口1的核心时钟 */
    DrvSYS_LockProtectedReg();   /* 对系统寄存器上锁 */

    DrvSPI_Open(eDRVSPI_PORT1,   /* 打开SPI端口1 */
                           eDRVSPI_MASTER,    /* 设为主控 */
                           eDRVSPI_TYPE1,    /* 时钟空闲状态为低电平,在串行时钟下降沿传输数据,上升沿锁存数据 */
                           12);   /* 每次传输12比特 */

    DrvSPI_SetClockFreq(eDRVSPI_PORT1,    /* 设定SPI端口1的时钟频率 */
                                        2000000,    /* 可变时钟1频率为2M */
                                        1000000);   /* 可变时钟2频率为1M */
    /* 若不选择可变时钟,复位默认时钟源为可变时钟1即DIVIDER的频率,具体可见芯片手册 */
    delay(100);   /* 延时使时钟稳定 */

    DrvSPI_DisableAutoSS(eDRVSPI_PORT1);   /* 禁止SPI端口1的自动从选择功能 */
    DrvSPI_SetTriggerMode(eDRVPORT1, eDRVSPI_LEVEL_TRIGGER);   /* 设置从引脚为电平激活模式 */
    DrvSPI_SetSlaveSelectActiveLevel(eDRVSPI_PORT1, eDRVSPI_ACTIVE_LOW_FALLING);   /* 设置端口1从引脚为低电平激活 */

    DrvSPI_SetEndian(eDRVSPI_PORT1,    /* 设置SPI端口1数据传送为高字节在前 */
                                   eDRVSPI_MSB_FIRST);
}

/* 函数名:SPI_U27_write
    功能:调制度写函数
    出口参数:无
    入口参数:调制度的值,无符号32位(uint型) */
void SPI_U27_write(uint32_t modulation_value_)
{
    while(DrvSPI_IsBusy(sDRVSPI_PORT1))   /* 查询SPI端口1是否忙 */
    {
    }
    DrvDPI_SetSS(eDRVSPI_PORT1,   /* 激活SPI端口1的从引脚1 */
                            eDRVSPI_SS1);
    DrvSPI_SingleWrite(eDRVSPI_PORT1, &modulation_value_);   /* 将调制度的值送到SPI发送器中 */
    DrvSPI_SetGo(eDRVSPI_PORT1);   /* 开始发送 */
    DrvSPI_ClrSS(eDRVSPI_PORT1, eDRVSPI_SS1);   /* 使从引脚1非激活 */
}

/* 函数名:monitor_D6
    功能:功率写函数
    出口参数:无
    入口参数:功率的值,无符号32位(uint型) */
void SPI_U28_write(uint32_t power_value_)
{
    while(Drv_IsBusy(eDRVSPI_PORT1))   /* 查询SPI端口1是否忙 */
    {
    }
    DrvSPI_SetSS(eDRVSPI_PORT1,   /* 激活SPI端口1的从引脚0 */
                            eDRVSPI_SS0);
    DrvSPI_SingleWrite(eDRVSPI_PORT1, &power_value_);   /* 将功率的值送到SPI发送器中 */
    DrvSPI_SetGo(eDRVSPI_PORT1);   /* 开始发送 */
    DrvSPI_ClrSS(eDRVSPI_PORT1, eDRVSPI_SS0);   /* 使从引脚0非激活 */
}

头文件为
#ifndef SPI_U27_U28_h
#define SPI_U27_U28_h
#include "common_variables.h"
#include "DrvSPI.h"
#include "delay.h"
#include "DrvGPIO.h"
#include "DrvSYS.h"
extern void initialize_SPI(void);   /* SPI初始化 */
extern void SPI_U27_write(uint32_t modulation_value_);   /* 芯片U27写入函数 */
extern void SPI_U28_write(uint32_t power_value_);   /* 芯片U28写入函数 */
#endif
回复 支持 反对

使用道具 举报

8

主题

65

帖子

0

精华

初级会员

Rank: 2

积分
121
金钱
121
注册时间
2019-6-18
在线时间
25 小时
 楼主| 发表于 2019-9-10 19:06:49 | 显示全部楼层
1. 什么是SPI
SPI是串行外设接口(Serial Peripheral Interface)的缩写。是 Motorola 公司推出的一
种同步串行接口技术,是一种高速的,全双工,同步的通信总线。在越来越多的芯片集成了这种通信协议,常见的有EEPROM、FLASH、AD转换器等。它的优点是支持全双工通信、通信简单、数据传输速率块、协议支持字长不限于8bits,可根据应用特点灵活选择消息字长;缺点是相比IIC多两根线;没有指定的流控制,没有应答机制确认是否接收到数据,所以跟IIC总线协议比较在数据可靠性上有一定的缺陷。
2. 硬件连接
SPI 总线包含 4 条线,分别为SS 、SCLK、MOSI、MISO。
    (1)SS/CS(SlaveSelect):片选信号线,当有多个 SPI 设备与 MCU 相连时,每个设备的这个片选信号线是与 MCU 单独的引脚相连的,而其他的 SCK、MOSI、MISO 线则为多个设备并联到相同的 SPI 总线上,低电平有效。
    (2)SCLK (Serial Clock):时钟信号线,由主通信设备产生,不同的设备支持的时钟频率不一样,如 STM32 的 SPI 时钟频率最大为 f PCLK /2。
    (3)MOSI (Master Output, Slave Input):主设备输出 / 从设备输入引脚。主机的数据从这条信号线输出,从机由这条信号线读入数据,即这条线上数据的方向为主机到从机。
    (4)MISO(Master Input, Slave Output):主设备输入 / 从设备输出引脚。主机从这条信号线读入数据,从机的数据则由这条信号线输出,即在这条线上数据的方向为从机到主机。
注意:主机和从机不可相互转换。
单个主设备多个从设备的连接方式如下图:

3. 数据传输的四种状态
SPI因为用片选的高低电平来选择从机,所以不需要进行寻址。
SPI总线在传输数据的同时也传输了时钟信号,所以SPI协议是一种同步(Synchronous)传输协议,我们用时钟的起始来代表数据传输的开始,用时钟的停止来表示数据传输的结束。据此可分为两种模式,时钟上升沿有效为一种,下降沿有效为另一种;此外,根据SPI设备何时交换数据以及何时对接收数据进行采样,又分为两种模式(奇数边沿采集与偶数边沿采集)。
我们通过设置控制寄存器SPICR1中的CPOL和CPHA位,将SPI可以分成四种传输模式,来保证两个设备之间的传输是同步的。
CPOL,即Clock Polarity(对立的),决定时钟空闲时的电平为高或低。
1 = 时钟低电平时有效,空闲时为高
0 = 时钟高电平时有效,空闲时为低
CPHA,即Clock Phase(阶段;时期),定义SPI数据传输的两种基本模式。
1 = 数据采样发生在时钟(SCK)偶数(2,4,6,…,16)边沿(包括上下边沿)
0 = 数据采样发生在时钟(SCK)奇数(1,3,5,…,15)边沿(包括上下边沿)
四种模式的时序图如下:

模式0:CPOL= 0,CPHA=0。SCK串行时钟线空闲是为低电平,数据在SCK时钟的上升沿被采样,数据在SCK时钟的下降沿切换
模式1:CPOL= 0,CPHA=1。SCK串行时钟线空闲是为低电平,数据在SCK时钟的下降沿被采样,数据在SCK时钟的上升沿切换
模式2:CPOL= 1,CPHA=0。SCK串行时钟线空闲是为高电平,数据在SCK时钟的下降沿被采样,数据在SCK时钟的上升沿切换
模式3:CPOL= 1,CPHA=1。SCK串行时钟线空闲是为高电平,数据在SCK时钟的上升沿被采样,数据在SCK时钟的下降沿切换
4. SPI架构及特性
(1)单次传输可选择为 8 或 16 位。
(2)波特率预分频系数(最大为 fPCLK/2) 。
(3)时钟极性(CPOL)和相位(CPHA)可编程设置 。
(4)数据顺序的传输顺序可进行编程选择,MSB 在前或 LSB 在前。
(5)可触发中断的专用发送和接收标志。
(6)可以使用 DMA 进行数据传输操作。
STM32的SPI架构图如下:

(1)MISO 数据线接收到的信号经移位寄存器处理后把数据转移到接收缓冲区,然后这个数据就可以由我们的软件从接收缓冲区读出了。
当要发送数据时,我们把数据写入发送缓冲区,硬件将会把它用移位寄存器处理后输出到 MOSI 数据线。
(2)SCK 的时钟信号则由波特率发生器产生,我们可以通过波特率控制位(BR)来控制它输出的波特率。
(3)控制寄存器 CR1 掌管着主控制电路,STM32 的 SPI 模块的协议设置(时钟极性、相位等)就是由它来制定的。而控制寄存器 CR2 则用于设置各种中断使能。
(4)最后为 NSS 引脚,这个引脚扮演着 SPI 协议中的SS 片选信号线的角色,如果我们把 NSS 引脚配置为硬件自动控制,SPI 模块能够自动判别它能否成为 SPI 的主机,或自动进入 SPI 从机模式。但实际上我们用得更多的是由软件控制某些 GPIO 引脚单独作为SS信号,这个 GPIO 引脚可以随便选择。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-5-16 11:43

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表