- void SYS_Init(void)
- {
- /*---------------------------------------------------------------------------------------------------------*/
- /* Init System Clock */
- /*---------------------------------------------------------------------------------------------------------*/
- /* Unlock protected registers */
- SYS_UnlockReg();
- /* Enable HIRC clock (Internal RC 48MHz) */
- CLK_EnableXtalRC(CLK_PWRCTL_HIRCEN_Msk);
- /* Wait for HIRC clock ready */
- CLK_WaitClockReady(CLK_STATUS_HIRCSTB_Msk);
- /* Select HCLK clock source as HIRC and HCLK source divider as 1 */
- CLK_SetHCLK(CLK_CLKSEL0_HCLKSEL_HIRC, CLK_CLKDIV0_HCLK(1));
- /* Set both PCLK0 and PCLK1 as HCLK */
- CLK->PCLKDIV = CLK_PCLKDIV_APB0DIV_DIV1 | CLK_PCLKDIV_APB1DIV_DIV1;
- /* Select IP clock source */
- /* Select UART0 clock source is HIRC */
- CLK_SetModuleClock(UART0_MODULE, CLK_CLKSEL1_UART0SEL_HIRC, CLK_CLKDIV0_UART0(1));
- /* Select UART1 clock source is HIRC */
- CLK_SetModuleClock(UART1_MODULE, CLK_CLKSEL1_UART1SEL_HIRC, CLK_CLKDIV0_UART1(1));
- /* Select PCLK1 as the clock source of SPI0 */
- CLK_SetModuleClock(SPI0_MODULE, CLK_CLKSEL2_SPI0SEL_PCLK1, MODULE_NoMsk);
-
- /* Enable UART0 peripheral clock */
- CLK_EnableModuleClock(UART0_MODULE);
- /* Enable UART1 peripheral clock */
- CLK_EnableModuleClock(UART1_MODULE);
- /* Enable PDMA module clock */
- CLK_EnableModuleClock(PDMA_MODULE);
- /* Enable SPI0 peripheral clock */
- CLK_EnableModuleClock(SPI0_MODULE);
-
- /* Update System Core Clock */
- /* User can use SystemCoreClockUpdate() to calculate PllClock, SystemCoreClock and CycylesPerUs automatically. */
- SystemCoreClockUpdate();
- /*---------------------------------------------------------------------------------------------------------*/
- /* Init I/O Multi-function */
- /*---------------------------------------------------------------------------------------------------------*/
- /* Set PB multi-function pins for UART0 RXD=PB.12 and TXD=PB.13 */
- SYS->GPB_MFPH = (SYS->GPB_MFPH & ~(SYS_GPB_MFPH_PB12MFP_Msk | SYS_GPB_MFPH_PB13MFP_Msk)) | \
- (SYS_GPB_MFPH_PB12MFP_UART0_RXD | SYS_GPB_MFPH_PB13MFP_UART0_TXD);
- /* Set PB multi-function pins for UART1 RXD(PB.2) and TXD(PB.3) */
- SYS->GPB_MFPL = (SYS->GPB_MFPL & ~(SYS_GPB_MFPL_PB2MFP_Msk | SYS_GPB_MFPL_PB3MFP_Msk)) | \
- (SYS_GPB_MFPL_PB2MFP_UART1_RXD | SYS_GPB_MFPL_PB3MFP_UART1_TXD);
- /* Setup SPI0 multi-function pins */
- /* PA.3 is SPI0_SS, PA.2 is SPI0_CLK,
- PA.1 is SPI0_MISO, PA.0 is SPI0_MOSI*/
- SYS->GPA_MFPL = (SYS->GPA_MFPL & ~(SYS_GPA_MFPL_PA3MFP_Msk |
- SYS_GPA_MFPL_PA2MFP_Msk |
- SYS_GPA_MFPL_PA1MFP_Msk |
- SYS_GPA_MFPL_PA0MFP_Msk)) |
- (SYS_GPA_MFPL_PA3MFP_SPI0_SS |
- SYS_GPA_MFPL_PA2MFP_SPI0_CLK |
- SYS_GPA_MFPL_PA1MFP_SPI0_MISO |
- SYS_GPA_MFPL_PA0MFP_SPI0_MOSI);
-
- /* Lock protected registers */
- SYS_LockReg();
- }
依据新塘的代码风格,系统的初始化被安排在SYS_Init()中完成所有的系统时钟和各个外设时钟的初始化,在初始化时钟之后初始化外设的引脚,设置各种复用,这里初始化了UART0 和 UART1的外设以及SPI0的外设。因为采用自动的NSS,所以这里将NSS的引脚也复用。
SPI初始化
- void SPI_Init(void)
- {
- /*---------------------------------------------------------------------------------------------------------*/
- /* Init SPI */
- /*---------------------------------------------------------------------------------------------------------*/
- /* Configure as a master, clock idle low, 32-bit transaction, drive output on falling clock edge and latch input on rising edge. */
- /* Set IP clock divider. SPI clock rate = 2 MHz */
- SPI_Open(SPI0, SPI_MASTER, SPI_MODE_0, 8, SPI_CLK_FREQ);
- /* Enable the automatic hardware slave select function. Select the SS pin and configure as low-active. */
- SPI_EnableAutoSS(SPI0, SPI_SS, SPI_SS_ACTIVE_LOW);
- }
SPI的初始化依旧是两个函数。
第一个SPI_Open(SPI_T *spi,
uint32_t u32MasterSlave,
uint32_t u32SPIMode,
uint32_t u32DataWidth,
uint32_t u32BusClock));
内置5个参数,
SPI_T *spi:选定开启的SPI口。我们选择SPI0。
uint32_t u32MasterSlave:选择主模式或者从模式。这里选择主模式。
uint32_t u32SPIMode:选择SPI的模式,这里有4个待选参数,主要是确定SPI的空闲电平,在上升沿还是下降沿读取数据,在第几个沿读数据等,这个在每个MCU都有该配置,只是M031这里做了一个集合。下图为四个可选参数。
下面给一张控制SPI设备的各个寄存器值设定图,
我这里选择SPI_MODE_0,即:将SPI时钟的空闲状态设为低电平,选择数据在SPI总线时钟的下降沿传输,选择数据在SPI总线时钟的上升沿锁存。
uint32_t u32DataWidth:为数据宽度,可在8-32位之间选择,我这里选择最低的8位,这个是比较常见的数据。
uint32_t u32BusClock:设置SPI的时钟,最大24MHz。
第二个函数为SPI_EnableAutoSS(SPI0, SPI_SS, SPI_SS_ACTIVE_LOW);设置硬件使能,自动从机选择功能。如果无需自动从选,可使用SPI_DisableAutoSS(SPI0);进行屏蔽,此时可任选一个通用IO控制从选信号。
SPI读写数据:
- /* Write to TX register */
- SPI_WRITE_TX(SPI0, 0x50650F);
- /* Check SPI0 busy status */
- while(SPI_IS_BUSY(SPI0));
- i32Err = SPI_READ_RX(SPI0);
SPI_WRITE_TX()进行SPI数据的发送,在等待发送完成后用SPI_READ_RX()进行读数据便可完成一次完整的SPI数据读写。
PDMA设置
- void SPI_DMAInit(void)
- {
- /* Reset PDMA module */
- SYS_ResetModule(PDMA_RST);
- /* Enable PDMA channels */
- PDMA_Open(PDMA, SPI_OPENED_CH);
- /*=======================================================================
- SPI master PDMA TX channel configuration:
- -----------------------------------------------------------------------
- Word length = 32 bits
- Transfer Count = DATA_COUNT
- Source = g_au32MasterToSlaveTestPattern
- Source Address = Incresing
- Destination = SPI0->TX
- Destination Address = Fixed
- Burst Type = Single Transfer
- =========================================================================*/
- /* Set transfer width (32 bits) and transfer count */
- PDMA_SetTransferCnt(PDMA, SPI_MASTER_TX_DMA_CH, PDMA_WIDTH_8, DATA_COUNT);
- /* Set source/destination address and attributes */
- PDMA_SetTransferAddr(PDMA, SPI_MASTER_TX_DMA_CH, (uint32_t)SPI_TX, PDMA_SAR_INC, (uint32_t)&SPI0->TX, PDMA_DAR_FIX);
- /* Set request source; set basic mode. */
- PDMA_SetTransferMode(PDMA, SPI_MASTER_TX_DMA_CH, PDMA_SPI0_TX, FALSE, 0);
- /* Single request type. SPI only support PDMA single request type. */
- PDMA_SetBurstType(PDMA, SPI_MASTER_TX_DMA_CH, PDMA_REQ_SINGLE, 0);
- /* Disable table interrupt */
- PDMA->DSCT[SPI_MASTER_TX_DMA_CH].CTL |= PDMA_DSCT_CTL_TBINTDIS_Msk;
- /*=======================================================================
- SPI master PDMA RX channel configuration:
- -----------------------------------------------------------------------
- Word length = 32 bits
- Transfer Count = DATA_COUNT
- Source = SPI0->RX
- Source Address = Fixed
- Destination = g_au32MasterRxBuffer
- Destination Address = Increasing
- Burst Type = Single Transfer
- =========================================================================*/
- /* Set transfer width (32 bits) and transfer count */
- PDMA_SetTransferCnt(PDMA, SPI_MASTER_RX_DMA_CH, PDMA_WIDTH_8, DATA_COUNT);
- /* Set source/destination address and attributes */
- PDMA_SetTransferAddr(PDMA, SPI_MASTER_RX_DMA_CH, (uint32_t)&SPI0->RX, PDMA_SAR_FIX, (uint32_t)SPI_RX, PDMA_DAR_INC);
- /* Set request source; set basic mode. */
- PDMA_SetTransferMode(PDMA, SPI_MASTER_RX_DMA_CH, PDMA_SPI0_RX, FALSE, 0);
- /* Single request type. SPI only support PDMA single request type. */
- PDMA_SetBurstType(PDMA, SPI_MASTER_RX_DMA_CH, PDMA_REQ_SINGLE, 0);
- /* Disable table interrupt */
- PDMA->DSCT[SPI_MASTER_RX_DMA_CH].CTL |= PDMA_DSCT_CTL_TBINTDIS_Msk;
- /* Enable SPI master DMA function */
- SPI_TRIGGER_TX_PDMA(SPI0);
- SPI_TRIGGER_RX_PDMA(SPI0);
- }
这里PDMA设置和UART差不多,不罗里吧嗦的胡扯了,主要就是一个SPI的使能,这里用SPI_TRIGGER_TX_PDMA()和SPI_TRIGGER_RX_PDMA()。这个是UART的SPI没有的。
在这个初始化完成后会进行一次发送和接收,如果不需要请删除 SPI_TRIGGER_TX_PDMA(SPI0); SPI_TRIGGER_RX_PDMA(SPI0);
如果想要重复PDMA 的发送可做如下处理:
- /* Re-trigger */
- /* Master PDMA TX channel configuration */
- /* Set transfer width (32 bits) and transfer count */
- PDMA_SetTransferCnt(PDMA, SPI_MASTER_TX_DMA_CH, PDMA_WIDTH_32, DATA_COUNT);
- /* Set request source; set basic mode. */
- PDMA_SetTransferMode(PDMA, SPI_MASTER_TX_DMA_CH, PDMA_SPI0_TX, FALSE, 0);
- /* Master PDMA RX channel configuration */
- /* Set transfer width (32 bits) and transfer count */
- PDMA_SetTransferCnt(PDMA, SPI_MASTER_RX_DMA_CH, PDMA_WIDTH_32, DATA_COUNT);
- /* Set request source; set basic mode. */
- PDMA_SetTransferMode(PDMA, SPI_MASTER_RX_DMA_CH, PDMA_SPI0_RX, FALSE, 0);
- /* Enable master's DMA transfer function */
- SPI_TRIGGER_TX_PDMA(SPI0);
- SPI_TRIGGER_RX_PDMA(SPI0);
可以封装为一个函数,每次需要启动DMA就调用一次该函数。但是这里又是老问题,启动DMA的速度慢,示波器测试需要8个微秒左右吧,时间过长。同样我们可以混搭寄存器操作,加快代码执行效率,这个技巧非常实用;
- void SPI_DMA_WRITE(void)
- {
- PB1 = 0;
- /* Master PDMA TX channel configuration */
- PDMA->CHCTL |= (1 << SPI_MASTER_TX_DMA_CH); /* Enable PDMA channel */
- PDMA->DSCT[SPI_MASTER_TX_DMA_CH].CTL =
- (PDMA->DSCT[SPI_MASTER_TX_DMA_CH].CTL & (~(PDMA_DSCT_CTL_TXCNT_Msk | PDMA_DSCT_CTL_OPMODE_Msk))) |
- (DATA_COUNT - 1) << PDMA_DSCT_CTL_TXCNT_Pos | /* Transfer count */
- 1 << PDMA_DSCT_CTL_OPMODE_Pos;
- /* Master PDMA RX channel configuration */
- PDMA->CHCTL |= (1 << SPI_MASTER_RX_DMA_CH); /* Enable PDMA channel */
- PDMA->DSCT[SPI_MASTER_RX_DMA_CH].CTL =
- (PDMA->DSCT[SPI_MASTER_RX_DMA_CH].CTL & (~(PDMA_DSCT_CTL_TXCNT_Msk | PDMA_DSCT_CTL_OPMODE_Msk))) |
- (DATA_COUNT - 1) << PDMA_DSCT_CTL_TXCNT_Pos | /* Transfer count */
- 1 << PDMA_DSCT_CTL_OPMODE_Pos;
- /* Enable master's DMA transfer function */
- SPI0->PDMACTL = (SPI_PDMACTL_RXPDMAEN_Msk | SPI_PDMACTL_TXPDMAEN_Msk);
- while (PDMA->DSCT[SPI_MASTER_TX_DMA_CH].CTL & PDMA_DSCT_CTL_TXCNT_Msk) ;
- PB1 = 1;
- }
这样,M031的DMA就完成了,如果需要启动DMA传输,就调用SPI_DMA_WRITE()函数便可。