Добавить в корзинуПозвонить
Найти в Дзене
Паяльник и код

Как использовать АЦП на CH32V003

АЦП используется в приложениях для измерения аналогового напряжения от различных датчиков, напряжения батареи, тока и т. д. На микроконтроллере есть один 10-битный блок АЦП. Это АЦП общего назначения, ожидать слишком большой точности от него не можем. Диапазон чувствительности аналогового входа — от 0 до VDD (2,7~5,5 В). Точность снижается, если напряжение ниже 2,9В. Всего 10 каналов: 8 внешних каналов (A0 – A7) + 2 внутренних канала (8 - VREF, 9 - калибровочных напряжений) Восьмой канал внутреннее опорное напряжения. Будем его использовать. Картинка из справки Опорное напряжение 1.2В Напишем программу для считывания напряжения с пина PC4 Исходник для MounRiver Studio 1. Функция инициализации ADC static void Init_ADC(void) {
GPIO_InitTypeDefGPIO_InitStructure = { 0 };
ADC_InitTypeDef ADC_InitStructure = { 0 }; //вкл тактирование периферии
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOC, ENABLE);
//частота 3 МГц
RCC_ADCCLKConfig(RCC_PCLK2_Div8)
Оглавление

АЦП используется в приложениях для измерения аналогового напряжения от различных датчиков, напряжения батареи, тока и т. д.

На микроконтроллере есть один 10-битный блок АЦП. Это АЦП общего назначения, ожидать слишком большой точности от него не можем. Диапазон чувствительности аналогового входа — от 0 до VDD (2,7~5,5 В). Точность снижается, если напряжение ниже 2,9В. Всего 10 каналов: 8 внешних каналов (A0 – A7) + 2 внутренних канала (8 - VREF, 9 - калибровочных напряжений)

Восьмой канал внутреннее опорное напряжения. Будем его использовать. Картинка из справки

-2

Опорное напряжение 1.2В

Исходный код

Напишем программу для считывания напряжения с пина PC4

Исходник для MounRiver Studio

ADC.zip

1. Функция инициализации ADC

static void Init_ADC(void) {
GPIO_InitTypeDefGPIO_InitStructure = { 0 };
ADC_InitTypeDef ADC_InitStructure = { 0 };
//вкл тактирование периферии
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOC,
ENABLE);
//частота 3 МГц
RCC_ADCCLKConfig(RCC_PCLK2_Div8);
//настройки pin PC4
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
//настройки вход
GPIO_InitStructure.GPIO_Mode =
GPIO_Mode_AIN;
GPIO_Init(GPIOC, &GPIO_InitStructure);
//переинициализация ADC
ADC_DeInit(ADC1);
//настройки для ADC
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode =
DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode =
DISABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);
//вкл калибровки
ADC_Calibration_Vol(ADC1, ADC_CALVOL_50PERCENT);
//вкл ADC
ADC_Cmd(ADC1,
ENABLE);
//калибровка
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1)) {}
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1)) {}
}

2. Функция для считывания опорного напряжения с порта ADC_Channel_Vrefint

static uint16_t Get_ADC_Vrefint(void) {
ADC_RegularChannelConfig(ADC1, ADC_Channel_Vrefint, 1, ADC_SampleTime_241Cycles);
ADC_SoftwareStartConvCmd(ADC1,
ENABLE);
while (! ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)) {}
returnADC_GetConversionValue(ADC1);
}

3. Функция считывания с внешнего источника. Порт считывания передаем через параметр ch

static uint16_t Get_ADC(uint8_t ch) {
ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_241Cycles);
ADC_SoftwareStartConvCmd(ADC1,
ENABLE);
while (! ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)) {}
returnADC_GetConversionValue(ADC1);
}

4. Задаём количество считываний для более точного расчета

const int n = 100;

5. Тело основной функции main

//инициализация
SystemCoreClockUpdate();
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
Delay_Init();
USART_Printf_Init(115200);
USARTx_CFG();
Delay_Ms(1000);
Init_ADC();
//переменная для хранения внешнего напряжения
int a = 0;
//переменная для хранения опорного напряжения
int v = 0;
while (1) {
ADC_Cmd(ADC1,
ENABLE);
a = 0;
v = 0;
for(int i=0; i<n; i++){
//берем напряжение с PC4
a+= Get_ADC(ADC_Channel_2);
//берем опорное напряжение
v+= Get_ADC_Vrefint();
Delay_Us(10);
}
//считаем среднее значение
a = a/n;
v = v/n;
//считаем напряжение
float adcVoltage = ((float)a * 1.20f) / (float)v;
//выводим в UART
char buffer[16];
int wholePart = (int) adcVoltage;
int decimalPart = (int)((adcVoltage-wholePart)*100);
sprintf(buffer, "%d.%02d", wholePart, decimalPart);
printf((uint8_t *)buffer);
printf("\r\n");
ADC_Cmd(ADC1,
DISABLE);
Delay_Ms(2000);
}

Принцип работы. Каждые 2 секунды включаем АЦП. 100 раз считываем с АЦП внешнее и опорное напряжение. Считаем среднее значение. По формуле высчитываем напряжение ([внешнее напряжение] * 1.2 / [опорное напряжение]). Выводим полученное значение. Отключаем АЦП

Тест

Для тестирования взял CH32V003A4M6 микроконтроллер

-3

И 3 батарейки с разным напряжением

Каждый тест батарейки проводилось при питании микроконтроллера 3.3В в начале и 5В (не стабильное) в конце

-4

Как видим очень хорошие результаты

Теперь можно померить напряжение больше 5В. Для этого понадобится резистивный делитель

-5

Я взял два резистора 100Ком и 10Ком. Получил точное их значение через тестер. Рассчитываем коэффициент

double resist = 9766.0000 / (99230.0000 + 9766.0000);

Добавляем его в нашу расчётную формулу

float adcVoltage = ((float)a * 1.20f) / (float)v / resist;

Теперь можно мерить напряжение до 50В

Для теста использовал блок питания 12В (мультиметр показал 12.12В)

-6

Так же 3.3В в начале и 5В в конце

Результат получился да же выше моих ожиданий

Для более точных измерений нужно питать микроконтроллер стабильным напряжением

Использование микроконтроллера для замера напряжения на литиевой батарее покажу в одной из следующих статей. Подписывайся, чтобы не пропустить!