SPI协议的原理,网上大把的资料可以找到,这里记录一下SPI的初始化过程,以即以读取W25Q16型号为例的一个简单的SPI读写过程。
CubeMX配置:
SPI模式
有只发送、只接收、半双工和全双工模式;
根据自己的需求,和SPI设备支持的类型,这里选择全双工的SPI;
片选信号
因为我们这个SPI总线上会挂载多个从设备,而且只有一个主机,所以,这里禁用硬件片选,程序里面读写之前使用软件进行片选;
帧格式
这里选择Motorola帧格式;
选择TI格式不能设置CPOL和CPHA,而且必须要开启硬件NSS这里才能选择TI模式。
博主在网上搜了一些关于TI的SSP协议相关的资料,只有简简单单的一句:SSP总线兼容SPI,SSI 和Microwire 总线的接口。
博主又搜了以下Microwire协议,和SPI一样,只不过片选信号是高电平有效。
这是手册里关于TI模式的说明部分和波形图:
也不清楚这个SCK的触发信号是干嘛的,所以这里老老实实选择Motorola就行了。
数据长度
4-16bit可选,根据实际,选择8bit;
数据传输模式
第一个传输的bit是高位还是低位,这里根据设备来选择,好像MSB(先发送高位)的比较多,注意这里的会同时设置发送和接收,如果设置错了,发送的指令对方就无法识别,可能收到的就是0xFF这种值。
SPI速率
SPI的波特率是由时钟分频得到的,CubeMX很方便的还帮我们计算出来了时钟速率,可以根据自己的需求设置。
时钟极性和时钟相位
根据极性和相位的不同,可以有4种不同的工作模式,这个根据芯片手册支持的模式选择,注意,一条总线上尽量挂载支持相通模式的设备,这样就不用每次在设备切换之前重新配置SPI外设。
暂时还没研究到这里……
NSS脉冲
参考SPI的NSS 脉冲模式的作用,这里用不到;
NSS信号类型
因为前面禁用了硬件片选,所以这里只能设置位软件模式。
除此意外,还需要把连接硬件片选信号的GPIO,设置为输出模式。
CubeMX生成的代码如下:
SPI_HandleTypeDef SPI3_Handler; //SPI2句柄
void SPI3_Init(void)
{
SPI3_Handler.Instance = SPI3;
SPI3_Handler.Init.Mode = SPI_MODE_MASTER;
SPI3_Handler.Init.Direction = SPI_DIRECTION_2LINES;
SPI3_Handler.Init.DataSize = SPI_DATASIZE_8BIT;
SPI3_Handler.Init.CLKPolarity = SPI_POLARITY_LOW;
SPI3_Handler.Init.CLKPhase = SPI_PHASE_1EDGE;
SPI3_Handler.Init.NSS = SPI_NSS_SOFT;
SPI3_Handler.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_128;
SPI3_Handler.Init.FirstBit = SPI_FIRSTBIT_MSB;
SPI3_Handler.Init.TIMode = SPI_TIMODE_DISABLE;
SPI3_Handler.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
SPI3_Handler.Init.CRCPolynomial = 7;
SPI3_Handler.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
SPI3_Handler.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
HAL_SPI_Init(&SPI3_Handler);
__HAL_SPI_ENABLE(&SPI3_Handler);
}
void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(hspi->Instance==SPI3)
{
__HAL_RCC_SPI3_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_2;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF7_SPI3;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_3|GPIO_PIN_4;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF6_SPI3;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
}
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOA_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
/*Configure GPIO pin : PA4 */
GPIO_InitStruct.Pin = GPIO_PIN_4;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
同一总线上不同设备,工作在不同模式的时候,速率可能不一样,需要一个重新设置速率的函数:
void SPI3_SetSpeed(u8 SPI_BaudRatePrescaler)
{
assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));//判断有效性
__HAL_SPI_DISABLE(&SPI3_Handler); //关闭SPI
SPI3_Handler.Instance->CR1 &= 0XFFC7; //位3-5清零,用来设置波特率
SPI3_Handler.Instance->CR1 |= SPI_BaudRatePrescaler; //设置SPI速度
__HAL_SPI_ENABLE(&SPI3_Handler); //使能SPI
}
因为全双工的读写是同时的,封装一个读写函数:
//SPI2 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节
u8 SPI3_ReadWriteByte(u8 TxData)
{
u8 Rxdata;
HAL_SPI_TransmitReceive(&SPI3_Handler, &TxData, &Rxdata, 1, 1000);
return Rxdata; //返回收到的数据
}
根据W25QXX的数据手册,写一个读ID的函数:
//读取芯片ID
//返回值如下:
//0XEF13,表示芯片型号为W25Q80
//0XEF14,表示芯片型号为W25Q16
//0XEF15,表示芯片型号为W25Q32
//0XEF16,表示芯片型号为W25Q64
//0XEF17,表示芯片型号为W25Q128
u16 W25QXX_ReadID(void)
{
u16 Temp = 0;
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);
SPI3_ReadWriteByte(0x90);//发送读取ID命令
SPI3_ReadWriteByte(0x00);
SPI3_ReadWriteByte(0x00);
SPI3_ReadWriteByte(0x00);
Temp|=SPI3_ReadWriteByte(0xFF)<<8;
Temp|=SPI3_ReadWriteByte(0xFF);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);
return Temp;
}
最后写一个在主函数中调用的接口函数。
void W25QXX_Init(void)
{
MX_GPIO_Init();
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);
SPI3_Init();
SPI3_SetSpeed(SPI_BAUDRATEPRESCALER_4);
uint16_t W25QXX_TYPE=W25QXX_ReadID();//读取FLASH ID.
printf("W25QXX ID:0x%4xrn",W25QXX_TYPE);
}
下载运行,读取到了芯片ID。
上一篇:STM32F7使用SPI发送完成和接收中断
下一篇:SPI的NSS 脉冲模式的作用
设计资源 培训 开发板 精华推荐
- LTC2294IUP 演示板,DeMUX 双路 ADC,+3.0V,80Msps 12 位 70MHz
- PAM2803 3W大功率白光LED驱动器典型应用
- NCP380HMU20AGEVB,笔记本USB开关评估板
- 适用于工业应用的 C8051F524 MCU 的 C8051F52x-53x-DK、8051 开发系统
- LTC1727ES8-2.5、3.3V 和 5V 三路电源监视器的典型应用
- 线性电源
- DI-117 - 15 W、12 V 适配器,空载功耗低于 100 mW
- AM2LS-0512S-NZ 12V 2W DC-DC 转换器的典型应用
- LT6656BCDC-5、5V 微控制器电压基准和稳压器的典型应用
- 使用 Microchip Technology 的 MIC2810-4GPYML 的参考设计