聲明:以下內(nèi)容均為本人學(xué)習(xí)心得。
一、基礎(chǔ)知識(shí)。
華大HC32F460 提供的SPI是4線式和3線式。搭載4個(gè)通道的串行外設(shè)接口,支持高速全雙工串行同步傳輸。
4線式:SCK、MOSI、MISO、SS0~SS3。3線式:SCK、MOSI、MISO。
SPI數(shù)據(jù)發(fā)送時(shí):傳送數(shù)據(jù)先進(jìn)入發(fā)送緩沖器(TX_BUFF),再將TX_BUFF的數(shù)據(jù)復(fù)制到移位寄存器(shifter),shifter依次發(fā)出數(shù)據(jù);SPI數(shù)據(jù)接受時(shí),數(shù)據(jù)從shifter依次移入,移入完成后再將shifter的數(shù)據(jù)復(fù)制到接收緩沖器(RX_BUFF)。數(shù)據(jù)傳輸時(shí),根據(jù)移位順序控制位SPI_CFG2.LSBF和奇偶校驗(yàn)控制位SPI_CR1.PAE的設(shè)置分4種情況:
- MSB先傳,奇偶校驗(yàn)無效。
- LSB先傳,奇偶校驗(yàn)無效。
- MSB先傳,奇偶校驗(yàn)有效時(shí)。
- LSB先傳,奇偶校驗(yàn)有效時(shí)。
傳送格式:
- CPHA=0的情況,當(dāng)SPI_CFG2.CPHA位為0時(shí),SPI在SCK的奇數(shù)邊沿進(jìn)行數(shù)據(jù)采樣,偶數(shù)邊沿進(jìn)行數(shù)據(jù)更新。
- CPHA=1的情況,當(dāng)SPI_CFG2.CPHA位為1時(shí),SPI在SCK的奇數(shù)邊沿進(jìn)行數(shù)據(jù)更新,偶數(shù)邊沿進(jìn)行數(shù)據(jù)采樣。
通信方式:
- 全雙工同步串行通信方式
當(dāng)SPI_CR1.TXMDS位為“0”時(shí),SPI運(yùn)行在全雙工同步串行通信方式。
注意的是:傳輸結(jié)束時(shí),如果接收數(shù)據(jù)緩沖寄存器為空,SPI將會(huì)把接收到的數(shù)據(jù)從移位寄存器復(fù)制到接收數(shù)據(jù)緩沖寄存器中,接收數(shù)據(jù)緩沖寄存器滿的標(biāo)志位被置成1(RDFF),并產(chǎn)生一個(gè)接收數(shù)據(jù)滿的中斷請(qǐng)求(SPRI)。 如果接收數(shù)據(jù)緩沖寄存器還保持著上次收到的數(shù)據(jù)而沒有被系統(tǒng)讀取,SPI會(huì)將數(shù)據(jù)過載標(biāo)志位置成1,本次數(shù)據(jù)接收無效,接收移位寄存器中的數(shù)據(jù)將被丟棄。
- 只進(jìn)行發(fā)送通信方式
當(dāng)SPI_CR1.TXMDS位為“1”時(shí),SPI運(yùn)行在只發(fā)送通信方式。
SPI主機(jī)模式的初始化:
①設(shè)置通信配置寄存器1(SPI_CFG1),包括有波特率的設(shè)定,使用幀數(shù)的設(shè)定,各種遲延時(shí)間的設(shè)定等。
②設(shè)置通信配置寄存器2(SPI_CFG2),包括有SS電平設(shè)定,數(shù)據(jù)移位順序設(shè)定,各種延遲的允許位的設(shè)定,數(shù)據(jù)格式及時(shí)鐘極性相位的設(shè)定等。
③如需要使用中斷,請(qǐng)?jiān)O(shè)置系統(tǒng)的中斷寄存器。
④如需要使用DMA,請(qǐng)?jiān)O(shè)置DMA的相關(guān)寄存器。
⑤設(shè)定輸入輸出管腳。
⑥設(shè)定SPI控制寄存器SPI_CR1,包括有模式及運(yùn)行方式的設(shè)定,自診斷功能的設(shè)定,奇偶校驗(yàn)的設(shè)定等。
⑦確認(rèn)SPI_CR1寄存器的設(shè)置。
⑧清除各種標(biāo)志位。
⑨設(shè)置中斷許可位
⑩將控制寄存器SPI_CR1的SPE位設(shè)置成1,動(dòng)作開始。
SPI從機(jī)模式的初始化:
①設(shè)置通信配置寄存器1(SPI_CFG1),包括使用幀數(shù)的設(shè)定。
②設(shè)置通信配置寄存器2(SPI_CFG2),包括有傳輸速率、數(shù)據(jù)格式及時(shí)鐘極性相位的設(shè)定等。
③如需要使用中斷,請(qǐng)?jiān)O(shè)置系統(tǒng)的中斷寄存器。
④如需要使用DMA,請(qǐng)?jiān)O(shè)置DMA的相關(guān)寄存器。
⑤設(shè)定輸入輸出管腳。
⑥設(shè)定SPI控制寄存器SPI_CR1,包括有模式及運(yùn)行方式的設(shè)定,自診斷功能的設(shè)定,奇偶校驗(yàn)的設(shè)定等。
⑦確認(rèn)SPI_CR1寄存器的設(shè)置。
⑧清除各種標(biāo)志位。
⑨設(shè)置中斷許可位
⑩將控制寄存器SPI_CR1的SPE位設(shè)置成1,動(dòng)作開始。
SPI作為主機(jī)時(shí)的數(shù)據(jù)傳送處理流程:
①等待數(shù)據(jù)發(fā)送緩沖寄存器空的中斷或通過輪詢方式確認(rèn)數(shù)據(jù)發(fā)送緩沖寄存器處于空狀態(tài)。
②向數(shù)據(jù)寄存器SPI_DR寫入要發(fā)送的數(shù)據(jù)。
③重復(fù)①②步驟直到最后一個(gè)數(shù)據(jù)發(fā)送完成。
④將發(fā)送數(shù)據(jù)寄存器空中斷的允許位TXIE清零,同時(shí)將SPI閑置狀態(tài)中斷允許位IDIE設(shè)為1。
⑤發(fā)送SPI閑置狀態(tài)中斷。
⑥將SPE置0,停止SPI動(dòng)作,同時(shí)將IDIE清零。
數(shù)據(jù)接收處理流程:
①等待數(shù)據(jù)接收緩沖寄存器空的中斷或通過輪詢方式確認(rèn)數(shù)據(jù)接收緩沖寄存器處于滿狀態(tài)。
②通過訪問SPI_DR從接收緩沖寄存器讀取數(shù)據(jù)
③重復(fù)①②步驟直到最后一個(gè)接收數(shù)據(jù)被讀取。
④將數(shù)據(jù)接收緩沖寄存器滿的中斷允許位RXIE清零。
通信錯(cuò)誤處理流程:
①等待通信錯(cuò)誤中斷或者通過輪詢方式確認(rèn)通信錯(cuò)誤標(biāo)志位(MODFERF/OVRERF/UDRERF/PERF)被置成1。
②確認(rèn)SS0狀態(tài),排除模式故障錯(cuò)誤。
③將SPE清零,停止SPI動(dòng)作。
④將所有SPI中斷允許位清零,屏蔽SPI中斷。
⑤通過錯(cuò)誤標(biāo)志位確定通信錯(cuò)誤種類,進(jìn)行通訊錯(cuò)誤處理。
⑥將錯(cuò)誤標(biāo)志位清零。
⑦啟動(dòng)SPI,重新開始通信。
二、實(shí)驗(yàn)代碼
實(shí)驗(yàn)樣例配置通過宏開關(guān)選擇主機(jī)或從機(jī)模式,將主機(jī)代碼下載到一塊板子,從機(jī)代碼下載到另一塊板子,通過排線將兩塊板子的SPI接口相連接,主機(jī)按下按鍵K2,觸發(fā)一次數(shù)據(jù)收發(fā),主機(jī)和從機(jī)對(duì)收到的數(shù)據(jù)和發(fā)送的數(shù)據(jù)進(jìn)行比較,相同的LED_BLUE亮,不同則LED_RED亮。
設(shè)置宏:EXAMPLE_SPI_MASTER_SLAVE = SPI_MASTER為主機(jī)模式。
? ? ? ? ? ? ? EXAMPLE_SPI_MASTER_SLAVE = SPI_SLAVE為從機(jī)模式。
①各參數(shù)配置
/* unlock/lock peripheral */
#define EXAMPLE_PERIPH_WE (LL_PERIPH_GPIO | LL_PERIPH_EFM | LL_PERIPH_FCG | \
LL_PERIPH_PWC_CLK_RMU | LL_PERIPH_SRAM)
#define EXAMPLE_PERIPH_WP (LL_PERIPH_EFM | LL_PERIPH_FCG | LL_PERIPH_SRAM)
/* Configuration for Example */
#define EXAMPLE_SPI_MASTER_SLAVE (SPI_MASTER)
#define EXAMPLE_SPI_BUF_LEN (128UL) //SPI接收或發(fā)送緩存區(qū)大小
/* SPI definition */
#define SPI_UNIT (CM_SPI1)
#define SPI_CLK (FCG1_PERIPH_SPI1)
#define SPI_TX_EVT_SRC (EVT_SRC_SPI1_SPTI)//SPI發(fā)送中斷序號(hào):300
#define SPI_RX_EVT_SRC (EVT_SRC_SPI1_SPRI)//SPI接收中斷序號(hào):299
/* DMA definition */
#define DMA_UNIT (CM_DMA1)
#define DMA_CLK (FCG0_PERIPH_DMA1 | FCG0_PERIPH_AOS)
#define DMA_TX_CH (DMA_CH0)
#define DMA_TX_TRIG_CH (AOS_DMA1_0)
#define DMA_RX_CH (DMA_CH1)
#define DMA_RX_INT_CH (DMA_INT_TC_CH1)
#define DMA_RX_TRIG_CH (AOS_DMA1_1)
#define DMA_RX_INT_SRC (INT_SRC_DMA1_TC1)
#define DMA_RX_IRQ_NUM (INT006_IRQn)
/* SS = PA7 */
#define SPI_SS_PORT (GPIO_PORT_A)
#define SPI_SS_PIN (GPIO_PIN_07)
#define SPI_SS_FUNC (GPIO_FUNC_42)
/* SCK = PA8 */
#define SPI_SCK_PORT (GPIO_PORT_A)
#define SPI_SCK_PIN (GPIO_PIN_08)
#define SPI_SCK_FUNC (GPIO_FUNC_43)
/* MOSI = PB0 */
#define SPI_MOSI_PORT (GPIO_PORT_B)
#define SPI_MOSI_PIN (GPIO_PIN_00)
#define SPI_MOSI_FUNC (GPIO_FUNC_40)
/* MISO = PC5 */
#define SPI_MISO_PORT (GPIO_PORT_C)
#define SPI_MISO_PIN (GPIO_PIN_05)
#define SPI_MISO_FUNC (GPIO_FUNC_41)
DMA的參數(shù)配置就不詳細(xì)解讀了,有不明白的可以去看我的DMA實(shí)驗(yàn)代碼詳解。
對(duì)于各個(gè)GPIO引腳的功能選擇,我們需要查看數(shù)據(jù)手冊(cè)的表2-2 Func32~63,里面有具體說明。
②發(fā)送、接收緩存區(qū)和標(biāo)志定義
static char u8TxBuf[EXAMPLE_SPI_BUF_LEN] = "SPI Master/Slave example: Communication between two boards!";
static char u8RxBuf[EXAMPLE_SPI_BUF_LEN];
static __IO en_flag_status_t enRxCompleteFlag = RESET;
③SPI串口通信配置
static void SPI_Config(void)
{
stc_spi_init_t stcSpiInit;
stc_dma_init_t stcDmaInit;
stc_irq_signin_config_t stcIrqSignConfig;
/* Configure Port */
GPIO_SetFunc(SPI_SS_PORT, SPI_SS_PIN, SPI_SS_FUNC);
GPIO_SetFunc(SPI_SCK_PORT, SPI_SCK_PIN, SPI_SCK_FUNC);
GPIO_SetFunc(SPI_MOSI_PORT, SPI_MOSI_PIN, SPI_MOSI_FUNC);
GPIO_SetFunc(SPI_MISO_PORT, SPI_MISO_PIN, SPI_MISO_FUNC);
/* Configuration SPI */
FCG_Fcg1PeriphClockCmd(SPI_CLK, ENABLE);
SPI_StructInit(&stcSpiInit);
stcSpiInit.u32WireMode = SPI_4_WIRE;
stcSpiInit.u32TransMode = SPI_FULL_DUPLEX;
stcSpiInit.u32MasterSlave = EXAMPLE_SPI_MASTER_SLAVE;
stcSpiInit.u32Parity = SPI_PARITY_INVD;
stcSpiInit.u32SpiMode = SPI_MD_1;//在奇數(shù)邊沿?cái)?shù)據(jù)發(fā)生變化,在偶數(shù)邊沿進(jìn)行數(shù)據(jù)采樣
stcSpiInit.u32BaudRatePrescaler = SPI_BR_CLK_DIV64;
stcSpiInit.u32DataBits = SPI_DATA_SIZE_8BIT;
stcSpiInit.u32FirstBit = SPI_FIRST_MSB;
stcSpiInit.u32FrameLevel = SPI_1_FRAME;
(void)SPI_Init(SPI_UNIT, &stcSpiInit);
/* DMA configuration */
FCG_Fcg0PeriphClockCmd(DMA_CLK, ENABLE);
(void)DMA_StructInit(&stcDmaInit);
stcDmaInit.u32BlockSize = 1UL;
stcDmaInit.u32TransCount = EXAMPLE_SPI_BUF_LEN;
stcDmaInit.u32DataWidth = DMA_DATAWIDTH_8BIT;
/* Configure TX */
stcDmaInit.u32SrcAddrInc = DMA_SRC_ADDR_INC;
stcDmaInit.u32DestAddrInc = DMA_DEST_ADDR_FIX;
stcDmaInit.u32SrcAddr = (uint32_t)(&u8TxBuf[0]);
stcDmaInit.u32DestAddr = (uint32_t)(&SPI_UNIT->DR);
if (LL_OK != DMA_Init(DMA_UNIT, DMA_TX_CH, &stcDmaInit)) {
for (;;) {
}
}
AOS_SetTriggerEventSrc(DMA_TX_TRIG_CH, SPI_TX_EVT_SRC);
/* Configure RX */
stcDmaInit.u32IntEn = DMA_INT_ENABLE;
stcDmaInit.u32SrcAddrInc = DMA_SRC_ADDR_FIX;
stcDmaInit.u32DestAddrInc = DMA_DEST_ADDR_INC;
stcDmaInit.u32SrcAddr = (uint32_t)(&SPI_UNIT->DR);
stcDmaInit.u32DestAddr = (uint32_t)(&u8RxBuf[0]);
if (LL_OK != DMA_Init(DMA_UNIT, DMA_RX_CH, &stcDmaInit)) {
for (;;) {
}
}
AOS_SetTriggerEventSrc(DMA_RX_TRIG_CH, SPI_RX_EVT_SRC);
/* DMA receive NVIC configure */
stcIrqSignConfig.enIntSrc = DMA_RX_INT_SRC;
stcIrqSignConfig.enIRQn = DMA_RX_IRQ_NUM;
stcIrqSignConfig.pfnCallback = &DMA_TransCompleteCallback;
(void)INTC_IrqSignIn(&stcIrqSignConfig);
NVIC_ClearPendingIRQ(stcIrqSignConfig.enIRQn);
NVIC_SetPriority(stcIrqSignConfig.enIRQn, DDL_IRQ_PRIO_DEFAULT);
NVIC_EnableIRQ(stcIrqSignConfig.enIRQn);
/* Enable DMA and channel */
DMA_Cmd(DMA_UNIT, ENABLE);
DMA_ChCmd(DMA_UNIT, DMA_TX_CH, ENABLE);
DMA_ChCmd(DMA_UNIT, DMA_RX_CH, ENABLE);
}
代碼中,GPIO_SetFunc(SPI_SS_PORT, ? SPI_SS_PIN, ? SPI_SS_FUNC); 目的就是把對(duì)應(yīng)的GPIO引腳配置自己想要的功能,類比于STM32中的映射吧。
我們?cè)敿?xì)講講配置SPI的結(jié)構(gòu)體:
typedef struct {
uint32_t u32WireMode; /*!< 配置4線或3線模式*/
uint32_t u32TransMode; /*!< 傳輸模式:全雙工or只進(jìn)行發(fā)送通信方式*/
uint32_t u32MasterSlave; /*!< 設(shè)置主機(jī)模式or從機(jī)模式 */
uint32_t u32ModeFaultDetect; /*!< 使能 模式故障錯(cuò)誤檢測(cè)*/
uint32_t u32Parity; /*!< 使能 SPI奇偶校驗(yàn) */
uint32_t u32SpiMode; /*!< 配置SPI模式:主機(jī)、從機(jī)、主機(jī)時(shí)鐘同步、從機(jī)時(shí)鐘同*/
uint32_t u32BaudRatePrescaler; /*!< 配置SPI波特率預(yù)分頻*/
uint32_t u32DataBits; /*!< 配置 傳輸\接收數(shù)據(jù)長(zhǎng)度 */
uint32_t u32FirstBit; /*!< 配置開始位是MSB 還是 LSB */
uint32_t u32SuspendMode; /*!< SPI通信暫停功能 */
uint32_t u32FrameLevel; /*!< SPI幀數(shù)設(shè)定位*/
} stc_spi_init_t;
④DMA傳輸完成回調(diào)函數(shù)
static void DMA_TransCompleteCallback(void)
{
enRxCompleteFlag = SET;
DMA_ClearTransCompleteStatus(DMA_UNIT, DMA_RX_INT_CH);
}
⑤DMA重裝載配置
static void DMA_ReloadConfig(void)
{
DMA_SetSrcAddr(DMA_UNIT, DMA_TX_CH, (uint32_t)(&u8TxBuf[0]));
DMA_SetTransCount(DMA_UNIT, DMA_TX_CH, EXAMPLE_SPI_BUF_LEN);
DMA_SetDestAddr(DMA_UNIT, DMA_RX_CH, (uint32_t)(&u8RxBuf[0]));
DMA_SetTransCount(DMA_UNIT, DMA_RX_CH, EXAMPLE_SPI_BUF_LEN);
/* Enable DMA channel */
DMA_ChCmd(DMA_UNIT, DMA_TX_CH, ENABLE);
DMA_ChCmd(DMA_UNIT, DMA_RX_CH, ENABLE);
}
這里為啥需要重裝載呢,因?yàn)閭鬏斖暌淮危珼MA指向源地址變了,需要重新回到初始的源地址,即緩存區(qū):TxBuf[0]和RxBuf[0]
⑥主函數(shù)
int32_t main(void)
{
/* Peripheral registers write unprotected */
LL_PERIPH_WE(EXAMPLE_PERIPH_WE);
/* Configure BSP */
BSP_CLK_Init();
BSP_LED_Init();
BSP_KEY_Init();
/* Configure SPI */
SPI_Config();
/* Peripheral registers write protected */
LL_PERIPH_WP(EXAMPLE_PERIPH_WP);
for (;;) {
/* Wait key trigger in master mode */
#if (EXAMPLE_SPI_MASTER_SLAVE == SPI_MASTER)
while (RESET == BSP_KEY_GetStatus(BSP_KEY_2)) {
}
#endif
enRxCompleteFlag = RESET;
memset(u8RxBuf, 0, EXAMPLE_SPI_BUF_LEN);
DMA_ReloadConfig();
/* Enable SPI */
SPI_Cmd(SPI_UNIT, ENABLE);
/* Waiting for completion of reception */
while (RESET == enRxCompleteFlag) {
}
/* Disable SPI */
SPI_Cmd(SPI_UNIT, DISABLE);
/* Compare u8TxBuf and u8RxBuf */
if (0 == memcmp(u8TxBuf, u8RxBuf, EXAMPLE_SPI_BUF_LEN)) {
BSP_LED_On(LED_BLUE);
BSP_LED_Off(LED_RED);
} else {
BSP_LED_On(LED_RED);
BSP_LED_Off(LED_BLUE);
}
#if (EXAMPLE_SPI_MASTER_SLAVE == SPI_MASTER)
/* Wait for the slave to be ready */
DDL_DelayMS(10U);
#endif
}
}
通過while (RESET == BSP_KEY_GetStatus(BSP_KEY_2))? 來判斷按鍵K2是否按下,按下為低電平,觸發(fā)SPI通信。
? ? ? ? enRxCompleteFlag = RESET;
? ? ? ? memset(u8RxBuf, 0, EXAMPLE_SPI_BUF_LEN);
? ? ? ? DMA_ReloadConfig();
這3句目的是為了初始化 接收緩存區(qū),如果一開始接收緩存區(qū)有數(shù)據(jù),則SPI會(huì)觸發(fā) 模式錯(cuò)誤。文章來源:http://www.zghlxwxcb.cn/news/detail-676275.html
實(shí)驗(yàn)代碼講解到此結(jié)束,有什么疑問可以私信我~文章來源地址http://www.zghlxwxcb.cn/news/detail-676275.html
到了這里,關(guān)于華大HC32F460 SPI+DMA通信實(shí)驗(yàn)代碼詳解的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!