16位的AD可以說是國產(chǎn)MCU的痛點(diǎn),至少在廉價(jià)的單片機(jī)里面,這個真的找不到飛思卡爾的替代品。之前未使用16位AD的時(shí)候,使用的是STM32F0的單片機(jī),因?yàn)?a target="_blank">產(chǎn)品需要,一直是將48M的主頻超頻到56M跑超速,后來因?yàn)橐咔榈仍颍琒T的價(jià)格飛上天,交期還特長,無奈之下?lián)Q了國產(chǎn)兆易創(chuàng)新的GD32,不得不說,對標(biāo)的GDE23主頻直接到了72M,M0+,不用超頻,正常跑高速就行。價(jià)格還便宜,不收過路費(fèi)。在這一點(diǎn)上,國產(chǎn)的MCU真的很強(qiáng)。
現(xiàn)在項(xiàng)目需要16位的AD,一時(shí)間找不到任何國產(chǎn)的替代品,當(dāng)然我們也把主意打到了ST的頭上,但是捋到STM32H7才找到16位AD,2020年的ST的價(jià)格大家都清楚,如果選用這款芯片,我們的產(chǎn)品成本將大大增加,這已經(jīng)超出了我們的預(yù)算。在之后的一番尋找中,確定了這個被恩智浦收購了多年的飛思卡爾的芯片。 MKV30,價(jià)格便宜,針對電機(jī)行業(yè)出生的MCU,在ADC的處理上可謂是下足了功夫。
自帶差分輸入模塊,支持高達(dá)16位的差分AD輸入,自帶硬件平均,可對輸入的AD信號進(jìn)行自動平均,支持低功耗和高速AD模式,可自動校準(zhǔn)AD,自帶比較器。 但是,因?yàn)楹茉缇捅皇召彛燥w思卡爾的資料并不如NXP自家的產(chǎn)品那樣詳細(xì)豐富,導(dǎo)致開發(fā)難度很大,而且這款芯片不像K60那款,因?yàn)樵缙谟?a href="http://wenjunhu.com/v/" target="_blank">智能車競賽的緣故,網(wǎng)友分享的資料和經(jīng)驗(yàn)很多。這款我拿到手里就很懵。本人并不是大佬,對新的單片機(jī)上手不是很容易。在開發(fā)的第一周就點(diǎn)了個燈,到處是坑。 下面分享我的開發(fā)過程和經(jīng)驗(yàn): 官網(wǎng)下載SDK直接pass,在有個基礎(chǔ)工程的基礎(chǔ)上使用MCUXpresso Config Tool配置ADC的引腳和功能初始化。 配置引腳:
因?yàn)槲倚枰褂脙陕稟DC的差分模式,這里配置ADC0和ADC1的引腳。使用PORTE16、PORTE17 、PORTE18 、PORTE19四個引腳。對應(yīng)ADC的ADC0_DP1,ADC0_DM1,ADC1_DP1,ADC1_DM1。軟件會自動配置引腳相關(guān)配置代碼。 ADC配置:
配置為16位的差分AD,因?yàn)槲易非笞罡咚俚腁DC采集,所以時(shí)鐘1分頻,硬件的8次平均。 ADC1配置相同。 開始進(jìn)入代碼:
/******************************************************************************** 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ù)的轉(zhuǎn)換};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ù)的轉(zhuǎn)換};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轉(zhuǎn)換器 */
ADC16_Init(ADC1_PERIPHERAL, &ADC1_config); /* 不使用軟件觸發(fā)器 */
ADC16_EnableHardwareTrigger(ADC1_PERIPHERAL, false); /* 配置硬件平均模式 */ ADC16_SetHardwareAverage(ADC1_PERIPHERAL, ADC1_hardwareAverageMode); /* 配置信道多路復(fù)用模式 */ ADC16_SetChannelMuxMode(ADC1_PERIPHERAL, ADC1_muxMode); /* 初始化通道 */
ADC16_SetChannelConfig(ADC1_PERIPHERAL, 1U, &ADC1_channelsConfig[0]); /* 自動校準(zhǔn) */
ADC16_DoAutoCalibration(ADC1_PERIPHERAL); /* Enable DMA. */ ADC16_EnableDMA(ADC1_PERIPHERAL, false); } 這里以ADC0為例,傳輸通道設(shè)置為1,配置為差分模式,不使能傳輸完成中斷。
ADC0_config結(jié)構(gòu)體中的配置主要是配置時(shí)鐘和采樣速度,我的配置是我能達(dá)到的最高速度。在ADC0_init函數(shù)中,配置為軟件觸發(fā),如果使用PDB,需要改為硬件觸發(fā),關(guān)閉了硬件平均。 當(dāng)我們需要獲取ADC的數(shù)據(jù)時(shí),需要以下代碼。
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);
可以將上述代碼添加進(jìn)主循環(huán),在需要AD值時(shí)便可以直接讀取ADC_Value0和ADC_Value1的值便可,可以包裝成一個函數(shù),需要時(shí)調(diào)用即可,執(zhí)行一次該代碼大約需要3us。如果AD的通道很多,可以使用for循環(huán),改善代碼。但是此方法占用MCU的內(nèi)存,下一篇更新靈活多通道的DMA采集。 要點(diǎn): 這里配置為ADC16位模式,但是并不是真正意義上的16位,在數(shù)據(jù)寄存器中有介紹,數(shù)據(jù)寄存器是16位,只有低15位是有效數(shù)據(jù)位,最高位為16位,所以ADC的范圍是0~32767,加上最高位的符號位能達(dá)到-32767~+32767.
我在這里沒看手冊,采集到的數(shù)據(jù)一直無法理解。
輸入通道輸入的是正弦波,結(jié)果串口打印出來的確是這個玩意,最后處理一下符號位解決。 上電之后會開始ADC采集,ADC采集完成觸發(fā)dma通道1開始傳輸?shù)街付ň彺?,dma通道1傳輸完成觸發(fā)鏈接,鏈接dma通道2,dma通道2將adc配置傳給adc配置寄存器。這樣可以靈活采集各種通道,并且對資源占用較小。只要設(shè)置好配置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默認(rèn)配置結(jié)構(gòu) */
EDMA_GetDefaultConfig(&userConfig); EDMA_Init(DMA0, &userConfig); EDMA_CreateHandle(&g_EDMA_Handle, DMA0, 1); /* 設(shè)置回調(diào) */ EDMA_SetCallback(&g_EDMA_Handle, Edma_Callback, NULL); /*eDMA傳輸結(jié)構(gòu)配置 。設(shè)置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); /* 設(shè)置回調(diào) */ 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); /* 當(dāng)傳輸完成時(shí)啟用中斷。 */ 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ù)。 因?yàn)橥ǖ?是通過通道一鏈接觸發(fā)的,所以在通道1的回調(diào)函數(shù)里面就不用再調(diào)用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ù)組中,需要時(shí)可以直接取值。我在使用ADC的DMA連續(xù)采樣時(shí)遇到一個問題,因?yàn)檫B續(xù)采樣會觸發(fā)callback函數(shù),此過程會觸發(fā)edma中斷,容易打斷原來代碼的進(jìn)程,如在高速應(yīng)用中使用需注意。
?
芯片的入門環(huán)境搭建
該芯片的入門環(huán)境搭建,內(nèi)容主要是官網(wǎng)獲取ASDK,這個芯片我不知道是因?yàn)橛玫奶貏e少,還是沒公開開發(fā)經(jīng)驗(yàn),很難找到相關(guān)資料,只有在NXP社區(qū)能找到一點(diǎn)資料,代理商也是只負(fù)責(zé)銷售,技術(shù)問題一概不管。在一頓亂搞之后搭建完一些工具之后,發(fā)現(xiàn)官方的SDK在我的板子上根本跑不起來,但是還好NXP論壇還有一個管理員提供一些支持,讓我能一步步走下來。那就開始點(diǎn)一個燈吧。 無論是下載資料還是論壇討論都必須注冊NXP賬號,注冊這里不談,跳過。
在此鏈接下找到MCUXpresso Config Tools 軟件,然后下載。
下載完成自己安裝,此軟件可自行配置工程,相當(dāng)于ST的STM32CubeMX,可以方便配置時(shí)鐘外設(shè),我們只用專注于寫邏輯便好,因?yàn)槲沂亲约寒嫷陌遄?,搭建的工程無法使用,只用它配置外設(shè)。 相同的頁面繼續(xù)下載一個SDK,MCUXpresso Software Development Kit (SDK)
在搜索框輸入芯片名稱,會彈出相應(yīng)開發(fā)板或芯片,我是自己打的板子,選擇芯片
選擇SDK版本
點(diǎn)擊進(jìn)入SDK
直接下載就好啦,因?yàn)槲覜]有梯子,下載特別慢,用其他瀏覽器會下載失敗,推介使用谷歌瀏覽器。
之后下載KEIL的MDK包,這個自行去官網(wǎng)下載。
原文標(biāo)題:國產(chǎn)16位MCU的痛點(diǎn),可以用這款物美價(jià)廉產(chǎn)品(附完整開發(fā)過程)
文章出處:【微信公眾號:嵌入式ARM】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
責(zé)任編輯:haq
-
芯片
+關(guān)注
關(guān)注
456文章
50889瀏覽量
424235 -
單片機(jī)
+關(guān)注
關(guān)注
6037文章
44562瀏覽量
635914 -
mcu
+關(guān)注
關(guān)注
146文章
17171瀏覽量
351464
原文標(biāo)題:國產(chǎn)16位MCU的痛點(diǎn),可以用這款物美價(jià)廉產(chǎn)品(附完整開發(fā)過程)
文章出處:【微信號:gh_c472c2199c88,微信公眾號:嵌入式微處理器】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論