说明:驱动基于STm32G031K6测试,其他型号需自行做改动。
SPI在开始配置的时候遇到些问题,这里也记录下,我这边用的是SPI2,其他SPI也可以参考
SPI2 初始化:
void STM32LLSpi2Init(void)
{
LL_SPI_InitTypeDef SPI_InitStruct = {0};
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_SPI2); //使能外设时钟
LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOB); //GPIO时钟使能
/**SPI2 GPIO Configuration
PB6 ------> SPI2_MISO
PB7 ------> SPI2_MOSI
PB8 ------> SPI2_SCK
*/
GPIO_InitStruct.Pin = gpioSPI2_MISO_PIN; //MISOPin指定
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE; //io模式配置为复用功能
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH; //设置为高速率
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL; //设置Pin为输出模式
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; //输入口设置不带上拉
GPIO_InitStruct.Alternate = LL_GPIO_AF_4; //当前芯片PB6复用功能AF4为SPI2的MISO,其他芯片的配置参考相应的芯片手册
LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_InitStruct.Pin = gpioSPI2_MOSI_PIN; //MOSIPin指定
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE; //
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH; //
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL; //
GPIO_InitStruct.Pull = LL_GPIO_PULL_DOWN; //输出口配置为下拉
GPIO_InitStruct.Alternate = LL_GPIO_AF_1; //io 功能选择
LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_InitStruct.Pin = gpioSPI2_SCK_PIN;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_DOWN; //输出口配置为下拉
GPIO_InitStruct.Alternate = LL_GPIO_AF_1; //io 功能选择
LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* SPI2 interrupt Init */ //因为SPI需要进行不同设备数据读取,所以这里不使用中断模式会更方便
//NVIC_SetPriority(SPI2_IRQn, 0);
//NVIC_EnableIRQ(SPI2_IRQn);
SPI_InitStruct.TransferDirection = LL_SPI_FULL_DUPLEX; //spi功能选择全双工
SPI_InitStruct.Mode = LL_SPI_MODE_MASTER; //SPI主设备模式
SPI_InitStruct.DataWidth = LL_SPI_DATAWIDTH_8BIT; //数据宽度8位
SPI_InitStruct.ClockPolarity = LL_SPI_POLARITY_HIGH; //Clk空闲时状态为高 根据使用的从设备进行配置
SPI_InitStruct.ClockPhase = LL_SPI_PHASE_2EDGE; //在第二次时钟跳变开始发送数据
SPI_InitStruct.NSS = LL_SPI_NSS_SOFT; //片选方式为软件设置
SPI_InitStruct.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV2; //时钟波特率设置
SPI_InitStruct.BitOrder = LL_SPI_MSB_FIRST; //字节传输方式,从高位开始
SPI_InitStruct.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE;//不开启crc校验
SPI_InitStruct.CRCPoly = 7; //CRC多项式
LL_SPI_Init(SPI2, &SPI_InitStruct);
LL_SPI_SetStandard(SPI2, LL_SPI_PROTOCOL_MOTOROLA); //使用的SPI协议,可选MT和TI的
LL_SPI_EnableNSSPulseMgt(SPI2); //仅做主设备时可用
/* Configure the SPI2 FIFO Threshold */
LL_SPI_SetRxFIFOThreshold(SPI2, LL_SPI_RX_FIFO_TH_QUARTER);//设置RX非空事件产生的FIFO阈值,根据通信时最小数据的大小设置,我这边最小为8位,所以设置为四分之一
LL_SPI_Enable(SPI2); //最后不要忘记使能SPI2,也可以在其他初始化完了之后的地方使能,为了防止忘记,这里先使能了
}
我个人在配置的时候,就是因为GPIO_InitStruct.Alternate 都设置的默认的AF0,导致一直通信不成功,曾一度怀疑是硬件问题。这里也提醒大家一下。
SPI数据收发:
//通过SPI在从设备内读写一个字节的数据,SPI特性,在MOSI写数据的时候MISO会获得应答数据
uint8_t STM32LLSpi2WRByte(uint8_t byte) //因为采用的非中断方式,所以数据直接获取
{
uint8_t wait_cnt=0;
while(!LL_SPI_IsActiveFlag_TXE(SPI2)) //等待数据发送完成
{
if(wait_cnt++ >100)
break;
}
LL_SPI_TransmitData8(SPI2, byte); //发送8位数据
wait_cnt = 0;
while(!LL_SPI_IsActiveFlag_RXNE(SPI2)) //等待接收缓存非空
{
if(wait_cnt++ >100)
break;
}
return LL_SPI_ReceiveData8(SPI2); //返回SPI2接收到的数据
}
SPI的读写多个字节的操作,都可以通过循环调用读写一个字节的函数去实现,这里不做说明。
说明:驱动基于STm32G031K6测试,其他型号需自行做改动。
SPI在开始配置的时候遇到些问题,这里也记录下,我这边用的是SPI2,其他SPI也可以参考
SPI2 初始化:
void STM32LLSpi2Init(void)
{
LL_SPI_InitTypeDef SPI_InitStruct = {0};
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_SPI2); //使能外设时钟
LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOB); //GPIO时钟使能
/**SPI2 GPIO Configuration
PB6 ------> SPI2_MISO
PB7 ------> SPI2_MOSI
PB8 ------> SPI2_SCK
*/
GPIO_InitStruct.Pin = gpioSPI2_MISO_PIN; //MISOPin指定
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE; //io模式配置为复用功能
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH; //设置为高速率
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL; //设置Pin为输出模式
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; //输入口设置不带上拉
GPIO_InitStruct.Alternate = LL_GPIO_AF_4; //当前芯片PB6复用功能AF4为SPI2的MISO,其他芯片的配置参考相应的芯片手册
LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_InitStruct.Pin = gpioSPI2_MOSI_PIN; //MOSIPin指定
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE; //
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH; //
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL; //
GPIO_InitStruct.Pull = LL_GPIO_PULL_DOWN; //输出口配置为下拉
GPIO_InitStruct.Alternate = LL_GPIO_AF_1; //io 功能选择
LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_InitStruct.Pin = gpioSPI2_SCK_PIN;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_DOWN; //输出口配置为下拉
GPIO_InitStruct.Alternate = LL_GPIO_AF_1; //io 功能选择
LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* SPI2 interrupt Init */ //因为SPI需要进行不同设备数据读取,所以这里不使用中断模式会更方便
//NVIC_SetPriority(SPI2_IRQn, 0);
//NVIC_EnableIRQ(SPI2_IRQn);
SPI_InitStruct.TransferDirection = LL_SPI_FULL_DUPLEX; //spi功能选择全双工
SPI_InitStruct.Mode = LL_SPI_MODE_MASTER; //SPI主设备模式
SPI_InitStruct.DataWidth = LL_SPI_DATAWIDTH_8BIT; //数据宽度8位
SPI_InitStruct.ClockPolarity = LL_SPI_POLARITY_HIGH; //Clk空闲时状态为高 根据使用的从设备进行配置
SPI_InitStruct.ClockPhase = LL_SPI_PHASE_2EDGE; //在第二次时钟跳变开始发送数据
SPI_InitStruct.NSS = LL_SPI_NSS_SOFT; //片选方式为软件设置
SPI_InitStruct.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV2; //时钟波特率设置
SPI_InitStruct.BitOrder = LL_SPI_MSB_FIRST; //字节传输方式,从高位开始
SPI_InitStruct.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE;//不开启crc校验
SPI_InitStruct.CRCPoly = 7; //CRC多项式
LL_SPI_Init(SPI2, &SPI_InitStruct);
LL_SPI_SetStandard(SPI2, LL_SPI_PROTOCOL_MOTOROLA); //使用的SPI协议,可选MT和TI的
LL_SPI_EnableNSSPulseMgt(SPI2); //仅做主设备时可用
/* Configure the SPI2 FIFO Threshold */
LL_SPI_SetRxFIFOThreshold(SPI2, LL_SPI_RX_FIFO_TH_QUARTER);//设置RX非空事件产生的FIFO阈值,根据通信时最小数据的大小设置,我这边最小为8位,所以设置为四分之一
LL_SPI_Enable(SPI2); //最后不要忘记使能SPI2,也可以在其他初始化完了之后的地方使能,为了防止忘记,这里先使能了
}
我个人在配置的时候,就是因为GPIO_InitStruct.Alternate 都设置的默认的AF0,导致一直通信不成功,曾一度怀疑是硬件问题。这里也提醒大家一下。
SPI数据收发:
//通过SPI在从设备内读写一个字节的数据,SPI特性,在MOSI写数据的时候MISO会获得应答数据
uint8_t STM32LLSpi2WRByte(uint8_t byte) //因为采用的非中断方式,所以数据直接获取
{
uint8_t wait_cnt=0;
while(!LL_SPI_IsActiveFlag_TXE(SPI2)) //等待数据发送完成
{
if(wait_cnt++ >100)
break;
}
LL_SPI_TransmitData8(SPI2, byte); //发送8位数据
wait_cnt = 0;
while(!LL_SPI_IsActiveFlag_RXNE(SPI2)) //等待接收缓存非空
{
if(wait_cnt++ >100)
break;
}
return LL_SPI_ReceiveData8(SPI2); //返回SPI2接收到的数据
}
SPI的读写多个字节的操作,都可以通过循环调用读写一个字节的函数去实现,这里不做说明。
举报