Comment/Uncomment selected code in Visual C++



    给VC增加注释/反注释的功能
                ——杨科  注:本文可随意转载, 但请保留我的署名(CopyLeft)

用过VB,PowerBuilder的朋友一定知道在它们的工具条上有两个按钮,用来完成对选中的代码进行注释或反注释,而在VC中如果要注释一段选中的代码,除了在要注释的代码中添加/* 和 */外,就是对每一行都使用单行注释//。其实在VC中提供了编写插件的功能,VC将开发环境中的各种对象以COM接口的形式暴露出来,而且提供的插件应用程序向导可以完成大部分的框架代码,所以我们只需要添加我们想要的功能就可以了。所以我们可以利用这些COM接口来对VC的开发环境进行操作,这样我们就可以完成对选中的代码的注释和反注释功能。

下面具体描述以下开发这个插件的过程:
首先创建一个新工程,类型选择DevStudio Add-in Widzard,填写工程名称后,单击OK继续。在接下来的页面中选中Provides a toolbar,这会使我们创建的插件具有一个工具条,不需要选中Responds to Developer Studio events,因为我们不需要对开发环境中的事件作出响应,在上面的两个文本框中,可以随便输入一些你对这个插件的描述及功能介绍,单击Finish完成向导。

对了,首先声明以下,在VC中每一个Add-in都是一个COM组件,在Add-in的向导中生成的程序是用MFC和ATL共同实现的,所以在生成的原代码中你会看到两个分别叫做theApp和_Module的全局变量。另外向导为我们生成了一个成为ICommands的接口,我们必须在这个接口添加适当的方法来完成我们需要的功能。

让我们看一下AppWidzard为我们生成了哪些类,首先我们会看到一个CCommands的类,而且其中实现了我们在上面提到的ICommands接口。一个称为CDSAddIn的类,这个类中实现两个方法OnConnection(), OnDisconnection(), 这两个方法我们不会直接在程序中调用,而是由VC集成环境调用的,当VC启动时它会首先查询关于Add-ins的注册信息,然后调用相应组件的OnConnection()方法,所以在这个方法中我们应该把我们要实现的命令添加到VC的继承环境中,当这个插件被卸载或VC关闭时,VC会调用OnDisconnection()方法,在这里我们应该释放我们在OnConnection()中分配的资源。

剩下的就是应用程序类了,另外还有一些必须的全局函数DllGetClassObject, DllCanUnloadNow, DllRegisterServer和DllUnregisterServer是COM组件的几个通用实现,可以查阅关于COM的书籍来了解这些知识。

AppWidzard生成的代码中在ICommands接口中实现了一个与应用程序同名的方法,通常这个方法名称并不是我们需要的,所以下面就开始来改写这个方法,把它改成CommentCode。双击ICommands接口中的与你应用程序同名的方法,VC会打开一个与你的应用程序同名的一个扩展名是odl的文件,找到一行叫做HRESULT YourAppCommandMethod()并把YourAppCommandMethod改成CommentCode(),然后打开Commands.cpp和Commands.h把其中的YourAppCommandMethod都改成CommentCode()。

现在我们修改DSAddIn.cpp,找到OnConnection方法,直接找到下面这断代码:
LPCTSTR szCommand = _T("YourAppCommand");
VARIANT_BOOL bRet;
CString strCmdString;
strCmdString.LoadString(IDS_CMD_STRING);
strCmdString = szCommand + strCmdString;
CComBSTR bszCmdString(strCmdString);
CComBSTR bszMethod(_T("YourAppCommandMethod"));
CComBSTR bszCmdName(szCommand);
VERIFY_OK(pApplication->AddCommand(bszCmdString, bszMethod, 0, m_dwCookie, &bRet));
并把它改成下面的样子:
LPCTSTR szCommand = _T("CommentCode");
VARIANT_BOOL bRet;
CString strCmdString;
strCmdString.LoadString(IDS_CMD_STRING);
strCmdString = szCommand + strCmdString;
CComBSTR bszCmdString(strCmdString);
CComBSTR bszMethod(_T("CommentCode"));
CComBSTR bszCmdName(szCommand);
VERIFY_OK(pApplication->AddCommand(bszCmdString, bszMethod, 0, m_dwCookie, &bRet));
由于上面的代码涉及到了字符串资源,所以我们再来看看上面提到的IDS_CMD_STRING的内容,打开这个字符串资源改成下面的形式:
/nComment Code/nComment the selected code/nComment the selected code

现在我们可以编译这个应用程序,应该不会出现错误,单击运行会弹出一个对话框要你指定一个宿主应用程序来调用这个DLL, 因为我们是为VC开发插件应用,所以找到你VC的可执行文件目录并选中msdev.exe。当另一个VC启动后,选择Tools¦Customize...,丛属性页种选择Add-ins and Macro Files标签,单击Browse按钮,选择你刚生成的DLL文件,这时旁边的列表框,会出现你编写的插件的名字,单击Close关闭对话框,这是在工具条的下面会出现一个小工具条,将鼠标移动工具条的按钮上面,会出现Comment the selected code的代码提示, 单击按钮会弹出一个对话框,这是AppWidzard对这个命令的缺省实现。

下面我们该实现我们的具体功能的代码了。由于VC把它集成环境中对象通过COM接口暴露出来,所以我们就利用这些对象来实现注释代码的功能。这里有两个问题:1、我们怎么能知道VC中当前打开的文档是否是源代码而不是图形或对话框,2、我们怎么才能知道当前源代码文档中的被选择的文本。下面我通过实际的代码来解释这两个问题。

1、查找当前打开的源代码编辑其中的文档并查找当前选中的文本。这里我写了一个函数因为在实现反注释的时候我们还需要完成相同的功能。请参阅代码中的注释(注意相应接口指针的释放):
HRESULT CCommands::GetTextSelection(IApplication *pApplication, ITextSelection** pTextSelection)
{
IDispatch* pDispatch;
//通过有集成环境传进来的应用程序对象查找当前处于活动状态的文档对象,注意返回的是一个IDispatch接口指针。
HRESULT hr = pApplication->get_ActiveDocument(&pDispatch);
//此处应注意即使get_ActiveDocument()函数成功返回pDispatch指针仍有可能为空(即VC中没有文档打开),所以此处要判 //断pDispatch是否为空
if (SUCCEEDED(hr) && pDispatch != NULL)
{
//取得IGenericDocument接口的指针,我们可以通过该接口指针来查询当前的活动文档是否是文本文档。
IGenericDocument* pDocument;
hr = pDispatch->QueryInterface(IID_IGenericDocument, (void**)&pDocument);
if (FAILED(hr))
{
pDispatch->Release();
return E_NOINTERFACE;
}
pDispatch->Release();

//在此处查询ITextDocument接口,如果能成功返回,就说明当前文档是文本文档。
ITextDocument* pTextDocument;
hr = pDocument->QueryInterface(IID_ITextDocument, (void**)&pTextDocument);
if (FAILED(hr))
{
pDocument->Release();
return E_NOINTERFACE;
}
pDocument->Release();

//现在我们有了当前的活动的文本文档,我们就可以通过get_Selection函数来获得当前选中的文本,这里要求的还是 //一个IDispatch接口的指针
hr = pTextDocument->get_Selection(&pDispatch);
if (FAILED(hr))
{
pTextDocument->Release();
return E_NOINTERFACE;
}
pTextDocument->Release();
//通过QueryInterface()方法取回ITextSelection的接口指针。
hr = pDispatch->QueryInterface(IID_ITextSelection, (void**)pTextSelection);
if (FAILED(hr))
{
pDispatch->Release();
return E_NOINTERFACE;
}
pDispatch->Release();

}
else
return E_NOINTERFACE;
return S_OK;
}

上面的方法已经解决了这两个问题,接下来的任务就是实现注释被选中的代码。请参阅下面的代码(省略了错误处理的部分代码)
HRESULT CCommands::CommentSelectedCode(ITextSelection *pTextSelection)
{
long lTopLine = -1;
long lBottomLine = -1;
long lCurLine;
long lCurColumn;
//取得被选中文本的最上面一行的行号
HRESULT hr = pTextSelection->get_TopLine(&lTopLine);
//取得被选中文本的最下面一行的行号
hr = pTextSelection->get_BottomLine(&lBottomLine);

long iLine;
CString s;
_variant_t v((long)dsMove);
//循环,针对选中的每一行,在前面加上VC的单行注释//
for(iLine = lTopLine; iLine <= lBottomLine; iLine++)
{
hr = pTextSelection->MoveTo(iLine, 1, v);
if (SUCCEEDED(hr))
{
pTextSelection->SelectLine();
BSTR bstrLineText;
hr = pTextSelection->get_Text(&bstrLineText);
if (SUCCEEDED(hr))
{
s = bstrLineText;
s = _T("//") + s;
pTextSelection->put_Text(s.AllocSysString());
}
}
}
return S_OK;
}

上面的代码就完成了对选中的代码的注释问题,下面添加对选中的代码的反注释功能,其中的代码大体相同,这里主要讲解如何间命令添加到VC的开发环境中去。
在ClassView中右键单击ICommands接口,选择Add method...,添加UncommentCode方法,参照上面的代码实现相应的功能。
打开CDSAddIn类的OnConnection方法,在添加CommentCode方法的下面添加下面的代码:
szCommand = _T("UncommentCode");
strCmdString.LoadString(IDS_CMD_UNCOMMENT);
strCmdString = szCommand + strCmdString;
bszCmdString = strCmdString;
bszMethod = _T("UncommentCode");
CComBSTR bszCmdUncommentName = szCommand;
VERIFY_OK(pApplication->AddCommand(bszCmdString, bszMethod, 1, m_dwCookie, &bRet));

然后在OnConnection()方法的下面的代码中
if (bFirstTime == VARIANT_TRUE)
{
VERIFY_OK(pApplication->AddCommandBarButton(dsGlyph, bszCmdName, m_dwCookie));
}
添加一行,如下面的代码
if (bFirstTime == VARIANT_TRUE)
{
VERIFY_OK(pApplication->AddCommandBarButton(dsGlyph, bszCmdName, m_dwCookie));
VERIFY_OK(pApplication->AddCommandBarButton(dsGlyph, bszCmdUncommentName, m_dwCookie));
}
在上面的pApplication->AddCommand()调用中,第三个参数1,指定了该命令所对应的图像在位图资源中的位置。

适当的修正工程的资源,完成的应用程序应该可以完成VB,PowerBuilder中的代码的注释和反注释功能。

另外,由于VC中的插件是通过COM技术实现的,所以能支持COM规范的语言都可以用来编写VC插件。
#include "stm32g4xx_hal.h" #include "time.h" #include "main.h" #include "DCAC_ADC.h" #include "OLED.h" #include "adc.h" #include "math.h" #include "PID.h" #include "SPWM.h" #define ADC_SAMPLES 100 extern float Vcc; uint16_t volt[ADC_SAMPLES]; // DMA采集缓冲区 float V_value = 0.0f; // RMS值 float voltage ; // 平均电压 float Top; float PID_Top; float Sum_V_value=0; float target_votage; extern volatile uint8_t dma_complete_flag; // DMA完成标志 extern TIM_HandleTypeDef htim3; extern uint8_t Key_flag; float zhj_voltage; uint32_t adc_raw_value = 0; float voltage = 0.0f; // void Catch_start() { // ADC校准+DMA采集启动(只需一次) HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED); HAL_ADC_Start_DMA(&hadc1, (uint32_t*)volt, ADC_SAMPLES); HAL_TIM_Base_Start_IT(&htim3); } void Show_ADC1(float Move_value)//交流电压 { if(dma_complete_flag) { static uint16_t Sum_count = 0; // 静态变量保持状态 int32_t sum_rms = 0; for(int i = 0; i < ADC_SAMPLES; i++) { sum_rms += (volt[i]-(Move_value*1241))*(volt[i]-(Move_value*1241)); } //V_value = sqrt((float)sum_rms / ADC_SAMPLES); // 有效值(单位: ADC码) //滤波 Sum_V_value+=sqrt((float)sum_rms / ADC_SAMPLES); Sum_count++; if(Sum_count==35) { V_value=Sum_V_value/35; //最终ADC值 Sum_count=0; Sum_V_value=0; } // voltage = V_value* 3.3f / 4095.0f; // 真实值 Top=voltage*sqrtf(2); //当前值 target_votage=get_current_votage();//目标值 // 峰值 PID_Top=Buck_PID(target_votage,Top); // 增大单次调整限制和总输出限制 set_pwm(PID_Top); dma_complete_flag = 0; // 清除标志 // 重新启动DMA采集,循环模式不需要开启 // HAL_ADC_Start_DMA(&hadc1, (uint32_t*)volt, ADC_SAMPLES); } } void adc_read_and_display(void)//直流电压 { // 启动ADC转换 HAL_ADC_Start(&hadc2); // 等待转换完成(超时100ms) if (HAL_ADC_PollForConversion(&hadc2, 10) == HAL_OK) { // 读取原始ADC值(12位,范围0-4095) adc_raw_value = HAL_ADC_GetValue(&hadc2); // 转换为电压值(假设参考电压3.3V) zhj_voltage = (adc_raw_value * 3.3f) / 4095.0f; OLED_ShowString(1, 1, "Zhi_V:"); OLED_ShowString(4, 1, "Zhi_A:"); OLED_ShowString(2, 3, "Raw:"); OLED_ShowNum(2, 7, adc_raw_value, 4); // 显示4位原始值 // 在OLED上显示电压值(第2行,从第1列开始) OLED_ShowString(3, 3, "Volt:"); OLED_ShowNum(3, 8, (uint32_t)zhj_voltage, 1); // 整数部分 OLED_ShowString(3, 9, "."); OLED_ShowNum(3, 10, (uint32_t)(zhj_voltage * 1000) % 1000, 3); // 小数部分(毫伏级) OLED_ShowString(3, 13, "V"); } // 停止ADC转换 HAL_ADC_Stop(&hadc2); } 我的ADC2不工作/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file adc.c * @brief This file provides code for the configuration * of the ADC instances. ****************************************************************************** * @attention * * Copyright (c) 2025 STMicroelectronics. * 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. * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "adc.h" /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ ADC_HandleTypeDef hadc1; ADC_HandleTypeDef hadc2; DMA_HandleTypeDef hdma_adc1; /* ADC1 init function */ void MX_ADC1_Init(void) { /* USER CODE BEGIN ADC1_Init 0 */ /* USER CODE END ADC1_Init 0 */ ADC_MultiModeTypeDef multimode = {0}; ADC_ChannelConfTypeDef sConfig = {0}; /* USER CODE BEGIN ADC1_Init 1 */ /* USER CODE END ADC1_Init 1 */ /** Common config */ hadc1.Instance = ADC1; hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2; hadc1.Init.Resolution = ADC_RESOLUTION_12B; hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT; hadc1.Init.GainCompensation = 0; hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE; hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV; hadc1.Init.LowPowerAutoWait = DISABLE; hadc1.Init.ContinuousConvMode = ENABLE; hadc1.Init.NbrOfConversion = 1; hadc1.Init.DiscontinuousConvMode = DISABLE; hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIG_T3_TRGO; hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING; hadc1.Init.DMAContinuousRequests = ENABLE; hadc1.Init.Overrun = ADC_OVR_DATA_PRESERVED; hadc1.Init.OversamplingMode = DISABLE; if (HAL_ADC_Init(&hadc1) != HAL_OK) { Error_Handler(); } /** Configure the ADC multi-mode */ multimode.Mode = ADC_MODE_INDEPENDENT; if (HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK) { Error_Handler(); } /** Configure Regular Channel */ sConfig.Channel = ADC_CHANNEL_15; sConfig.Rank = ADC_REGULAR_RANK_1; sConfig.SamplingTime = ADC_SAMPLETIME_47CYCLES_5; sConfig.SingleDiff = ADC_SINGLE_ENDED; sConfig.OffsetNumber = ADC_OFFSET_NONE; sConfig.Offset = 0; if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN ADC1_Init 2 */ /* USER CODE END ADC1_Init 2 */ } /* ADC2 init function */ void MX_ADC2_Init(void) { /* USER CODE BEGIN ADC2_Init 0 */ /* USER CODE END ADC2_Init 0 */ ADC_ChannelConfTypeDef sConfig = {0}; /* USER CODE BEGIN ADC2_Init 1 */ /* USER CODE END ADC2_Init 1 */ /** Common config */ hadc2.Instance = ADC2; hadc2.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2; hadc2.Init.Resolution = ADC_RESOLUTION_12B; hadc2.Init.DataAlign = ADC_DATAALIGN_RIGHT; hadc2.Init.GainCompensation = 0; hadc2.Init.ScanConvMode = ADC_SCAN_DISABLE; hadc2.Init.EOCSelection = ADC_EOC_SINGLE_CONV; hadc2.Init.LowPowerAutoWait = DISABLE; hadc2.Init.ContinuousConvMode = DISABLE; hadc2.Init.NbrOfConversion = 1; hadc2.Init.DiscontinuousConvMode = DISABLE; hadc2.Init.ExternalTrigConv = ADC_SOFTWARE_START; hadc2.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; hadc2.Init.DMAContinuousRequests = DISABLE; hadc2.Init.Overrun = ADC_OVR_DATA_PRESERVED; hadc2.Init.OversamplingMode = DISABLE; if (HAL_ADC_Init(&hadc2) != HAL_OK) { Error_Handler(); } /** Configure Regular Channel */ sConfig.Channel = ADC_CHANNEL_15; sConfig.Rank = ADC_REGULAR_RANK_1; sConfig.SamplingTime = ADC_SAMPLETIME_640CYCLES_5; sConfig.SingleDiff = ADC_SINGLE_ENDED; sConfig.OffsetNumber = ADC_OFFSET_NONE; sConfig.Offset = 0; if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN ADC2_Init 2 */ /* USER CODE END ADC2_Init 2 */ } static uint32_t HAL_RCC_ADC12_CLK_ENABLED=0; void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle) { GPIO_InitTypeDef GPIO_InitStruct = {0}; RCC_PeriphCLKInitTypeDef PeriphClkInit = {0}; if(adcHandle->Instance==ADC1) { /* USER CODE BEGIN ADC1_MspInit 0 */ /* USER CODE END ADC1_MspInit 0 */ /** Initializes the peripherals clocks */ PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC12; PeriphClkInit.Adc12ClockSelection = RCC_ADC12CLKSOURCE_SYSCLK; if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) { Error_Handler(); } /* ADC1 clock enable */ HAL_RCC_ADC12_CLK_ENABLED++; if(HAL_RCC_ADC12_CLK_ENABLED==1){ __HAL_RCC_ADC12_CLK_ENABLE(); } __HAL_RCC_GPIOB_CLK_ENABLE(); /**ADC1 GPIO Configuration PB0 ------> ADC1_IN15 */ GPIO_InitStruct.Pin = GPIO_PIN_0; GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); /* ADC1 DMA Init */ /* ADC1 Init */ hdma_adc1.Instance = DMA1_Channel1; hdma_adc1.Init.Request = DMA_REQUEST_ADC1; hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY; hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE; hdma_adc1.Init.MemInc = DMA_MINC_ENABLE; hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; hdma_adc1.Init.Mode = DMA_CIRCULAR; hdma_adc1.Init.Priority = DMA_PRIORITY_LOW; if (HAL_DMA_Init(&hdma_adc1) != HAL_OK) { Error_Handler(); } __HAL_LINKDMA(adcHandle,DMA_Handle,hdma_adc1); /* ADC1 interrupt Init */ HAL_NVIC_SetPriority(ADC1_2_IRQn, 0, 0); HAL_NVIC_EnableIRQ(ADC1_2_IRQn); /* USER CODE BEGIN ADC1_MspInit 1 */ /* USER CODE END ADC1_MspInit 1 */ } else if(adcHandle->Instance==ADC2) { /* USER CODE BEGIN ADC2_MspInit 0 */ /* USER CODE END ADC2_MspInit 0 */ /** Initializes the peripherals clocks */ PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC12; PeriphClkInit.Adc12ClockSelection = RCC_ADC12CLKSOURCE_SYSCLK; if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) { Error_Handler(); } /* ADC2 clock enable */ HAL_RCC_ADC12_CLK_ENABLED++; if(HAL_RCC_ADC12_CLK_ENABLED==1){ __HAL_RCC_ADC12_CLK_ENABLE(); } __HAL_RCC_GPIOB_CLK_ENABLE(); /**ADC2 GPIO Configuration PB15 ------> ADC2_IN15 */ GPIO_InitStruct.Pin = GPIO_PIN_15; GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); /* ADC2 interrupt Init */ HAL_NVIC_SetPriority(ADC1_2_IRQn, 0, 0); HAL_NVIC_EnableIRQ(ADC1_2_IRQn); /* USER CODE BEGIN ADC2_MspInit 1 */ /* USER CODE END ADC2_MspInit 1 */ } } void HAL_ADC_MspDeInit(ADC_HandleTypeDef* adcHandle) { if(adcHandle->Instance==ADC1) { /* USER CODE BEGIN ADC1_MspDeInit 0 */ /* USER CODE END ADC1_MspDeInit 0 */ /* Peripheral clock disable */ HAL_RCC_ADC12_CLK_ENABLED--; if(HAL_RCC_ADC12_CLK_ENABLED==0){ __HAL_RCC_ADC12_CLK_DISABLE(); } /**ADC1 GPIO Configuration PB0 ------> ADC1_IN15 */ HAL_GPIO_DeInit(GPIOB, GPIO_PIN_0); /* ADC1 DMA DeInit */ HAL_DMA_DeInit(adcHandle->DMA_Handle); /* ADC1 interrupt Deinit */ /* USER CODE BEGIN ADC1:ADC1_2_IRQn disable */ /** * Uncomment the line below to disable the "ADC1_2_IRQn" interrupt * Be aware, disabling shared interrupt may affect other IPs */ /* HAL_NVIC_DisableIRQ(ADC1_2_IRQn); */ /* USER CODE END ADC1:ADC1_2_IRQn disable */ /* USER CODE BEGIN ADC1_MspDeInit 1 */ /* USER CODE END ADC1_MspDeInit 1 */ } else if(adcHandle->Instance==ADC2) { /* USER CODE BEGIN ADC2_MspDeInit 0 */ /* USER CODE END ADC2_MspDeInit 0 */ /* Peripheral clock disable */ HAL_RCC_ADC12_CLK_ENABLED--; if(HAL_RCC_ADC12_CLK_ENABLED==0){ __HAL_RCC_ADC12_CLK_DISABLE(); } /**ADC2 GPIO Configuration PB15 ------> ADC2_IN15 */ HAL_GPIO_DeInit(GPIOB, GPIO_PIN_15); /* ADC2 interrupt Deinit */ /* USER CODE BEGIN ADC2:ADC1_2_IRQn disable */ /** * Uncomment the line below to disable the "ADC1_2_IRQn" interrupt * Be aware, disabling shared interrupt may affect other IPs */ /* HAL_NVIC_DisableIRQ(ADC1_2_IRQn); */ /* USER CODE END ADC2:ADC1_2_IRQn disable */ /* USER CODE BEGIN ADC2_MspDeInit 1 */ /* USER CODE END ADC2_MspDeInit 1 */ } } /* USER CODE BEGIN 1 */ // hdma_adc1.Init.Mode = DMA_CIRCULAR;---------136�� /* USER CODE END 1 */
最新发布
07-11
在STM32 HAL库开发中,ADC2无法正常工作通常涉及初始化配置、DMA设置或GPIO引脚配置问题。以下是一些关键点和排查建议: ### 三、ADC2 初始化检查 确保ADC2的初始化流程正确,包括时钟使能、通道配置、采样时间设置等。使用CubeMX生成代码后,应检查`MX_ADC2_Init()`函数中的参数是否符合预期。 ```c void MX_ADC2_Init(void) { hadc2.Instance = ADC2; hadc2.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; hadc2.Init.Resolution = ADC_RESOLUTION_12B; hadc2.Init.ScanConvMode = ENABLE; // 多通道采集需要开启扫描模式 hadc2.Init.ContinuousConvMode = ENABLE; // 连续转换模式 hadc2.Init.DiscontinuousConvMode = DISABLE; hadc2.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; hadc2.Init.ExternalTrigConv = ADC_SOFTWARE_START; hadc2.Init.DataAlign = ADC_DATAALIGN_RIGHT; hadc2.Init.NbrOfConversion = 2; // 假设有两个通道 hadc2.Init.DMAContinuousRequests = ENABLE; hadc2.Init.EOCSelection = ADC_EOC_SINGLE_CONV; if (HAL_ADC_Init(&hadc2) != HAL_OK) { Error_Handler(); } } ``` 同时,确保ADC通道的配置正确,例如使用`HAL_ADC_ConfigChannel()`函数设置每个通道的参数[^1]。 ### 四、DMA 配置与中断处理 如果ADC2使用DMA进行数据传输,必须正确配置DMA通道并与ADC2关联。检查`MX_DMA_Init()`函数中是否启用了ADC2的DMA请求,并确认DMA模式(循环模式或普通模式)是否符合应用需求。 ```c void MX_DMA_Init(void) { __HAL_RCC_DMA2_CLK_ENABLE(); hdma_adc2.Instance = DMA2_Stream0; hdma_adc2.Init.Channel = DMA_CHANNEL_1; hdma_adc2.Init.Direction = DMA_PERIPH_TO_MEMORY; hdma_adc2.Init.PeriphInc = DMA_PINC_DISABLE; hdma_adc2.Init.MemInc = DMA_MINC_ENABLE; hdma_adc2.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; hdma_adc2.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; hdma_adc2.Init.Mode = DMA_CIRCULAR; // 循环模式适合连续采样 hdma_adc2.Init.Priority = DMA_PRIORITY_HIGH; hdma_adc2.Init.FIFOMode = DMA_FIFOMODE_DISABLE; if (HAL_DMA_Init(&hdma_adc2) != HAL_OK) { Error_Handler(); } __HAL_LINKDMA(&hadc2, DMA_Handle, hdma_adc2); } ``` 此外,在启用DMA传输时,建议开启DMA传输完成中断,以便在数据准备好后进行处理,避免数据被覆盖。可以在`HAL_ADC_Start_DMA()`之后启用中断,并在中断回调函数中设置标志位[^2]。 ```c void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) { if (hadc == &hadc2) { adc_dma_complete_flag = 1; // 设置DMA传输完成标志 } } ``` ### 五、GPIO 引脚配置 ADC输入通道对应的GPIO引脚必须配置为模拟输入模式。例如,若使用PA0和PA1作为ADC2的输入通道,应在CubeMX或手动代码中设置为模拟模式。 ```c GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1; GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); ``` 检查实际使用的引脚是否与所选ADC通道对应,并确认没有与其他外设功能冲突。某些MCU型号的ADC通道可能仅限于特定引脚组[^1]。 ### 六、调试建议 - 使用调试器查看ADC寄存器状态,确认ADC是否进入就绪状态。 - 检查ADC校准是否成功执行,必要时调用`HAL_ADCEx_Calibration_Start()`。 - 在主循环中添加LED指示灯或串口打印语句,帮助定位程序卡顿或错误位置。 - 如果使用DMA循环模式,确保数据缓冲区足够大以容纳多个采样周期的数据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值