- 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));
- /* Enable UART0 peripheral clock */
- CLK_EnableModuleClock(UART0_MODULE);
- /* Enable UART1 peripheral clock */
- CLK_EnableModuleClock(UART1_MODULE);
- /* Enable PDMA module clock */
- CLK_EnableModuleClock(PDMA_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);
- /* Lock protected registers */
- SYS_LockReg();
- }
在初始化时钟之前需要确认自己的外部晶振的频率,然后在system_M031Series.h文件的第38行修改宏定义。
在初始化时钟时会将需要的外设时钟一起初始化,这里初始化了UART0和UART1的时钟以及PDMA的时钟。
初始化UART
由于时钟已经配置,在初始化UART的配置时会显得特别简单。如果你的UART没有特殊要求,两行代码即可完成UART的初始化。
- void UART0_Init()
- {
- /*---------------------------------------------------------------------------------------------------------*/
- /* Init UART */
- /*---------------------------------------------------------------------------------------------------------*/
- /* Reset UART0 */
- SYS_ResetModule(UART0_RST);
- /* Configure UART0 and set UART0 baud rate */
- UART_Open(UART0, 115200);
- }
- void UART1_Init()
- {
- /*---------------------------------------------------------------------------------------------------------*/
- /* Init UART */
- /*---------------------------------------------------------------------------------------------------------*/
- /* Reset UART1 */
- SYS_ResetModule(UART1_RST);
- /* Configure UART1 and set UART1 Baudrate */
- UART_Open(UART1, 2500000);
-
- /* Enable Interrupt and install the call back function */
- NVIC_EnableIRQ(UART13_IRQn);
- UART_EnableInt(UART1, UART_INTEN_RDAIEN_Msk);
-
- }
串口0用于printf的调试。串口1 是我需要与其他串口设备通信的接口。除了我在串口1设置了串口接收中断以外,初始化一个串口仅仅需要两个函数,非常方便,这里使用的串口默认配置:一个停止位,无校验位,8位数据,如果需要修改可自行进入函数修改。
在配置串口1的接收中断时遇到了问题:调用NVIC_EnableIRQ()函数初始化中断线时,参数我填的是UART1_IRQn,无报错,编译可通过,但是测试没现象,于是进入debug页面,发现中断函数并未被编译。
尝试了很多方法都不能进行编译,后来去看UART1_IRQn的定义,发现这个宏定义下面还有一个UART13_IRQn。于是明白过来了,
UART0和UART2共用一个中断函数UART02_IRQHandler(),
UART1和3共用中断函数UART13_IRQHandler()
这里区别于其他家的库,不能用UART1_IRQn,需要用UART13_IRQn。
PDMA配置
- void PDMA_UART_TxTest(void)
- {
- /* UART Tx PDMA channel configuration */
- /* Set transfer width (8 bits) and transfer count */
- PDMA_SetTransferCnt(PDMA, UART_TX_DMA_CH, PDMA_WIDTH_8, UART_TEST_LENGTH);
- /* Set source/destination address and attributes */
- PDMA_SetTransferAddr(PDMA, UART_TX_DMA_CH, (uint32_t)SrcArray, PDMA_SAR_INC, (uint32_t)&UART1->DAT, PDMA_DAR_FIX);
- /* Set request source; set basic mode. */
- PDMA_SetTransferMode(PDMA, UART_TX_DMA_CH, PDMA_UART1_TX, FALSE, 0);
- /* Single request type */
- PDMA_SetBurstType(PDMA, UART_TX_DMA_CH, PDMA_REQ_SINGLE, 0);
- /* Disable table interrupt */
- PDMA_DisableInt(PDMA,UART_TX_DMA_CH, PDMA_INT_TEMPTY );
- }
这里有几个需要自行修改的地方,PDMA_SetTransferCnt(PDMA, UART_TX_DMA_CH, PDMA_WIDTH_8, UART_TEST_LENGTH);修改PDMA_WIDTH_8为修改数据宽度,这里默认8位,UART_TEST_LENGTH为发送长度,我这里设置为11个。PDMA_SetTransferAddr(PDMA, UART_TX_DMA_CH, (uint32_t)SrcArray, PDMA_SAR_INC, (uint32_t)&UART1->DAT, PDMA_DAR_FIX); SrcArray为数组的地址。因为我是发送的DMA,这里配置为内存到外设,如果是接收DMA则做以下设置: PDMA_SetTransferAddr(PDMA, UART_RX_DMA_CH, (uint32_t)&UART1->DAT, PDMA_SAR_FIX, (uint32_t)DestArray, PDMA_DAR_INC);
配置完后再主函数打开DMA:
- SYS_ResetModule(PDMA_RST);
- PDMA_Open(PDMA, (1 << UART_TX_DMA_CH));
中断函数配置及启动DMA
- void UART13_IRQHandler(void)
- {
- uint8_t res;
- // uint32_t u32IntSts = UART1->ISR;
- PB1 = 0;
- res = UART_READ(UART1);//读UART_DAT寄存器自动清除中断标志
- if(res == 0x1A)
- {
- UART_DISABLE_INT(UART1, UART_INTEN_TXPDMAEN_Msk);
- PDMA_UART_TxTest();
- UART_ENABLE_INT(UART1, UART_INTEN_TXPDMAEN_Msk);
- while (PDMA->DSCT[UART_TX_DMA_CH].CTL & PDMA_DSCT_CTL_TXCNT_Msk) ;
-
- }
- PB1 = 1;
- }
在M031中,区别于我之前用过的其他MCU,在进入中断函数之后,只要读取串口接收寄存器UART_DAT中的值,便可自动清除中断标志,并不需要去操作其他寄存器。非常好用。
DMA的启动和其他的MCU类似,需重新配置传输个数,RAM地址等,再调用一次初始化函数就行。然后利用while (PDMA->DSCT[UART_TX_DMA_CH].CTL & PDMA_DSCT_CTL_TXCNT_Msk) ;判断数据是否发送完成,实际上就是等待传输个数计数器为0。
这里插一句PB1的作用,PBI就是gpio PB1口。初始化就一句话GPIO_SetMode(PB, BIT1, GPIO_MODE_OUTPUT);这里方便示波器观察时间。在使用库函数初始化PWM时,因为每一次的启动都要调用该函数,库函数的操作很费时间,在触发串口接收中断后将PB1拉低,发送完拉高,在示波器观察到从触发接收中断到第一个串口数据发送出去,也就是DMA启动完成,大约耗时8us,效率低下。于是我将DMA初始化改用寄存器的方式,时间缩小到2.8us,好用!
- void PDMA_UART_TxTest(void)
- {
- /* UART Tx PDMA channel configuration */
- PDMA->DSCT[UART_TX_DMA_CH].CTL =
- (UART_TEST_LENGTH - 1) << PDMA_DSCT_CTL_TXCNT_Pos | /* Transfer count */
- PDMA_WIDTH_8 | /* Transfer width 8 bits */
- PDMA_DAR_FIX | /* Fixed destination address */
- PDMA_SAR_INC | /* Increment source address */
- PDMA_DSCT_CTL_TBINTDIS_Msk | /* Table interrupt disabled */
- PDMA_REQ_SINGLE | /* Single request type */
- PDMA_OP_BASIC; /* Basic mode */
- PDMA->DSCT[UART_TX_DMA_CH].SA = (uint32_t)SrcArray; /* Source address */
- PDMA->DSCT[UART_TX_DMA_CH].DA = (uint32_t)&UART1->DAT; /* Destination address */
- /* Request source selection */
- PDMA->REQSEL0_3 = (PDMA->REQSEL0_3 & (~PDMA_REQSEL0_3_REQSRC1_Msk)) | (PDMA_UART1_TX << PDMA_REQSEL0_3_REQSRC1_Pos);
- }