普冉(PUYA)單片機開發筆記(7): ADC-輪詢式多路采樣

概述

應用中經常會有使用單片機進行模數轉換的需求。PY32F003 具有 1 個 12 位的模擬數字轉換器(ADC),今天我們一起來使用一下這個 ADC。

數據手冊中對 ADC 簡介如下。

SAR ADC:逐次逼近式 ADC,原理參見“參考鏈接:什么是SAR ADC? - 知乎”。12位采樣值的最大值4095。數據手冊上標明的最大可用通道數量是 8 個外部通道,但對照 PY32F003F18P 的管腳復用表,如果應用中還要使用 GPIO,LED,定時器 和 UART 的話,可使用的外部 ADC 通道數最多不超過 6 個。對比于 PY32F003F18P 的 20 腳封裝和低廉的芯片價格,這樣的 MCU 可以在應用中采樣 6 個外部模擬量通道也是相當可觀的數量了。

PY32F003 可以在不使用外部晶振的情況下完成數模轉換,但其采樣精度還需要驗證。今天先嘗試著把 ADC 的功能跑通先。

實現代碼

參考在 STM32F103 上實現 ADC 的思路,在 PY32F003 上完成一下看。大致的步驟如下:

  1. 為 ADC1 指定 GPIO 管腳,并設置其復用功能
  2. 對 ADC1 進行初始化
  3. 在主循環中進行采樣和打印輸出

在 main.h 中增加和 ADC 相關的函數聲明

/** ----------------------------------------------------------------------------
* @name   : void ADC_Init(void)
* @brief  : ADC 初始化
* @param  : [in] None
* @retval : [out] void
* @remark :
*** ----------------------------------------------------------------------------
*/
void ADC_Init(void);/** ----------------------------------------------------------------------------
* @name   : HAL_StatusTypeDef ADC_Sample(char * sampleResult)
* @brief  : 獲取 ADC 的采樣結果,結果存放在 sampleResult 字符串中
* @param  : [in] None
* @retval : [out] HAL_HandleTypeDef. 操作成功返回 HAL_OK, 錯誤返回錯誤碼。
* @remark : sampleResult 是格式化的字符串,需要解析
*** ----------------------------------------------------------------------------
*/
HAL_StatusTypeDef ADC_Sample(char * sampleResult);

在 app_adc.c 文件中實現函數功能

在 Application/User 組增加 app_adc.c 文件,完整代碼如下。

/********************************************************************************* @file    app_adc.c* @brief   Application level Analog-Digital Conveter codes.******************************************************************************* @attention** Copyright (c) 2023 CuteModem Intelligence.* All rights reserved.** This software is licensed under terms that can be found in the LICENSE file* in the root directory of this software component.* If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************/#include "main.h"ADC_HandleTypeDef hadc;
uint32_t adc_value[3];/********************************************************************************************************
* @name   : HAL_StatusTypeDef ADC_Sample(char * sampleResult)
* @brief  : 獲取 ADC 的采樣結果,結果存放在 sampleResult 字符串中
* @param  : [in] None
* @retval : [out] HAL_HandleTypeDef. 操作成功返回 HAL_OK, 錯誤返回錯誤碼。
* @remark : sampleResult 是格式化的字符串,需要解析
********************************************************************************************************/
HAL_StatusTypeDef ADC_Sample(char * sampleResult)
{uint8_t i=0;if(HAL_ADCEx_Calibration_Start(&hadc) != HAL_OK) return HAL_ERROR;HAL_ADC_Start(&hadc);                       //開始采樣for (i = 0; i < 3; i++){HAL_ADC_PollForConversion(&hadc, 10000);  //等待ADC轉換adc_value[i] = HAL_ADC_GetValue(&hadc);   //獲取AD值}#if(1)// excel formatsprintf(sampleResult, "%d,%d,%d",(uint16_t)adc_value[0],(uint16_t)adc_value[1],(uint16_t)adc_value[2]);
#else// JSON formatsprintf(sampleResult, "[{\"C\":0,\"D\":%d}"",{\"C\":1,\"D\":%d}"",{\"C\":5,\"D\":%d}""]",(uint16_t)adc_value[0],(uint16_t)adc_value[1],(uint16_t)adc_value[2]);
#endif    HAL_ADC_Stop(&hadc); // 停止采樣return HAL_OK;
}void ADC_Init(void)
{ADC_ChannelConfTypeDef sConfig = {0};__HAL_RCC_ADC_FORCE_RESET();__HAL_RCC_ADC_RELEASE_RESET();__HAL_RCC_ADC_CLK_ENABLE();hadc.Instance = ADC1;if (HAL_ADCEx_Calibration_Start(&hadc) != HAL_OK)                 //AD校準Error_Handler();/* Configure global features of the ADC1  */hadc.Init.ClockPrescaler        = ADC_CLOCK_SYNC_PCLK_DIV1;       //ADC_CLOCK_SYNC_PCLK_DIV2/4,分頻系數hadc.Init.Resolution            = ADC_RESOLUTION_12B;             //設置采樣位數hadc.Init.DataAlign             = ADC_DATAALIGN_RIGHT;            //右對齊hadc.Init.ScanConvMode          = ADC_SCAN_DIRECTION_FORWARD;     //掃描方向設置hadc.Init.EOCSelection          = ADC_EOC_SINGLE_CONV;            //ADC_EOC_SINGLE_CONV:單次采樣 ; ADC_EOC_SEQ_CONV:序列采樣hadc.Init.LowPowerAutoWait      = ENABLE;                         //ENABLE:讀取ADC值后,開始下一次轉換; DISABLE:直接轉換hadc.Init.ContinuousConvMode    = DISABLE;                        //ENABLE:連續模式, DISABLE:單次模式hadc.Init.DiscontinuousConvMode = DISABLE;                        //非連續轉換模式設置hadc.Init.ExternalTrigConv      = ADC_SOFTWARE_START;             //觸發模式設置hadc.Init.ExternalTrigConvEdge  = ADC_EXTERNALTRIGCONVEDGE_NONE;  //外部觸發沿設置hadc.Init.DMAContinuousRequests = DISABLE;                        //DMA連續模式設置hadc.Init.Overrun               = ADC_OVR_DATA_OVERWRITTEN;       //ADC_OVR_DATA_OVERWRITTEN:過載時覆蓋,ADC_OVR_DATA_PRESERVED:保留舊值if (HAL_ADC_Init(&hadc) != HAL_OK) Error_Handler();               //初始化ADC/* Configure selected ADC channels  */sConfig.Channel      = ADC_CHANNEL_0;                             sConfig.Rank         = ADC_RANK_CHANNEL_NUMBER;                   sConfig.SamplingTime = ADC_SAMPLETIME_71CYCLES_5;                 if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)             Error_Handler();sConfig.Channel = ADC_CHANNEL_1;                                  sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;                           sConfig.SamplingTime = ADC_SAMPLETIME_71CYCLES_5;                 if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)             Error_Handler();sConfig.Channel = ADC_CHANNEL_4;                                  sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;                           sConfig.SamplingTime = ADC_SAMPLETIME_71CYCLES_5;                 if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)             Error_Handler();
}

在 app_adc.c 中定義了業務所需的變量,功能函數也在一個 .c 文件中全部實現。這樣做是參考了面向對象的編程模式,遵循代碼/變量和功能解耦的原則,ADC 所需的全局變量都在 app_adc.c 中定義,main.c 中就不用再引用 ADC 相關的變量,也不用關心實現的細節了。唯一的接口就是 ADC_Sample() 函數的 sampleResult,sampleResult 定義為一個字符串具有很好的通用性,并隱藏了實現的細節。這里例子中被注釋掉的 JSON 串返回結果的代碼,在實際應用中,在上一層的業務邏輯處理是很方便的。當然 MCU 編程,一般不會采用 JSON 這種富文本的格式,這里只作為一種示例。

ADC_Sample() 函數中每次采樣之前都對 ADC 進行了校準,校準完成后開始采樣,采樣完畢后停止 ADC。

在 py32f0xx_hal_msp.c 文件中指定 GPIO 及其復用功能

/*** -----------------------------------------------------------------------* @name   : void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)* @brief  : 初始化 ADC 相關 MSP* @param  : [in] *hadc, ADC handler pointer* @retval : void* @remark :* -----------------------------------------------------------------------
*/
void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
{GPIO_InitTypeDef GPIO_InitStruct = {0};/*=============PA0/1/4初始化=============*/if (hadc->Instance == ADC1){__HAL_RCC_ADC_CLK_ENABLE();   /* Peripheral clock enable */__HAL_RCC_GPIOA_CLK_ENABLE(); /*ADC GPIO ConfigurationPA0     ------> ADC_IN0PA1     ------> ADC_IN1PA4     ------> ADC_IN5*/GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_4; // 指定 PA0/1/4GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;                    // 設置為模擬端口GPIO_InitStruct.Pull = GPIO_PULLDOWN;                       // 下拉:無輸入時采樣值接近零HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);                     // 執行初始化}
}

按照廠家例程的文件組織,所有的 HAL_xxx_MspInit() 集中在 py32_f0xx_hal_msp.c 文件中,由于在 ADC_Init() 函數中調用了 HAL_ADC_Init() 函數,要調用 HAL_ADC_MspInit(),這個函數在 HAL 庫中的原型是 weak 類型的,并且是一個空函數,因此需要在實用中重寫。

當然,把 HAL_ADC_MspInit() 函數在 app_adc.c 文件中實現也是可以的。

修改 DEBUG 口的管腳映射

PY32F003 ADC1 的通道 0/1/5 復用了 PA0/1/4,之前的實驗中,PA1/0 被用作了 DEBUG 口 UART2,和 ADC1 的通道是沖突的,所以需要把 DEBUG 口對應的管腳挪走。查了數據手冊,AF4 組的 PA2/3 可以用作 UART2,修改 UART_Config() 如下。

除了修改管腳映射以外,中斷優先級等的不做修改。

HAL_StatusTypeDef USART_Config(void)
{// Using PA2/PA3 (TX/RX)HAL_StatusTypeDef conf_res = HAL_OK;GPIO_InitTypeDef GPIO_InitStruct;gUartInited = 0;    //====================// USART2初始化//====================__HAL_RCC_USART2_CLK_ENABLE();__HAL_RCC_GPIOA_CLK_ENABLE();UartHandle.Instance = USART2;UartHandle.Init.BaudRate = 115200;UartHandle.Init.WordLength = UART_WORDLENGTH_8B;UartHandle.Init.StopBits = UART_STOPBITS_1;UartHandle.Init.Parity = UART_PARITY_NONE;UartHandle.Init.HwFlowCtl = UART_HWCONTROL_NONE;UartHandle.Init.Mode = UART_MODE_TX_RX;conf_res = HAL_UART_Init(&UartHandle);if(conf_res != HAL_OK) return conf_res;/**USART2 GPIO ConfigurationPA2     ------> USART2_TXPA3     ------> USART2_RX*/GPIO_InitStruct.Pin = GPIO_PIN_2 | GPIO_PIN_3;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_PULLUP;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF4_USART2;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);HAL_NVIC_SetPriority(USART2_IRQn, 0, 3);    // 使能NVICHAL_NVIC_EnableIRQ(USART2_IRQn);            // 使能USART2中斷gUartInited = 1;return conf_res;
}

在 main.c 的主循環中采樣

int main(void)
{HAL_Init();             // systick初始化SystemClock_Config();   // 配置系統時鐘GPIO_Config();if(USART_Config() != HAL_OK) Error_Handler();         printf("[SYS_INIT] Debug port initilaized.\r\n");ADC_Init();printf("[SYS_INIT] ADC initilaized.\r\n");printf("\r\n+---------------------------------------+""\r\n|        PY32F003 MCU is ready.         |""\r\n+---------------------------------------+""\r\n         10 digits sent to you!          ""\r\n+---------------------------------------+""\r\n");if (DBG_UART_Start() != HAL_OK) Error_Handler();char sres[64]={0};uint8_t sIndex = 0;while (1){ BSP_LED_Toggle(LED3);if(sIndex % 2 == 0){if(ADC_Sample(sres) == HAL_OK){printf("%s\r\n", sres);}else{printf("Sample error.\r\n");}}sIndex ++;}HAL_Delay(500);
}

代碼中,主循環每 0.5s 翻轉一次 LED,每 1s 采樣一次。

實驗結果

初次跑通

按照上述步驟編寫好代碼,編譯燒錄,在 XCOM 上得到的結果如圖。初次運行,PA0/1/4 出于懸空狀態,得到的采樣值是隨機的。

注意到在 HAL_ADC_MspInt() 函數中,將 PA0/1/4 這三個管腳的 PULL 屬性都設置成了 PULLDOWN,本想著即使懸空的話仍可得到接近 0 的采樣值。但實驗結果中,PA0 的懸空狀態采樣值仍在 1480 多的值,折合成電壓為

? 1480/4096*3.3 = 1.192V

這個值挺高的,而 PA1/4 管腳換算得到的電壓值分別為 0.661/0.524V,這兩個值也不低。這說明 PY32F003 的內部下拉應該是“弱下拉”——或許,在 HAL_ADC_Init() 函數中又對這幾個管腳做了什么配置?這個問題留著以后關注。

基于此,在實際項目中用到 PY32F003 進行 ADC 時,在信號管腳接入前,要使用一個(或一組)運放做一下電壓跟隨才好。

采樣時長

在 HAL_ADC_ConfigChannel() 中,設置了采樣周期均為 71.5,加上轉換的耗費 12.5 周期,合計84 個時鐘周期,計算得到采樣時間為 3.5us 一次,也挺快了了。

對 GND 和 VCC 的采樣值

將 PA0 接地,然后再觀察其采樣值,得到了全“0”的采樣結果。

將 PA0 接 3.3V 管腳,50次采樣得到的平均值是 4087.22,換算得到 3.293V,也還好。

PULLUP 還是 PULLDOWN,還是 NOPULL?

把 PA0/1/4 都設置為內部上拉/下拉/無上下拉狀態時,PA0 接地,測得 PA1/4 的采用值分別是:

PULLUP:2.159/2.191V,PULLDOWN:0.242/0.322V,NOPULL:1.990/3.061V

PA1和PA4的特性略有不同。

PA0 得到的采樣值均為0,這說明管腳的 PULL 被初始化的狀態不會對采樣的測量值產生影響。

在 PA0 接 VCC 時,不論其 PULL 屬性如何,對采樣值也沒有影響。

總結

  • 根據廠家例程移植,跑通 ADC 的輪詢式采樣是比較簡單的。如果熟悉對 STM32 的 ADC 配置,可以照搬 STM32 的步驟。
  • 分配 ADC1 的采樣通道時,要把開發板默認的 UART2 管腳和 ADC1 的采樣通道管腳錯開。
  • 當某一管腳配置為模擬信號時,其管腳的 PULL 屬性對測量結果無影響。
  • 實用中,ADC1 的采樣輸入管腳最好使用運放做一個電壓跟隨器。
  • 0~VCC 中間值的采樣精度如何,尚未驗證,留待后續實驗完成。

后續還會繼續嘗試使用 DMA 的 ADC,敬請期待。

謬誤之處,懇請指正。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/211908.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/211908.shtml
英文地址,請注明出處:http://en.pswp.cn/news/211908.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

1830_emacs lisp的交互式模式

org-mode的標記語法 Grey 全部學習匯總&#xff1a; GitHub - GreyZhang/g_org: my learning trip for org-mode 交互式模式 emacs的交互式模式讓我對emacs的生命力有了更進一步的認識&#xff0c;但是我并沒有找到什么特別豐富的資料做這方面的學習與分析。尤其是理論與實…

class070 子數組最大累加和問題與擴展-上【算法】

class070 子數組最大累加和問題與擴展-上【算法】 code1 53. 最大子數組和 // 累加和最大子數組和 // 給你一個整數數組 nums // 請你找出一個具有最大累加和的非空子數組 // 返回其最大累加和 // 測試鏈接 : https://leetcode.cn/problems/maximum-subarray/ dp[i]&#xff…

【Docker】Docker Compose,yml 配置指令參考的詳細講解

作者簡介&#xff1a; 辭七七&#xff0c;目前大二&#xff0c;正在學習C/C&#xff0c;Java&#xff0c;Python等 作者主頁&#xff1a; 七七的個人主頁 文章收錄專欄&#xff1a; 七七的閑談 歡迎大家點贊 &#x1f44d; 收藏 ? 加關注哦&#xff01;&#x1f496;&#x1f…

基于c++版數據結構基于數組棧改-Python思維總結

##棧部分-&#xff08;疊貓貓&#xff09; ##抽象數據類型棧的定義&#xff1a;是一種遵循先入后出的邏輯的線性數據結構。 換種方式去理解這種數據結構如果我們在一摞盤子中取到下面的盤子&#xff0c;我們首先要把最上面的盤子依次拿走&#xff0c;才可以繼續拿下面的盤子&…

【Java期末復習資料】(2)常見例題 //持續更新

本文章主要是常見例題&#xff0c;解析不會太詳細&#xff0c;有問題、不會的可以給我發消息哦&#xff0c;后續會出模擬卷 常見例題&#xff1a; 1.下列跟Java技術平臺有關的是&#xff08;ABD&#xff09; A.JVM B.JDK C.JPN D.JRE 2.面向對象的特征包括&#xff08;ACD&…

wxPython的控件tree

wxPython樹控件介紹 樹&#xff08;tree&#xff09;是一種通過層次結構展示信息的控件&#xff0c;如下圖所示是樹控件示例&#xff0c;左窗口中是樹控件&#xff0c;在wxPython中樹控件類是wx.TreeCtrl。 wx.TreeCtrl常用的方法有 AddRoot(text, image-1, selImage-1, data…

在Deepin中安裝x11vnc工具并結合內網穿透軟件實現遠程訪問桌面

文章目錄 1. 安裝x11vnc2. 本地遠程連接測試3. Deepin安裝Cpolar4. 配置公網遠程地址5. 公網遠程連接Deepin桌面6. 固定連接公網地址7. 固定公網地址連接測試 x11vnc是一種在Linux系統中實現遠程桌面控制的工具&#xff0c;它的原理是通過X Window系統的協議來實現遠程桌面的展…

P4 Qt如何添加qss樣式表文件和添加圖片資源

目錄 前言 01 添加圖片資源文件 02 添加qss文件 前言 &#x1f3ac; 個人主頁&#xff1a;ChenPi &#x1f43b;推薦專欄1: 《C_ChenPi的博客-CSDN博客》??? &#x1f525; 推薦專欄2: 《Qt基礎_ChenPi的博客-CSDN博客》??? &#x1f33a;本篇簡介 &#xff1a;這一章…

JVM Optimization Learning(六)

目錄 一、JVM Optimization 1、Shenandoah Shenandoah的使用方法 2、ZGC ZGC的版本更迭 ZGC的使用方法 ZGC的參數設置 3、JMH測試GC性能 一、JVM Optimization 1、Shenandoah Shenandoah是由Red Hat開發的一款低延遲的垃圾收集器&#xff0c;Shenandoah并發執行大部分…

機器人純阻抗控制接觸剛性環境(阻尼影響因素)

問題描述 在機器人學中&#xff0c;阻抗控制是一種常用的控制策略&#xff0c;用于管理機器人在與環境交互時的運動和力。阻抗控制背后的關鍵概念是將環境視為導納&#xff0c;而將機器人視為阻抗。 純阻抗控制接觸剛性環境時&#xff0c;機器人的行為方式主要受其阻抗參數的…

數據結構和算法專題---6、定時算法與應用

本章我們會對定時算法做個簡單介紹&#xff0c;包括常用的定時算法&#xff08;最小堆、時間輪&#xff09;的概述、實現方式、典型場景做個說明。 概述 系統或者項目中難免會遇到各種需要自動去執行的任務&#xff0c;實現這些任務的手段也多種多樣&#xff0c;如操作系統的…

【C++】使用“/**/“進行注釋的好處

2023年12月10日&#xff0c;周日晚上 我今天下午看Google Chrome的源碼時&#xff0c;才發現"/**/"原來還能這么用 使用"/**/"的好處就是&#xff0c;可以在任何地方進行注釋&#xff0c;哪怕是參數列表 void CircularWindow::enterEvent(QEvent *event/…

【Python】判斷域名是否合法

python判斷域名是否合法|校驗域名 域名以點號分隔成多個字符串。單個字符串由各國文字的特定字符集、字母、數字、連字符&#xff08;-&#xff09;組成&#xff0c;字母不區分大小寫&#xff0c;連字符&#xff08;-&#xff09;不得出現在字符串的頭部或者尾部。單個字符串長…

GitHub Enterprise Server 添加代碼安全、自動化功能

GitHub的軟件更新用于管理私有服務器上的存儲庫&#xff0c;具有GitHub容器注冊訪問、Dependabot安全警報和更新以及可重用工作流的特性。 GitHub Enterprise Server 3.5是GitHub用于托管和管理私有服務器上存儲庫的最新版本&#xff0c;它引入了新的代碼安全特性&#xff0c;新…

Helm 常用運維命令

原理參考 ## https://blog.csdn.net/knight_zhou/article/details/122079292 常用運維命令 helm search: ??搜索charthelm pull: ???下載chart到本地目錄查看helm install: ??上傳chart到Kuberneteshelm list: ????列出已發布的chart

【開源】基于Vue和SpringBoot的車險自助理賠系統

項目編號&#xff1a; S 018 &#xff0c;文末獲取源碼。 \color{red}{項目編號&#xff1a;S018&#xff0c;文末獲取源碼。} 項目編號&#xff1a;S018&#xff0c;文末獲取源碼。 目錄 一、摘要1.1 項目介紹1.2 項目錄屏 二、功能模塊2.1 數據中心模塊2.2 角色管理模塊2.3 車…

Maven基礎

目錄 Maven坐標 坐標簡介 主要組成 Maven依賴管理 配置依賴 依賴簡介 配置依賴 依賴傳遞 依賴傳遞簡介 排除依賴 依賴范圍 生命周期 生命周期簡介 執行指定生命周期 Maven坐標 坐標簡介 Maven中的坐標是資源的唯一標識&#xff0c;通過該坐標可以唯一定位資…

Redis交互速度慢,CPU占用100%,集群方案,報錯等問題

后續補充結論 仔細查看前輩們堆的代碼中發現居然調用了大量key*查詢&#xff0c;導致走的遍歷非常慢&#xff01;因為這相當與全部數據量遍歷&#xff0c;即這個原因導致了查詢速度與數據量成正比&#xff0c;推測也是CPU占用高的元兇&#xff1b;即使加上key前綴再匹配*也會走…

Python開發運維:Python調用K8S API實現資源管理

目錄 一、實驗 1.Python操作K8S API獲取資源 2.Python操作K8S API創建deployment資源 3.Python操作K8S API刪除k8s資源 4.Python操作K8S API修改k8s資源 5.Python操作K8S API查看k8s資源 二、問題 1.Windows11安裝kubernetes報錯 2.Python通過調用哪些方法實現Pod和De…

在SpringData JPA 中實現對持久層的操作

1.導入依賴 hibernate 這個依賴自帶實現JPA接口 <dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><depen…