16位的AD可以說是國產(chǎn)MCU的痛點,至少在廉價的單片機里面,這個真的找不到飛思卡爾的替代品。之前未使用16位AD的時候,使用的是STM32F0的單片機,因為產(chǎn)品需要,一直是將48M的主頻超頻到56M跑超速,后來因為疫情等原因,ST的價格飛上天,交期還特長,無奈之下?lián)Q了國產(chǎn)兆易創(chuàng)新的GD32,不得不說,對標的GDE23主頻直接到了72M,M0+,不用超頻,正常跑高速就行。價格還便宜,不收過路費。在這一點上,國產(chǎn)的MCU真的很強。
現(xiàn)在項目需要16位的AD,一時間找不到任何國產(chǎn)的替代品,當然我們也把主意打到了ST的頭上,但是捋到STM32H7才找到16位AD,2020年的ST的價格大家都清楚,如果選用這款芯片,我們的產(chǎn)品成本將大大增加,這已經(jīng)超出了我們的預算。在之后的一番尋找中,確定了這個被恩智浦收購了多年的飛思卡爾的芯片。 MKV30,價格便宜,針對電機行業(yè)出生的MCU,在ADC的處理上可謂是下足了功夫。
自帶差分輸入模塊,支持高達16位的差分AD輸入,自帶硬件平均,可對輸入的AD信號進行自動平均,支持低功耗和高速AD模式,可自動校準AD,自帶比較器。 但是,因為很早就被收購,所以飛思卡爾的資料并不如NXP自家的產(chǎn)品那樣詳細豐富,導致開發(fā)難度很大,而且這款芯片不像K60那款,因為早期有智能車競賽的緣故,網(wǎng)友分享的資料和經(jīng)驗很多。這款我拿到手里就很懵。本人并不是大佬,對新的單片機上手不是很容易。在開發(fā)的第一周就點了個燈,到處是坑。 下面分享我的開發(fā)過程和經(jīng)驗: 官網(wǎng)下載SDK直接pass,在有個基礎工程的基礎上使用MCUXpresso Config Tool配置ADC的引腳和功能初始化。 配置引腳:
因為我需要使用兩路ADC的差分模式,這里配置ADC0和ADC1的引腳。使用PORTE16、PORTE17 、PORTE18 、PORTE19四個引腳。對應ADC的ADC0_DP1,ADC0_DM1,ADC1_DP1,ADC1_DM1。軟件會自動配置引腳相關配置代碼。 ADC配置:
配置為16位的差分AD,因為我追求最高速的ADC采集,所以時鐘1分頻,硬件的8次平均。 ADC1配置相同。 開始進入代碼:
/******************************************************************************** Definitions******************************************************************************/#define
DEMO_ADC16_CHANNEL 1U#define DEMO_ADC16_CHANNEL_GROUP 0U#define
DEMO_ADC16_BASEADDR ADC0#define DEMO_DMAMUX_BASEADDR DMAMUX0#define
DEMO_DMA_CHANNEL 1U#define DEMO_DMA_ADC0_SOURCE 40U#define
DEMO_DMA_ADC1_SOURCE 41U#define DEMO_DMA_BASEADDR DMA0#define
ADC16_RESULT_REG_ADDR 0x4003b010U#define ADC16_RESULT_REG_ADDR1 0x40027010U//查詢寄存器手冊得到#define DEMO_DMA_IRQ_ID DMA0_IRQn #define DEMO_ADC16_SAMPLE_COUNT 8U /* The ADC16 sample count. *//************************************************************************************************************************
ADC0 initialization code**********************************************************************************************************************/adc16_channel_config_t
ADC0_channelsConfig[1] = { { .channelNumber = 1U, //傳輸通道 .enableDifferentialConversion = true, //差分模式 .enableInterruptOnConversionCompleted = false, //使能傳輸完成中斷 }};const adc16_config_t ADC0_config = { .referenceVoltageSource = kADC16_ReferenceVoltageSourceVref, .clockSource = 0, .enableAsynchronousClock = false, .clockDivider = kADC16_ClockDivider1, .resolution = kADC16_ResolutionSE16Bit, .longSampleMode = kADC16_LongSampleDisabled, .enableHighSpeed = true, .enableLowPower = false, .enableContinuousConversion = false//連續(xù)的轉換};const
adc16_channel_mux_mode_t ADC0_muxMode = kADC16_ChannelMuxA;/* 硬件平均 8 */const
adc16_hardware_average_mode_t ADC0_hardwareAverageMode = kADC16_HardwareAverageDisabled;void ADC0_init(void) { /* Initialize ADC16 converter */ ADC16_Init(ADC0_PERIPHERAL, &ADC0_config); /* Make sure, that software trigger is used */
ADC16_EnableHardwareTrigger(ADC0_PERIPHERAL, false); /* Configure hardware average mode */ ADC16_SetHardwareAverage(ADC0_PERIPHERAL, ADC0_hardwareAverageMode); /* Configure channel multiplexing mode */
ADC16_SetChannelMuxMode(ADC0_PERIPHERAL, ADC0_muxMode); /* Initialize channel */
ADC16_SetChannelConfig(ADC0_PERIPHERAL, 0U, &ADC0_channelsConfig[0]); /* Perform auto calibration */ ADC16_DoAutoCalibration(ADC0_PERIPHERAL); /* Enable DMA. */ ADC16_EnableDMA
(ADC0_PERIPHERAL, false);}/************************************************************************************************************************ ADC1 initialization code**********************************************************************************************************************/adc16_channel_config_t ADC1_channelsConfig[1] = { { .channelNumber = 2U, .enableDifferentialConversion = true, //差分模式
.enableInterruptOnConversionCompleted = false, }};const adc16_config_t ADC1_config = { .referenceVoltageSource = kADC16_ReferenceVoltageSourceVref, .clockSource = 0, .enableAsynchronousClock = false, .clockDivider = kADC16_ClockDivider1, .resolution = kADC16_ResolutionSE16Bit, .longSampleMode = kADC16_LongSampleDisabled, .enableHighSpeed = true, .enableLowPower = false, .enableContinuousConversion = false//連續(xù)的轉換};const
adc16_channel_mux_mode_t ADC1_muxMode = kADC16_ChannelMuxA;const
adc16_hardware_average_mode_t ADC1_hardwareAverageMode = kADC16_HardwareAverageDisabled;void ADC1_init(void) {// EnableIRQ(ADC0_IRQn); /* 初始化ADC16轉換器 */
ADC16_Init(ADC1_PERIPHERAL, &ADC1_config); /* 不使用軟件觸發(fā)器 */
ADC16_EnableHardwareTrigger(ADC1_PERIPHERAL, false); /* 配置硬件平均模式 */ ADC16_SetHardwareAverage(ADC1_PERIPHERAL, ADC1_hardwareAverageMode); /* 配置信道多路復用模式 */ ADC16_SetChannelMuxMode(ADC1_PERIPHERAL, ADC1_muxMode); /* 初始化通道 */
ADC16_SetChannelConfig(ADC1_PERIPHERAL, 1U, &ADC1_channelsConfig[0]); /* 自動校準 */
ADC16_DoAutoCalibration(ADC1_PERIPHERAL); /* Enable DMA. */ ADC16_EnableDMA(ADC1_PERIPHERAL, false); } 這里以ADC0為例,傳輸通道設置為1,配置為差分模式,不使能傳輸完成中斷。
ADC0_config結構體中的配置主要是配置時鐘和采樣速度,我的配置是我能達到的最高速度。在ADC0_init函數(shù)中,配置為軟件觸發(fā),如果使用PDB,需要改為硬件觸發(fā),關閉了硬件平均。 當我們需要獲取ADC的數(shù)據(jù)時,需要以下代碼。
adc16_channel_config_t adc16ChannelConfigStruct; adc16ChannelConfigStruct.channelNumber = 1; //ADC通道 adc16ChannelConfigStruct.channelNumber = 2;
adc16ChannelConfigStruct.enableDifferentialConversion = true;//使能差分
adc16ChannelConfigStruct.enableInterruptOnConversionCompleted = false;//失能中斷
ADC16_SetChannelConfig(ADC1, 0U, &adc16ChannelConfigStruct); ADC16_SetChannelConfig(ADC0, 0U, &adc16ChannelConfigStruct); while (0U == (kADC16_ChannelConversionDoneFlag &
ADC16_GetChannelStatusFlags(ADC1, 0U))); ADC_Value0 = ADC16_GetChannelConversionValue(ADC0, 0U); ADC_Value1 = ADC16_GetChannelConversionValue(ADC1, 0U);
可以將上述代碼添加進主循環(huán),在需要AD值時便可以直接讀取ADC_Value0和ADC_Value1的值便可,可以包裝成一個函數(shù),需要時調用即可,執(zhí)行一次該代碼大約需要3us。如果AD的通道很多,可以使用for循環(huán),改善代碼。但是此方法占用MCU的內(nèi)存,下一篇更新靈活多通道的DMA采集。 要點: 這里配置為ADC16位模式,但是并不是真正意義上的16位,在數(shù)據(jù)寄存器中有介紹,數(shù)據(jù)寄存器是16位,只有低15位是有效數(shù)據(jù)位,最高位為16位,所以ADC的范圍是0~32767,加上最高位的符號位能達到-32767~+32767.
我在這里沒看手冊,采集到的數(shù)據(jù)一直無法理解。
輸入通道輸入的是正弦波,結果串口打印出來的確是這個玩意,最后處理一下符號位解決。 上電之后會開始ADC采集,ADC采集完成觸發(fā)dma通道1開始傳輸?shù)街付ň彺妫琩ma通道1傳輸完成觸發(fā)鏈接,鏈接dma通道2,dma通道2將adc配置傳給adc配置寄存器。這樣可以靈活采集各種通道,并且對資源占用較小。只要設置好配置adc的數(shù)組,剩下的dma就會處理.DMA配置:
void EDMA_Configuration(void){ edma_config_t userConfig; /* 配置 DMAMUX */ DMAMUX_Init(DMAMUX); /* 通道CH1初始化 */ DMAMUX_SetSource(DMAMUX, 1, 40); /* Map ADC0 source to channel 1 */
DMAMUX_EnableChannel(DMAMUX, 1); /* 通道CH2初始化 */ DMAMUX_SetSource(DMAMUX, 2, 41);/* Map ADC1 source to channel 2 */ DMAMUX_EnableChannel(DMAMUX, 2); /* 獲取eDMA默認配置結構 */
EDMA_GetDefaultConfig(&userConfig); EDMA_Init(DMA0, &userConfig); EDMA_CreateHandle(&g_EDMA_Handle, DMA0, 1); /* 設置回調 */ EDMA_SetCallback(&g_EDMA_Handle, Edma_Callback, NULL); /*eDMA傳輸結構配置 。設置dma通道1的adc值傳到g_adc16SampleDataArray*/ EDMA_PrepareTransfer(&transferConfig, (void *)ADC16_RESULT_REG_ADDR, sizeof(uint32_t), (void *)g_adc16SampleDataArray, sizeof(uint32_t), sizeof(uint32_t), sizeof(g_adc16SampleDataArray),
kEDMA_PeripheralToMemory); EDMA_SubmitTransfer(&g_EDMA_Handle, &transferConfig); /* Enable interrupt when transfer is done. */ EDMA_EnableChannelInterrupts(DEMO_DMA_BASEADDR, DEMO_DMA_CHANNEL, kEDMA_MajorInterruptEnable);#if defined(FSL_FEATURE_EDMA_ASYNCHRO_REQUEST_CHANNEL_COUNT) &&
FSL_FEATURE_EDMA_ASYNCHRO_REQUEST_CHANNEL_COUNT /* Enable async DMA request. */
EDMA_EnableAsyncRequest(DEMO_DMA_BASEADDR, DEMO_DMA_CHANNEL, true);#endif /*
FSL_FEATURE_EDMA_ASYNCHRO_REQUEST_CHANNEL_COUNT */ /* Enable transfer. */
EDMA_StartTransfer(&g_EDMA_Handle); //將dma通道1鏈接到通道0 EDMA_SetChannelLink(DMA0, 1,
kEDMA_MinorLink, 2); EDMA_SetChannelLink(DMA0, 1,
kEDMA_MajorLink,2); //*********************************************************************************************/
EDMA_CreateHandle(&DMA_CH2_Handle, DMA0, 2); EDMA_SetCallback(&DMA_CH2_Handle, Edma_Callback1, NULL); /* 設置回調 */ EDMA_PrepareTransfer(&g_transferConfig, (void *)ADC16_RESULT_REG_ADDR1, sizeof(uint32_t), (void *)g_adc16SampleDataArray1, sizeof(uint32_t), sizeof(uint32_t), sizeof(g_adc16SampleDataArray1), kEDMA_PeripheralToMemory); EDMA_SubmitTransfer(&DMA_CH2_Handle, &g_transferConfig); //傳輸完后修正通道 DMA0-》TCD[1].DLAST_SGA = -1* sizeof(g_adc16SampleDataArray); DMA0-》TCD[2].DLAST_SGA = -1* sizeof(g_adc16SampleDataArray1); /* 當傳輸完成時啟用中斷。 */ EDMA_EnableChannelInterrupts(DEMO_DMA_BASEADDR, 2, kEDMA_MajorInterruptEnable);#if defined(FSL_FEATURE_EDMA_ASYNCHRO_REQUEST_CHANNEL_COUNT) &&
FSL_FEATURE_EDMA_ASYNCHRO_REQUEST_CHANNEL_COUNT// /* 啟用異步DMA請求 */
EDMA_EnableAsyncRequest(DEMO_DMA_BASEADDR, 2, true);#endif /* FSL_FEATURE_EDMA_ASYNCHRO_REQUEST_CHANNEL_COUNT */ /* 使能數(shù)據(jù)傳輸 */ EDMA_StartTransfer(&DMA_CH2_Handle); }
DAM 通道1和通道2的callback函數(shù)。 因為通道2是通過通道一鏈接觸發(fā)的,所以在通道1的回調函數(shù)里面就不用再調用EDMA_StartTransfer()函數(shù)了。 此處注意將ADC的采樣模式改為連續(xù)模式。
static void Edma_Callback(edma_handle_t *handle, void *userData, bool transferDone, uint32_t tcds){ EDMA_StartTransfer(&g_EDMA_Handle); g_Transfer_Done = false; if (transferDone) { g_Transfer_Done = true; }} static void Edma_Callback1(edma_handle_t *handle, void *userData, bool transferDone, uint32_t tcds){ g_Transfer_Done1 = false; if (transferDone) { g_Transfer_Done1 = true; }}
至此ADC的DMA就完成了,ADC會一直采集并通過DMA傳輸?shù)絞_adc16SampleDataArray[]和g_adc16SampleDataArray1[]兩個數(shù)組中,需要時可以直接取值。我在使用ADC的DMA連續(xù)采樣時遇到一個問題,因為連續(xù)采樣會觸發(fā)callback函數(shù),此過程會觸發(fā)edma中斷,容易打斷原來代碼的進程,如在高速應用中使用需注意。
?
芯片的入門環(huán)境搭建
該芯片的入門環(huán)境搭建,內(nèi)容主要是官網(wǎng)獲取ASDK,這個芯片我不知道是因為用的特別少,還是沒公開開發(fā)經(jīng)驗,很難找到相關資料,只有在NXP社區(qū)能找到一點資料,代理商也是只負責銷售,技術問題一概不管。在一頓亂搞之后搭建完一些工具之后,發(fā)現(xiàn)官方的SDK在我的板子上根本跑不起來,但是還好NXP論壇還有一個管理員提供一些支持,讓我能一步步走下來。那就開始點一個燈吧。 無論是下載資料還是論壇討論都必須注冊NXP賬號,注冊這里不談,跳過。
在此鏈接下找到MCUXpresso Config Tools 軟件,然后下載。
下載完成自己安裝,此軟件可自行配置工程,相當于ST的STM32CubeMX,可以方便配置時鐘外設,我們只用專注于寫邏輯便好,因為我是自己畫的板子,搭建的工程無法使用,只用它配置外設。 相同的頁面繼續(xù)下載一個SDK,MCUXpresso Software Development Kit (SDK)
在搜索框輸入芯片名稱,會彈出相應開發(fā)板或芯片,我是自己打的板子,選擇芯片
選擇SDK版本
點擊進入SDK
直接下載就好啦,因為我沒有梯子,下載特別慢,用其他瀏覽器會下載失敗,推介使用谷歌瀏覽器。
之后下載KEIL的MDK包,這個自行去官網(wǎng)下載。
原文標題:國產(chǎn)16位MCU的痛點,可以用這款物美價廉產(chǎn)品(附完整開發(fā)過程)
文章出處:【微信公眾號:嵌入式ARM】歡迎添加關注!文章轉載請注明出處。
責任編輯:haq
-
芯片
+關注
關注
456文章
51192瀏覽量
427314 -
單片機
+關注
關注
6043文章
44621瀏覽量
638615 -
mcu
+關注
關注
146文章
17324瀏覽量
352658
原文標題:國產(chǎn)16位MCU的痛點,可以用這款物美價廉產(chǎn)品(附完整開發(fā)過程)
文章出處:【微信號:gh_c472c2199c88,微信公眾號:嵌入式微處理器】歡迎添加關注!文章轉載請注明出處。
發(fā)布評論請先 登錄
相關推薦
評論