SPI

STM32-SPI使用 SPI知识:

1)

高速同步串行口。3~4线接口(CS ,CLK ,MOSI,MISO),收发独立、可同步进行。

2)SPI分为主从模式,主模式提供时钟和片选选择信号.

3) 模式控制:CPOL用来控制时钟信号(clk)在空闲时候的状态;CPHA用来控制采样时刻时CLK的边缘动作。

CPOL CPHA 模式

0 0 CLK空闲时为低电平,CLK上升沿采样数据。

0 1 CLK空闲为低电平,CLK下降沿采样数据。

1 0 CLK空闲时为高电平,CLK上升沿采样数据。

1 1 CLK空闲时为高电平,CLK下降沿采样数据。

1)SPI配置(3.01库):

SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //双工模式

SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //SPI主模式

SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //8bit数据

SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //CLK空闲时为高电平

SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //CLK上升沿采样,因为上升沿是第二个边沿动作,所以也可以理解为第二个边沿采样

SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //片选用软件控制

SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; //SPI频率

SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //高位在前

SPI_InitStructure.SPI_CRCPolynomial = 7; //crc7,stm32spi带硬件ecc

SPI_Init(SPI1, &SPI_InitStructure);

2)CS信号:

主模式下要为从设备提供片选信号,值得注意的是STM32的主频相当较高,要提防数据没有完全发送前拉高CS信号。

3)SPI读写:(非中断模式)

a)写一个字节:

while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);

//确保发生前Buffer为空,也就是说上一次已经发生完成

SPI_I2S_SendData(SPI1, Data); //往寄存器中写入一个字节

while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);

//等待接受到一个字节数据,为什么要这么做?加这一句的原因是为了确保这个字节已经发送出去,因为发生和接受是并行同步进行,那就是说你发生出去一个字节意味着你收到一个字节。所以这样判断完全没有问题,再说必要性,如果你不加这句你就会容易犯过早拉高CS信号的错误,你想想如果在SPI_I2S_SendData(SPI1,
Data)后面立即拉高CS是什么后果。

SPI_I2S_ReceiveData(SPI1); //都会接收到的数据,看起来没什么必要,但以用stm32的经验推荐这样做,也许会有意想不到的收获。

SPI_Writebyte(u8 data)

{

while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);

SPI_I2S_SendData(SPI1, Data);

while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);

SPI_I2S_ReceiveData(SPI1);

}

b)读一个字节:

读的时候要注意一个问题,因为从模式是没法提供时钟的,所以主模式下必须要在接收的同时提供时钟。办法就是发送一个字节来实现,因为还是上面说的,发送一个字节就意味着收到一个字节,代码和写完全一样,只要把读出来的字节保存即可。

u8 SPI_Readbyte(u8 data)

{

while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);

SPI_I2S_SendData(SPI1, Data);

while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);

return SPI_I2S_ReceiveData(SPI1);

}

总结:上面的程序是最求稳定而设定的,如果你对速度有要求,你可以做相应的精简,比如读写直接对寄存器进行操作,另外配置SPI前要对从模式的模式了解清楚,包括从设备支持的时钟范围和模式(CPOL,CPHA状态)。

/*****************************************************************************************************

* @brief: LDC1000应用程序

* _____________ _______________

* |PB4(SSI2CLK)
----> SCLK|

* |PB5(SSI2FSS)
----> CSB |

* |PB6(SSI2RX)    <----       SDO
|

*  Tiva M4 |PB7(SSI2TX)
----> SDI
| LDC1000

* |PA4(INT/GPIO)
<---- INTB|

* |PB0(Timer CLK)
---->   TBCLK|

* _____________| |______________

*****************************************************************************************************/

#include <stdint.h>

#include <stdbool.h>

#include "driverlib/rom.h"

#include "driverlib/adc.h"

#include "driverlib/sysctl.h"

#include "driverlib/pwm.h"

#include "driverlib/timer.h"

#include "driverlib/gpio.h"

#include "driverlib/pin_map.h"

#include "driverlib/interrupt.h"

#include "driverlib/ssi.h"

#include "inc/hw_ints.h"

#include "inc/hw_memmap.h"

#include "inc/hw_gpio.h"

#include "LDC1000_cmd.h"

#include "inc/hw_timer.h"

#include "inc/hw_types.h"

#include "inc/hw_ssi.h"

#ifndef TARGET_IS_BLIZZARD_RA1

#define TARGET_IS_BLIZZARD_RA1

#endif

#ifndef  PART_TM4C123GH6PM

#define  PART_TM4C123GH6PM

#endif

#define SPI_RWBIT 0x80
                          //LDC1000,SPI时序读写位,1=读,0=写

unsigned long ProximityData;                                           //LDC上Proximity Data

unsigned long FrequencyData;                                           //LDC1000上的Frequency Data

volatile unsigned char DataRdy ;                                       //LDC1000中断标志

uint32_t DataRcv[5] ;                                                  //存储SPI读取的数据

/********************************************************************

* @brief: SPI写数据

* @param: unsigned int,SPIdata:待写的数据

* @return: none

*********************************************************************/

void SPIDataSend(unsigned int SPIdata)

{

SSIDataPut(SSI2_BASE,SPIdata);  
//SPI发送(写)数据

while(SSIBusy(SSI2_BASE)) ;          
  //等待SPI发送(写)完成

}

/********************************************************************

* @brief: LDC1000初始化配置,ps:在SPI中配置了数据位16个数据长度,故

* 在发送数据时可以将地址和值进行或运算一起发送出去

* @param: none

* @return: none

*********************************************************************/

void LDC1000_init()

{

SPIDataSend(LDC1000_CMD_RPMAX<<8|TEST_RPMAX_INIT);
//配置Rp_MAX(0x01)寄存器

SPIDataSend(LDC1000_CMD_RPMIN<<8|TEST_RPMIN_INIT);
//配置Rp_MIN(0x02)寄存器

SPIDataSend(LDC1000_CMD_SENSORFREQ<<8|0x94);
//配置Sensor Frequency(0x03)寄存器

SPIDataSend(LDC1000_CMD_LDCCONFIG<<8|0x17);
//配置LDC Configuration(0x04)寄存器

SPIDataSend(LDC1000_CMD_CLKCONFIG<<8|0x00);
//配置Clock Configuration(0x05)寄存器,

//使用TBCLK作为时钟源

SPIDataSend(LDC1000_CMD_INTCONFIG<<8|0x02);
//配置INTB Pin Configuration(0x0A),

//配置INTB为比较输出标志位(status of Comparator output)

SPIDataSend(LDC1000_CMD_THRESHILSB<<8|0x50);
//配置Comparator Threshold High(0x06)寄存器低8位

SPIDataSend(LDC1000_CMD_THRESHIMSB<<8|0x14);
//配置Comparator Threshold High(0x07)寄存器高8位

SPIDataSend(LDC1000_CMD_THRESLOLSB<<8|0xC0);
//配置Comparator Threshold Low(0x08)寄存器低8位

SPIDataSend(LDC1000_CMD_THRESLOMSB<<8|0x12);
//配置Comparator Threshold Low(0x09)寄存器高8位

SPIDataSend(LDC1000_CMD_PWRCONFIG<<8|0x01);
//配置Power Configuration(0x0B)寄存器,

//为Active Mode,使能转化

}

/********************************************************************

* @brief: 使用SPI读取LDC1000中的数据

* @param: none

* @return: none

*********************************************************************/

void LDCRead()

{

SPIDataSend((LDC1000_CMD_PROXLSB|SPI_RWBIT)<<8);          
//写入将要读取的Proximity Data LSB寄存器地址(0x21)

SSIDataGet(SSI2_BASE,&DataRcv[0]);                      
//读取上述寄存器中的值,并存入DataRcv[0]

ProximityData|= DataRcv[0] ;

SPIDataSend((LDC1000_CMD_PROXMSB|SPI_RWBIT)<<8);
//写入将要读取的Proximity Data MSB寄存器地址(0x22)

SSIDataGet(SSI2_BASE,&DataRcv[1]);
//读取上述寄存器中的值,并存入DataRcv[1]

ProximityData|= (DataRcv[1]<<8) ;
//组合成ProximityData

SPIDataSend((LDC1000_CMD_FREQCTRLSB|SPI_RWBIT)<<8);
//写入将要读取的Frequency Counter Data LSB寄存器地址(0x23)

SSIDataGet(SSI2_BASE,&DataRcv[2]);
//读取上述寄存器中的值,并存入DataRcv[2]

FrequencyData|= DataRcv[2] ;

SPIDataSend((LDC1000_CMD_FREQCTRMID|SPI_RWBIT)<<8);
//写入将要读取的Frequency Counter Data Mid-Byte寄存器地址(0x24)

SSIDataGet(SSI2_BASE,&DataRcv[3]);
//读取上述寄存器中的值,并存入DataRcv[3]

FrequencyData|= (DataRcv[3]<<8) ;

SPIDataSend((LDC1000_CMD_FREQCTRMSB|SPI_RWBIT)<<8);
//写入将要读取的Frequency Counter Data MSB寄存器地址(0x25)

SSIDataGet(SSI2_BASE,&DataRcv[4]);
//读取上述寄存器中的值,并存入DataRcv[4]

FrequencyData|= (DataRcv[4]<<16) ;
//组合成FrequencyData

GPIOIntEnable(GPIO_PORTA_BASE,GPIO_INT_PIN_4);
//使能PA4中断

}

/********************************************************************

*  @brief: Timer初始化

*  @param: none

*  @return: none

* _____________ _______________

* | |

*  Tiva M4 |PB0(Timer CLK)
---->   TBCLK|
LDC1000

* _____________| |______________

*********************************************************************/

void TimerInit()

{

TimerDisable(TIMER2_BASE,TIMER_A);

GPIOPinTypeTimer(GPIO_PORTB_BASE,GPIO_PIN_0);

GPIOPinConfigure(GPIO_PB0_T2CCP0);
//配置PB0为CCP模式

HWREG(TIMER2_BASE + TIMER_O_CFG) = 0x04;
//选择16-bit timer

//配置TimerA周期计数(Periodic Timer mode)

HWREG(TIMER2_BASE + TIMER_O_TAMR)|=(TIMER_TAMR_TAAMS|TIMER_TAMR_TAMR_PERIOD) ;

//加载Timer计数值:40,并且设置Match值:20(Timer默认为减计数)

HWREG(TIMER2_BASE + TIMER_O_TAMATCHR) = 20;

TimerLoadSet(TIMER2_BASE,TIMER_A,40);

TimerEnable(TIMER2_BASE,TIMER_A);
//使能Timer

}

/********************************************************************

* @brief: PA4初始化

* @param: none

* @return: none

* _________ ___________

* | |

* | |

* Tiva M4
| PA4  <--- INTB|  LDC1000

* | |

* —————————| |__________

*********************************************************************/

void GPIOInit()

{

HWREG(GPIO_PORTA_BASE + GPIO_O_DEN) |= 1<<4 ;

GPIOIntTypeSet(GPIO_PORTA_BASE,GPIO_PIN_4,GPIO_RISING_EDGE);     //配置为上升沿中断

GPIOIntEnable(GPIO_PORTA_BASE,GPIO_INT_PIN_4);
 //使能PA4中断

IntEnable(INT_GPIOA);
 //使能GPIOA中断

}

/********************************************************************

* @brief:
SPI通信初始化

* @param:
none

* @return:
none

* _____________ _______________

* |PB4(SSI2CLK)
----> SCLK|

* |PB5(SSI2FSS)
----> CSB |

* |PB6(SSI2RX)    <----       SDO
|

*  Tiva M4 |PB7(SSI2TX)
----> SDI
| LDC1000

* |PB0(Timer CLK)
---->   TBCLK|

* _____________| |______________

*********************************************************************/

void SPIInit()

{

//配置PB6为SSI2RX,即对Tiva M4而言的SPI数据接收线

GPIOPinTypeSSI(GPIO_PORTB_BASE,GPIO_PIN_6) ;

GPIOPinConfigure(GPIO_PB6_SSI2RX);

//配置PB6为SSI2TX,即对Tiva M4而言的SPI数据发送线

GPIOPinTypeSSI(GPIO_PORTB_BASE,GPIO_PIN_7) ;

GPIOPinConfigure(GPIO_PB7_SSI2TX);

//配置PB4为SSI2CLK线,作为时钟线

GPIOPinTypeSSI(GPIO_PORTB_BASE,GPIO_PIN_4) ;

GPIOPinConfigure(GPIO_PB4_SSI2CLK);

//配置PB5为SSI2FFS线,作为片选线

GPIOPinTypeSSI(GPIO_PORTB_BASE,GPIO_PIN_5) ;

GPIOPinConfigure(GPIO_PB5_SSI2FSS);

SSIDisable(SSI2_BASE);                                           //禁能SSI2

//配置SSI2为SSI_FRF_MOTO_MODE_0协议格式,SPI主模式,时钟源为5K,16位数据长度

SSIConfigSetExpClk(SSI2_BASE,SysCtlClockGet(),SSI_FRF_MOTO_MODE_0,SSI_MODE_MASTER,5000,16);

SSIEnable(SSI2_BASE) ;
//使能SSI2

}

/********************************************************************

* @brief:
PA4中断服务函数,该函数在startup_ccs.c的中断向量表中进行

* 了注册

* @param:
none

* @return:
none

* _________ ___________

* | |

* | |

* Tiva M4
| PA4  <--- INTB|  LDC1000

* | |

* —————————| |__________

*********************************************************************/

void GPIOAIntHandler()

{

GPIOIntClear(GPIO_PORTA_BASE,GPIO_INT_PIN_4) ;
//清除PA4中断标志

DataRdy = 1 ;
//LDC1000中断标志置位

GPIOIntDisable(GPIO_PORTA_BASE,GPIO_INT_PIN_4) ;
//禁能PA4中断,将在SPI数据读取完成后重新使能PA4中断

}

//---------------------------------------main函数----------------------------------------------

int main(void)

{

SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_XTAL_16MHZ |
//配置主时钟为50MHz

SYSCTL_OSC_MAIN);

SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA) ;
//使能GPIOA外设模块

SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB) ;
//使能GPIOB外设模块

SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER2);
//使能Timer2外设模块

SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI2);
//使能SSI2外设模块

DataRdy = 0 ; //LDC1000中断标志清零

TimerInit() ; //TBCLK所在时钟初始化

GPIOInit() ; //GPIO初始化(PA4)

SPIInit() ; //SPI初始化

LDC1000_init(); //LDC1000初始配置

ROM_IntMasterEnable();                                        
//使能总中断

while(HWREG(SSI2_BASE + SSI_O_SR)& SSI_SR_RNE)                
//首先清除SPI上的接收缓存,排除干扰

{

DataRcv[0] = HWREG(SSI2_BASE + SSI_O_DR) ;

}

while(1)

{

//转化结束后读取ProximityData和FrequencyData

if(DataRdy)

{

DataRdy = 0 ;
//LDC1000中断标志清零(在PA4中断服务程序中置位)

LDCRead() ;
//SPI读取数据操作

}

}

}

SPI,布布扣,bubuko.com

时间: 2024-10-19 16:51:07

SPI的相关文章

RT-thread 设备驱动组件之SPI设备

本文主要介绍RT-thread中的SPI设备驱动,涉及到的文件主要有:spi_dev.c,spi_core.c,spi.h,spi_hard.c,spi_hard.h. 一.SPI设备框架 先来看spi.h中的一些数据结构: ** * SPI message structure */ struct rt_spi_message { const void *send_buf; void *recv_buf; rt_size_t length; struct rt_spi_message *next

SPI设备的驱动

主要包括两个SPI设备步骤:register_chrdevspi_register_driver关键点1:spi_board_info可以去已经运行的板子下面找例子:/sys/bus/spi/drivers已辰汉电子MX27 MDK 开发板为例:在/sys/bus/spi/drivers目录:lcd_spi pmic_spi 对应的:mx27mdk27v0.c文件中定义如下: static struct spi_board_info mxc_spi_board_info[] __initdata

spi flash偶尔出现写入错误的情况

spi flash W25Q128会偶尔出现写入错误的情况,会发现读出的值和写入的值不一致,需加入2次读出比较判断. W25QXX_Read(&temp_date_count,0x000000,1); //W25QXX_Write((u8*)&temp_date,0x400000,135); //W25QXX_Read((u8*)&temp_data_test,0x400000,135); W25QXX_Write((u8*)&temp_date,(temp_date_co

linux enc28j60网卡驱动移植(硬件spi和模拟spi)

本来想移植DM9000网卡的驱动,无奈硬件出了点问题,通过杜邦线链接开发板和DM9000网卡模块,系统上电,还没加载网卡驱动就直接崩溃了,找不到原因...刚好手上有一个enc28j60的网卡模块,于是就着手移植enc28j60的驱动. 其实移植enc28j60的驱动也十分简单,网上有现成的,只需要分配一些硬件资源即可. 由于我的内核版本老到掉牙,没有自带enc28j60的驱动,只能在网上找一个: enc28j60.c http://git.ti.com/ti-linux-kernel/ti-li

[SPI&amp;I2C]I2C和SPI协议介绍

IIC vs SPI 现今,在低端数字通信应用领域,我们随处可见IIC (Inter-Integrated Circuit) 和 SPI (Serial Peripheral Interface)的身影.原因是这两种通信协议非常适合近距离低速芯片间通信.Philips(for IIC)和Motorola(for SPI) 出于不同背景和市场需求制定了这两种标准通信协议. IIC 开发于1982年,当时是为了给电视机内的CPU和外围芯片提供更简易的互联方式.电视机是最早的嵌入式系统之一,而最初的嵌

spi总结

SPI的四种模式 SPI的相位(CPHA)和极性(CPOL)分别可以为0或1,对应的4种组合构成了SPI的4种模式(mode) Mode 0:CPOL = 0,CPHA = 0 Mode 1:CPOL = 0,CPHA = 1 Mode 2:CPOL = 1,CPHA = 0 Mode 3:CPOL = 1,CPHA = 1 时钟极性CPOL(设置时钟空闲时的电平):即SPI空闲时,时钟信号SCLK的电平.(0:空闲时低电平,1:空闲时高电平) 时钟相位CPHA(设置读取数据和发送数据的时钟沿)

SPI机制

Service Provider Interface 是java的服务提供的发现机制,很多框架中都有用到. 使用这个机制需要做以下几步: 1,在calsspath下见一个目录:META-INF\services 2,在上面的目录里建一个txt文件,文件名和接口名一致,文件中描述实现类位置,文件名和文件中实现类都需要写全名. 3,然后写好接口和实现,使用serviceloader来解析即可 spi.HelloInterface文件内容(换行符为分隔实现类描述): spi.TextHello spi

SPI总线介绍

SPI总线结构 SPI(Serial Peripheral Interface)串行外设接口,是一种高速的,全双工,同步的通信总线.采用主从模式(Master Slave)架构,支持多个slave,一般仅支持单Master. SPI接口共有4根信号线,分别是:设备选择线(SS).时钟线(SCK).串行输出数据线(MOSI).串行输入数据线(MISO). 数据传输过程: 主节点通过MOSI(Master output Slave input) 线输出数据,从节点在SIMO(Slave input 

HAL驱动库学习-SPI

如何使用SPI库1 声明SPI hanlde, 例如: SPI_HandleTypeDef hspi2 通过实现HAL_SPI_MspInit()函数初始化底层资源 以下两个必须进行初始化 a 使能spi时钟 b 配置spi pins 以下根据需要初始化 c NVIC 中断配置 d DMA配置3 配置hspi,包括模式,datasize,baudrate,msb/lsb,crc等4 调用HAL_SPI_Init()函数,初始化spi配置以及底层资源(通过调用HAL_SPI_MspInit())

Linux下spi驱动开发

转载至:http://www.embedu.org/Column/Column367.htm 作者:刘洪涛,华清远见嵌入式学院讲师. 一.概述 基于子系统去开发驱动程序已经是linux内核中普遍的做法了.前面写过基于I2C子系 统的驱动开发.本文介绍另外一种常用总线SPI的开发方法.SPI子系统的开发和I2C有很多的相似性,大家可以对比学习.本主题分为两个部分叙述,第一 部分介绍基于SPI子系统开发的理论框架:第二部分以华清远见教学平台FS_S5PC100上的M25P10芯片为例(内核版本2.6