读书人

弄清 stm32 SPI总线 基础和编程(转)

发布时间: 2012-06-30 17:20:12 作者: rapoo

搞清 stm32 SPI总线 基础和编程(转)

现在flash那部分还没看明白 但感觉这挺好的,所以转来~

原文地址:?http://blog.sina.com.cn/s/blog_4aa25f130100ssei.html

SPI应用是相当的广了,同步串行总线,同步同步当然需要时钟信号来统一了,这样通讯双方通讯时就比较默契没那么延迟(呆呆)了。应用于中低速场合。

学起来从哪些地方入手呢?如下:

SPI概念与特点???关键的时序接受????编程时重点?????STM32内部的SPI使用(介绍、功能、中断)

SPI概念与特点:不多说,串行,需要同步信号???主从结构的???CS(片选)??SCK???SDI??SDO

????????????????????? ??? ?? 全双工??一主控多从???8或16位数据通信.

其它特点:8个主模式波特率预分频系数??fpcll/2

??????????????? 主模式和从模式下快速通行??并支持切换

??????????????? 编程MSB 或LSB在前

??????????????? 专用发送和接受标志可促发中断

??????????????? 有SPI忙标志位

?????????支持硬件CRC校验,发送模式下crc值作为最后一个字节被发送,接受模式下最后一字节自动CRC校验

?????????支持错误中断标志,支持DMA功能的1字节发送和接受缓冲器:产生发送和接受请求。

SPI总线时序介绍:

看下下图应该就会了


弄清 stm32 SPI总线 基础和编程(转)
?
使用stm32??spi需要以下的步骤

1.管脚因为复用的,故先配置好管脚、并打开spi时钟

2.设置spi的工作模式

通过 SPI1_CR1 来设置,设置SPI1主机模式,设置数据格式8位,然后通过 CPOL 和 CPHA 位来设置 SCK时钟极性及采样方式。并设置 SPI1 的时钟频率(最大18Mhz),以及数据的格式(MSB 在前还是 LSB在前)。
3.使能SPI.

SPI固件库函数

弄清 stm32 SPI总线 基础和编程(转)
?
下面是初始化,必须得结构体原型

typedef struct
{
??uint16_t SPI_Direction;//设置方向?????(2线全双工、2线只接受、一线发送、一线接受)

??uint16_t SPI_Mode;?????//模式?????????(从或主设备)

??uint16_t SPI_DataSize; //宽度?????????(8或16位)

??uint16_t SPI_CPOL;?????//时钟极性?????(低或高)

??uint16_t SPI_CPHA;?????//时钟相位?????(第一个或第二个跳变沿)

??uint16_t SPI_NSS;??????//片选方式?????(硬件或软件方式)

??uint16_t SPI_BaudRatePrescaler;?//波特率预分频????(从2---256分频)??

??uint16_t SPI_FirstBit;??//最先发送的位????????????(最低位,还是最高位在先)????????

??uint16_t SPI_CRCPolynomial;?//设置crc多项式????????(数字)如7

}SPI_InitTypeDef;

下面是实例,对SPI2进行的初始化

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_14| GPIO_Pin_15;
?GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
?GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;??//复用推挽输出
?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_High;
?SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
?SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
?SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;
?SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
?SPI_InitStructure.SPI_CRCPolynomial = 7;
?SPI_Init(SPI2, &SPI_InitStructure);?//使能SPI2
?SPI_Cmd(SPI2, ENABLE);??
}

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

以上是,初始化的一些函数,发现固件库还有很多函数没用上,下面就要谈到应用了

STM32的SPI串行外围总线接口,本程序,是将STM32的SPI配置为全双工模式,且NSS使用的软件模式。在使用SPI前,下面的这个过程我们必须理解,即STM32作为主机发送一个字节数据时,必然能接收到一个数据,至于数据是否处理,由程序操作。

●?全双工模式(BIDIMODE=0并且RXONLY=0)?

─??当写入数据到SPI_DR寄存器(发送缓冲器)后,传输开始;?

─??在传送第一位数据的同时,数据被并行地从发送缓冲器传送到8位的移位寄存器中,

然后按顺序被串行地移位送到MOSI引脚上;?

─??与此同时,在MISO引脚上接收到的数据,按顺序被串行地移位进入8位的移位寄存器

中,然后被并行地传送到SPI_DR寄存器(接收缓冲器)中。?

注意:也就是说,在主机模式下,发送和接收是同时进行的,所以我们发送了一个数据,也就能接收到一个数据。而STM32内部硬件是这个过程的支撑!


弄清 stm32 SPI总线 基础和编程(转)
?

读一个字节,往里面发送0,外设就返回一个数据了,发送的0外设不处理(需要先写入命令生效)

#define SPI_ReadByte(SPIx)?SPI_WriteByte(SPIx,0)

写一个字节就直接发送相应的字节,外设就返回一个数据了

u8 SPI_WriteByte(SPI_TypeDef* SPIx,u8 byte);

//spi 写一个字节...................................................................
u8 SPI_WriteByte(SPI_TypeDef* SPIx,u8 Byte)
{
?while((SPIx->SR&SPI_I2S_FLAG_TXE)==RESET);??//等待发送区空??
?SPIx->DR=Byte;???//发送一个byte??
?while((SPIx->SR&SPI_I2S_FLAG_RXNE)==RESET);//等待接收完一个byte?
?return SPIx->DR;????????????????//返回收到的数据???
}


弄清 stm32 SPI总线 基础和编程(转)
?
void M25P16_Write_Enable(void)
{
?Select_Flash();?
?SPI_Flash_Write(WRITE_ENABLE);?
?NotSelect_Flash();
}

void M25P16_Read_Id(u8 * id)
{
?u8 i;
?
?Select_Flash();?
?SPI_Flash_Write(READ_ID);?

?for(i=0;i<20;i++)
?{
??id[i] = SPI_Flash_Read();?
?}
?
?NotSelect_Flash();
}

void M25P16_Write_Status_Reg(u8 reg)
{
?Select_Flash();?
?SPI_Flash_Write(WRITE_STAUS_REG);?
?SPI_Flash_Write(reg);
?NotSelect_Flash();
}

?

void M25P16_Read_Data(u32 addr,u32 len,u8 *buf)
{
?u32 i;
?Select_Flash();?
?SPI_Flash_Write(READ_DATA);?
?SPI_Flash_Write((addr>>16) & 0xff);
?SPI_Flash_Write((addr>>8) & 0xff);
?SPI_Flash_Write(addr & 0xff);
?for(i=0;i<len;i++)
?{
??buf[i]=SPI_Flash_Read();
?}
?NotSelect_Flash();
}

?

//页编程函数,页编程前一定要进行页擦除!!!
void M25P16_Page_Program(u32 addr,u16 len,u8 *buf)
{
?u32 i;
?
?M25P16_Write_Enable();
?Select_Flash();?
?SPI_Flash_Write(PAGE_PROGRAM);?
?SPI_Flash_Write((addr>>16) & 0xff);
?SPI_Flash_Write((addr>>8) & 0xff);
?SPI_Flash_Write(addr & 0xff);

?for(i=0;i<len;i++)
??SPI_Flash_Write(buf[i]);

?NotSelect_Flash();

?while(M25P16_Read_Status_Reg()&0x01);?
}

?

以上函数搞懂了,特别市红色部分标注的为重点,对于spi也就基本清楚了,这些也算是最底层的函数了,提供基本的API供以后文件系统或其它地方使用。

?

----------------------------------------------------------

最后补上一些文件框架的说

首先我们把最底层的SPI初始化写上,函数如下:


弄清 stm32 SPI总线 基础和编程(转)
?
可见有芯片自带SPI模块??有给mp3芯片的,有给flash的,还有给无线网络的,还有给软件模拟spi时序供给触摸屏的控制器的,然后我们把这个.c文件配套的.h文件给下面具体的函数包含,就能正确的选取和使用了,当这些具体功能的函数写好了后,对应得.h函数就又继续给更高级别的应用层使用。

今天先就介绍下spi_flash模块了,等天补上其它3个模块的讲解,毕竟现在是基础哦。

?

读书人网 >编程

热点推荐