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的讀寫多個字節的操作,都可以通過循環調用讀寫一個字節的函數去實現,這裏不做說明。