環境搭建這里就跳過了,沒啥用,我還是用KEIL 5 開發,自行下載個PACK包安裝就好了。點此前往新塘官網。搜索自己的MCU型號,打開頁面,在資源中有文檔和軟件。
在文檔中下載數據手冊等文檔,在軟件中下載例程和工具,軟件中最實用的是以下幾個軟件:
從上到下依次是:官方例程庫,Nu_link驅動,外設引腳配置軟件,時鐘配置軟件。
外設引腳配置軟件用于快速配置引腳以及復用,該軟件只能配置引腳及其功能,不能配置外設等功能呢,例如串口的相關配置,這些事實現不了的。
時鐘配置軟件僅用于配置系統時鐘以及各外設時鐘。這兩個軟件支持導出.c代碼??蓮椭普迟N到自己的工程。
這兩個軟件都是非常簡單的,這里就不贅述了。
但是有一個時鐘配置軟件有BUG,以我用的M031SE3AE為例,外部時鐘最大可使用32M,但是軟件中最大只支持24M,希望官方可以修復。
開始代碼
下載官方的例程,固件庫代碼在文件夾:D:M031_Series_BSP_CMSIS_V3.03.000SampleCodeStdDriver,寄存器代碼在D:M031_Series_BSP_CMSIS_V3.03.000SampleCodeRegBased
這里采用固件庫的方式開發,方便快捷。
時鐘初始化:
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類似,[size=14.6667px]需重新配置傳輸個數,[size=14.6667px]RAM[size=14.6667px]地址等,再調用一次初始化函數就行。然后利用[size=14.6667px]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);}
END
本文為21ic論壇藍V作者吶咯密密原創撰寫
編輯:jq
-
mcu
+關注
關注
146文章
16993瀏覽量
350318 -
函數
+關注
關注
3文章
4307瀏覽量
62432 -
代碼
+關注
關注
30文章
4748瀏覽量
68357 -
BUG
+關注
關注
0文章
155瀏覽量
15653 -
pack
+關注
關注
14文章
74瀏覽量
9612
原文標題:缺貨下,又換MCU了:新塘031 串口PDMA通信。
文章出處:【微信號:gh_c472c2199c88,微信公眾號:嵌入式微處理器】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論