STM32 Часть 9–Аналого-цифровой преобразователь (АЦП)
Сегодня по плану АЦП.
После прочтения документации на данный модуль мои волосы встали дыбом 😉 , так как такого большого количества режимов работы у модуля АЦП я не встречал. Но в то же время как и остальные модули работа и описание достаточно прозрачное.
Модуль АЦП
Кратенько рассмотрим что же “может” АЦП:
- разрешение 12-бит
- минимальное время преобразования 1,17 мкс (при частоте системной шины 24 МГц)
- встроенный ИОН и датчик температры
- встроенный механизм самокалибровки
- режимы однократного и циклического преобразования
- режим “сканирования” для последовательного преобразования нескольких каналов
- время преобразования устанавливается индивидуально для каждого канала
- инициировать преобразования возможно от внешних или внутренних сигналов
- и это ещё не всё 😉 читать подробнее в документции к МК
Функциональная схема блока АЦП:
И так приступим.
Для работы модуля АЦП необходимо:
- разрешить тактирование модуля
- сконфигурировать необходимый режим работы
Максимальная тактовая частота для модуля АЦП составляет 12 МГц, при частоте системной шины 24 МГц, на данном этапе выжимать по полной нет необходимости, поэтому продолжим работать от встроенного RC генератора частотой 8 МГц, при этом максимальная частота модуля может быть 4 Мгц, так как предварительный делитель имеет минимальный коэффициент деления двойку:
Установим предварительный делитель и разрешим тактирование модуля:
RCC->CFGR &= ~RCC_CFGR_ADCPRE; RCC->CFGR |= RCC_CFGR_ADCPRE_DIV2; RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
Примечание: ранее я не сделал акцент на одном моменте, как только вы разрешаете тактирование того или иного блока микроконтроллера, путем установки соответствующего бита в блоке управлением сбросом и тактированием RCC, то все регистры выбранного блока автоматически инициализируются значениями по умолчанию (если не ошибаюсь это касается всех модулей МК).
Из всех режимов работы наиболее простой режим однократного преобразования одного канала, с программным запуском.
Вначале необходимо выбрать канал АЦП, мне понравился десятый канал и сконфигурировать линию ввода-вывода (вывод PC0):
#define ADC_INPUT10 C, 0, HIGH, ANALOG, PIN_CONFIGURATION(ADC_INPUT10);
Модуль позволяет производить последовательное преобразование до 16 каналов, порядок каналов может быть любой, нам необходимо установить преобразование одного десятого канала. Для этого устанавливаем длину последовательности преобразований равной единице (значение после сброса АЦП) и десятый номер канала в первую ячейку (не уверен в правильности терминологии, так как английский знаю на три с минусом):
ADC1->SQR3 = ADC_SQR3_SQ1_1 | ADC_SQR3_SQ1_3;
На каждый канал выделено пяти битное поле, соответственно для 16 каналов необходимо три регистра (SQR1-SQR3), ячейка для первого преобразования занимает с 0 по 4 бит в регистре SQR3, соответственно для десятого канала необходимо установить 1 и 3 бит (ADC_SQR3_SQ1_1, ADC_SQR3_SQ1_3).
Далее необходимо выбрать источник запуска, нам нужен программный запуск:
ADC1->CR2 = ADC_CR2_EXTSEL_0 | ADC_CR2_EXTSEL_1 | ADC_CR2_EXTSEL_2 | ADC_CR2_EXTTRIG;
Ну вот теперь можно вывести АЦП из режима пониженного энергопотребления:
ADC1->CR2 |= ADC_CR2_ADON;
произвести калибровку АЦП:
ADC1->CR2 |= ADC_CR2_RSTCAL; while ((ADC1->CR2 & ADC_CR2_RSTCAL) == ADC_CR2_RSTCAL) { } ADC1->CR2 |= ADC_CR2_CAL; while ((ADC1->CR2 & ADC_CR2_RSTCAL) == ADC_CR2_CAL) { }
и всё 😉 можно пользоваться.
Функция целиком:
void mcu_adc_init(void) { RCC->CFGR &= ~RCC_CFGR_ADCPRE; RCC->CFGR |= RCC_CFGR_ADCPRE_DIV2; RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; ADC1->SQR3 = ADC_SQR3_SQ1_1 | ADC_SQR3_SQ1_3; ADC1->CR2 = ADC_CR2_EXTSEL_0 | ADC_CR2_EXTSEL_1 | ADC_CR2_EXTSEL_2 | ADC_CR2_EXTTRIG; ADC1->CR2 |= ADC_CR2_ADON; ADC1->CR2 |= ADC_CR2_RSTCAL; while ((ADC1->CR2 & ADC_CR2_RSTCAL) == ADC_CR2_RSTCAL) { } ADC1->CR2 |= ADC_CR2_CAL; while ((ADC1->CR2 & ADC_CR2_RSTCAL) == ADC_CR2_CAL) { } }
Для примера я использовал прерывания от таймера 17 (описан ранее), точнее прописал в обработчике: запуск – ожидание — считывание результата:
void mcu_adc_channel_10_convert(void) { ADC1->CR2 |= ADC_CR2_SWSTART; while ((ADC1->SR & ADC_SR_EOC) != ADC_SR_EOC) { } mcu_adc_channel_10_value = ADC1->DR; }
Результат преобразования вывожу на ЖКИ индикатор, ну и для тех у кого нет индикатора меняю частоту “мигания” зеленого светодиода в зависимости от результата преобразования:
void handler_tim1_trigger_and_communication_and_tim17(void) { if (TIM17->SR & TIM_SR_UIF) { TIM17->SR &= ~TIM_SR_UIF; mcu_adc_channel_10_convert(); if (test_count == 0) { PIN_ON(LED_GREEN); } else if (test_count == (1 + (mcu_adc_channel_10_value >> 4))) { PIN_OFF(LED_GREEN); } if (++test_count >= ((1 + (mcu_adc_channel_10_value >> 4)) * 2)) test_count = 0; } }
Напряжение на вход АЦП подаю используя обычный делитель напряжения выполненный на переменном резисторе:
Резистор R2 необходим для защиты линии ввода-вывода в случае неправильной конфигурации.
Небольшое видео, сори за тряску 😉
Всю необходимую информацию вы можете найти на офф стайте:
STM32F100RB — STM32 Value Line 32-bit Microcontrollers
Проект с примером:
[download id=»30″]
Обсуждение данной статьи и всего цикла на форуме.
Спасибо за статью ) Запуталяся как выбирать канал АЦП (ADC1->SQR3) не могли бы Вы подробние написать ? Спасибо
@Виктор
Ну, все зависит от того что вы хотите получить?
Пытаюсь настроить опрос первого канала.
Не идет.
Помогите…
Не могу понять, как менять номер опрашиваемого канала
Стоит задача опрашивать четыре канала и передавать данные на ПК.
Не доходит как измерять нужный канал
@Alex
Вам уже ответили на другом форуме. Если ещё актуально напишите, я завтра попробую
Вам просто оцифровать и потом считать или с использованием ПДП (DMA)
На каком форуме ответили?
@Alex
Ой, похоже перетрудился и ошибся
http://radiokot.ru/forum/viewtopic.php?f=20&t=52612
Спасибо за ссылку
По силке с радиокота «как считать с 2 и больше?» :
1.Также как раньше, только по очереди, каждый раз перенастраивая ADC на нужный канал
Сорри просто сейчас пока нет дискавери, не могу проверить(((, но хочу узнать ли правильно ?!
void adc_init(void)
{ RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; //Подключаем АЦП к линии тактирования
RCC->CFGR |= RCC_CFGR_ADCPRE; //Предделитель
RCC->CFGR |= RCC_CFGR_ADCPRE_DIV8;
ADC1->CR2 |= ADC_CR2_CAL; //Запуск калибровки (это нужно сделать ДО установки ADON)
while (!(ADC1->CR2 & ADC_CR2_CAL))
{
}
ADC1->SQR3 |= (ADC_SQR3_SQ1_3 | ADC_SQR3_SQ1_1); // 10 канал
ADC1->CR2 |= (ADC_CR2_EXTSEL_0 | ADC_CR2_EXTSEL_1 | ADC_CR2_EXTSEL_2 | ADC_CR2_EXTTRIG);
ADC1->CR2 |= ADC_CR2_ADON;
GPIOC->CRL &= ~GPIO_CRL_CNF0; //Аналоговый вход (это АЦП, 10й канал) PC0
GPIOC->CRL &= ~GPIO_CRL_MODE0;
GPIOC->CRL &= ~GPIO_CRL_CNF1; //Аналоговый вход (это АЦП, 11й канал) PC1
GPIOC->CRL &= ~GPIO_CRL_MODE1;
}
ADC1->SQR3 |= (ADC_SQR3_SQ1_3 | ADC_SQR3_SQ1_1); // 10 канал //
void ADC10(void)
{
ADC1->CR2 |= ADC_CR2_SWSTART; //Запуск преобразования
while (!(ADC1->SR & ADC_SR_EOC)) //Ждем окончания преобразования
adc10 = ADC1->DR;
}
ADC1->SQR3 |= (ADC_SQR3_SQ1_3 | ADC_SQR3_SQ1_1|ADC_SQR3_SQ1_0); // 11 канал // меняем канал
void ADC11(void)
{
ADC1->CR2 |= ADC_CR2_SWSTART; //Запуск преобразования
while (!(ADC1->SR & ADC_SR_EOC)) //Ждем окончания преобразования
adc11 = ADC1->DR;
}
2.»Использовать DMA для передачи только что преобразованных данных обычных каналов. Такой подход использовал я при управлении источником питания на stm32f100с4 (статья на этом сайте). »
((( вообще не разобрался с исходником все так запутано, можете навести пример, если будет время. Спасибо …
@Виктор
Вы не ответили на мой вопрос:
«Ну, все зависит от того что вы хотите получить?»
—
Как я понимаю у вас пока мало работы с контроллерами?
Попробуйте поработать со стандартной библиотекой, с ней в комплекте идут хорошие примеры по работе с каждым модулем.
Да, только начинаю разбираться,очень хочу разобраться, со стандартной библиотекой как то сложно, мне как то так проще …
Я хочу сначала померять на 10 канале, затем на 11…
1 метод проверил, — работает ))))
@Виктор
А по точнее?
необходимо ли:
периодическое преобразование?
однократное?
без или с использованием DMA?
вариантов работы масса.
Зря вы так, вам без либы будет очень сложно разобраться. С ней можно избежать явных ошибок в конфигурировании. Параллельно если хотите можете смотреть где какие битики устанавливаются.
да периодическое, сейчас пытаюсь разобраться с DMA. С либ. не пойму как по ней смотреть, куча функций, все расплывается перед глазами, что выбрать ?! ( С АВР было проще )
void ADC_DeInit(ADC_TypeDef* ADCx);
void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct);
void ADC_StructInit(ADC_InitTypeDef* ADC_InitStruct);
void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState);
void ADC_DMACmd(ADC_TypeDef* ADCx, FunctionalState NewState);
void ADC_ITConfig(ADC_TypeDef* ADCx, u16 ADC_IT, FunctionalState NewState);
void ADC_ResetCalibration(ADC_TypeDef* ADCx);
FlagStatus ADC_GetResetCalibrationStatus(ADC_TypeDef* ADCx);
void ADC_StartCalibration(ADC_TypeDef* ADCx);
FlagStatus ADC_GetCalibrationStatus(ADC_TypeDef* ADCx);
void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
FlagStatus ADC_GetSoftwareStartConvStatus(ADC_TypeDef* ADCx);
void ADC_DiscModeChannelCountConfig(ADC_TypeDef* ADCx, u8 Number);
void ADC_DiscModeCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, u8 ADC_Channel, u8 Rank, u8 ADC_SampleTime);
void ADC_ExternalTrigConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
u16 ADC_GetConversionValue(ADC_TypeDef* ADCx);
u32 ADC_GetDualModeConversionValue(void);
void ADC_AutoInjectedConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
void ADC_InjectedDiscModeCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
void ADC_ExternalTrigInjectedConvConfig(ADC_TypeDef* ADCx, u32 ADC_ExternalTrigInjecConv);
void ADC_ExternalTrigInjectedConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
void ADC_SoftwareStartInjectedConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
FlagStatus ADC_GetSoftwareStartInjectedConvCmdStatus(ADC_TypeDef* ADCx);
void ADC_InjectedChannelConfig(ADC_TypeDef* ADCx, u8 ADC_Channel, u8 Rank, u8 ADC_SampleTime);
void ADC_InjectedSequencerLengthConfig(ADC_TypeDef* ADCx, u8 Length);
void ADC_SetInjectedOffset(ADC_TypeDef* ADCx, u8 ADC_InjectedChannel, u16 Offset);
u16 ADC_GetInjectedConversionValue(ADC_TypeDef* ADCx, u8 ADC_InjectedChannel);
void ADC_AnalogWatchdogCmd(ADC_TypeDef* ADCx, u32 ADC_AnalogWatchdog);
void ADC_AnalogWatchdogThresholdsConfig(ADC_TypeDef* ADCx, u16 HighThreshold, u16 LowThreshold);
void ADC_AnalogWatchdogSingleChannelConfig(ADC_TypeDef* ADCx, u8 ADC_Channel);
void ADC_TempSensorVrefintCmd(FunctionalState NewState);
FlagStatus ADC_GetFlagStatus(ADC_TypeDef* ADCx, u8 ADC_FLAG);
void ADC_ClearFlag(ADC_TypeDef* ADCx, u8 ADC_FLAG);
ITStatus ADC_GetITStatus(ADC_TypeDef* ADCx, u16 ADC_IT);
void ADC_ClearITPendingBit(ADC_TypeDef* ADCx, u16 ADC_IT);
@Виктор
Виктор, так дело не пойдет. Вы меня совсем не «слышите».
Начните с примеров которые идут в комплекте с либой.
Получите хоть какие то начальные навыки.
Спасибо, хорошая статья мне помогло быстро разобраться с АЦП.
Есть правда одно но. Точность измерения сильно зависит от сопротивления на входной ноге АЦП. Сумарное сопротивление между входом процессора и питанием не должно превыхать 3 ком. Это из аппноута. На моей практике это подтвердилось.
В схемке с резистивным делителем при использовании делителя более 3 ком (а у вас все 10ком да еще на входе 1 ком просто так висит еще), будет сильное привирание на напряжениях ниже 1-го вольта. По факту при напряжении 0,2 вольта цап будет показывать аж 0,4 а то и 0,5 вольта.
При повышении напряжения резистором сумарное сопротивление будет падать и значение с АЦП-а будет приближаться к действительному значению.
В вашем примере я посоветовал-бы использовать переменный резистор не более 2 ком и защитный резистор (если он вообще нужен) не более чем ом 200-300.
Как-то так.
При высоком входном сопротивлении надо уже что-то городить в программе и в схеме с кортексом. Вся эта проблема вылезла в полный рост когда я на вход АЦП я повесил LM35DT и при фактическом напряжении 0,25 вольт АЦП мне выдает 0,48. Что вообще ни в какие ворота не лезет.
Сейчас вот мудрю как правильно привязать ЛМ-ку к кортексу.
@Макс
Спасибо за отзыв.
Описывать нюансы аналого-цифрового преобразования в статье и не предполагалось.
Откуда вы взяли такое число 3 кОм ?
Входное сопротивление зависит от текущих настроек АЦП.
http://www.st.com/internet/com/TECHNICAL_RESOURCES/TECHNICAL_LITERATURE/DATASHEET/CD00251732.pdf
(стр. 68-69).
Для «увеличения» входного сопротивления можно поставить ФНЧ (на R, C).
Хорошо бы ещё фильтровать наводки от сети 220В 50Гц.
В примере я не использовал ФНЧ и 50Гц не фильтровал, но да же без этого при среднем положении переменного резистора показания равны 0x087D=2173, при этом напряжение на входе АЦП 1,57В, что теоретически должно дать
(1.576В / 2,99В)*4095 = 2158. Как видим разница всего лишь 15 единиц, т.е. около 11 мВ.
Вероятно вы что-то не так делаете.
Заменил переменный резистор на другой с номиналом 50 кОм, измерил при разных положениях:
при входном 0,162В показания 0x00F5=245, теоретически (0,162В / 2,99В)*4095 = 222, разница 23
при входном 0,369В показания 0x0249=585, теоретически (0,369В / 2,99В)*4095 = 505, разница 80
при входном 1,571В показания 0x087E=2174, теоретически (1,571В / 2,99В)*4095 = 2151, разница 23
при входном 2,20В показания 0x0B90=2960, теоретически (2,20В / 2,99В)*4095 = 3013, разница 53
К сожалению мультиметр простой и на диапазоне 20В всего два знака после запятой.
Просмотрел документацию на LM35DT, пришлите на мыло текущее подключение датчика.
@ZiB
при переменном резисторе 50 кОм.
(Хотя в изделии будут 3.3 во как разрабатывать на дискавери может быть весело) .
на стр.68 дана табличка с максимальным допустимым Rain. Исходя из этой таблички выходит что Rain зависит от времени цикла АЦП. У меня частота 24 Мгц. (У вас в примере вы использовали 8). Попробуйте поднять частоту до 24.
По итогам у меня выличилось установкой пред делителя на 8 т.е.
Вместо RCC->CFGR |= RCC_CFGR_ADCPRE_DIV2;
Я поставил RCC->CFGR |= RCC_CFGR_ADCPRE_DIV8;
и показания с резистора стали более менее правильные.
И с LM75 стало мерить что-то более менее вменяемое. Добавил еще на выход LM емкость 0,64 Mkf. Ну вроде теперь все работает.
Прикольно вот еще что. У меня платка такая же как и у вас. STM32VLDiscovery. Обратил внимание на ваши расчеты с Vreef=2,99. Полез с мультиметром. У неё действительно Vdd=2,99. Раньше я этого не замечал ну и Vref тоже соотвественоо не 3.3
@Макс
Ну вот видите, используете другие настройки, не проверили питание и говорите, что не работает.
В этом деле важна каждая мелочь.
Я вот не могу врубится что означает вот этот кусок РМ
Если я правильно понимаю бит ADON необходимо устанавливать дважды и перед вторым разом потупить для того, чтобы ацп стабилизировался, а потом делать калибровку?
@shurik303
Первая установка бита выводит АЦП из спячки, вторая и последующие запускают преобразования.
(если одновременно с этим битом изменишь другие, то преобразования не будет).
Следовательно, включаешь-ждешь-калибровка-преобразование.
В коде выше я не жду, почему, уже не могу сказать.
Типовое время выходя по документации равно нулю, максимальное 1 мкс.
@ZiB
Складывается такое впечатление что референс мануал писали разные люди — одни части написаны подробно, а другие в стиле «Чего тут рассказывать и так все ясно». Вот в этом случае как понять установить бит, потм сбросить и опять установить, сам то он не сбрасывается. И еще с калибровкой: при установленном ADON, CAL не устанавливается, то есть калибровку проводить при выключенном ацп? Просто я только начал разбираться с микроконтроллерами, отсюда и дурацкие вопросы.
@shurik303
Да, нет. Вроде нормально описано.
Ну четко же написано, да и я написал, записал один раз, потом второй и так далее, без сброса.
Как по твоему АЦП пройдет калибровку, если оно выключено ?
Может спутал, там описано, что нужно калибровать однократно, желательно сразу после включения.
Да, такие вопросы будут постоянно… )
Дык в отладчике смотрел — пошагово трассировал и бит не устанавливался. И вот из РМ:
@shurik303
Я думаю, в данном случае иметься ввиду, что перед калибровкой необходимо, что бы АЦП был выключен хотя бы в течении двух тактов.
Код приведенный статье, прекрасно работает.
Глянул бегло SPL, там аналогичная ситуация.
А, отладчикам я давно уже не доверяю…
а в STM32 есть специальные режимы энергопотребления для улучшенной роботы ацп, как у avr?
@drakon
Добрый день,
В принципе можно выключать ЦПУ, но если выполнить хорошо питание, то STMу этого не требуется у него работа ЦПУ не оказывает большого воздействия на результат преобразования.
Я как-то тестировал на своей плате, при тактовых от 24 до 72 МГц (частоту дискретизации не помню) результат преобразования принимал два значения N и N+1, т.е. например 2047, через некоторое время 2048, потом через некоторое время опять 2047 и т.д. по кругу.
Это без фильтрации и прочих ухищрений.
Друзья, не понимаю как привязать к АЦП тот или иной вывод. Вот как сказать контроллеру что данные надо читать с A1 линии,или D10, достаточно лишь инициализировать GPIO и выход A1 или D10 как аналоговых вход? не вижу в коде где вы указываете какую линию ввода использовать того или иного GPIO.
прямо этот код не могу пользовать так как платка у меня STM32F3, ваще не компилится, миллион ошибок так как регистры константы другие =( Но принцип то тот же все равно я полагаю.
void ADC_Init2(char port, char line){
GPIO_Init_(port,line,3,0,1,0);
RCC->AHBENR|=RCC_AHBENR_ADC12EN;
RCC->CFGR2 &=~RCC_CFGR2_ADCPRE12;
RCC->CFGR2 |= RCC_ADC12PLLCLK_Div6;
ADC1->CFGR|=0x2000; //регулярное преобразование
ADC1->CR|=ADC_CR_ADCAL; //начать калибровку
while (!(ADC1->CR & ADC_CR_ADCAL)){};
ADC1->CFGR|=ADC_CFGR_EXTSEL;
ADC1->CFGR|=ADC_CFGR_EXTEN;
ADC1->CFGR|=ADC_CFGR_CONT;
ADC1->SQR1=1;
ADC1->CR|=ADC_CR_ADEN; //включить АЦП
ADC1->CR|=ADC_CR_ADSTART;//запустить преобразование
}
ADC_Init2(‘A’,1); //инициализирую
потом в цикле читаю ADC1->DR; и если он выше 0 должен загоореться светодиод, но этого не происходит! =( следовательно значение этого регистра всегда 0 =((
я так понимаю ADC1->SQR1=1; значит что первый в списке регулярных каналов будет ADC1_IN1 который судя по таблице пинов является PA0.
потому я просто ставлю сопротивление между 3.3V и PA0 чтобы получить что-нибудь в регистре..
подскажи те чего не так делаю =)
@Alexey
Добрый день,
Алексей, это не форум и «спамить» тут не нужно.
Если у Вас есть какие-то вопросы не по статье или просто вопрос ко мне, лучше использовать форму обратной связи, в меню сайта «Задать вопрос».
Касательно вашего вопроса:
1) АЦП имеет встроенный мультиплексор, выходные линии которого «раскиданы» по разным линиями ввода-вывода. У разных мк коммутация реализована по разному, откройте документацию на выбранное вами семейство и прочитайте (или хотя бы просмотрите функциональные схемы) по модулю АЦП.
2) На начальной стадии (при отсутствии опыта) не стоит пытаться работать напрямую с регистрами, воспользуйтесь примерами и стандартной библиотекой. Это сэкономит вам массу времени.
@ZiB
Испытываю рвотные позывы от завернутых в структуры данных и функции которые хрен знают что делают >_<
ладно, поищу еще
@Алексей
Ну, а вот такие «ADC1->CFGR|=0×2000; //регулярное преобразование» конструкции вам понятны сходу ?
Если, хотите, чтобы вам помогли, предоставляйте больше информации о железе и желательно весь проект.
Потому, что ошибки могут быть где угодно и строить догадки на обрывках кода, да ещё и рыться в доке да бы понять, чего тут понаписал «автор» тоже не вариант…
Ну, а мне проще написать пример (некоторые примеры так и появились на сайте), так как это займет меньше времени.
К сожалению я сейчас занят, но если вы не решите свою проблему напишите мне, я попробую выкроить время.
И я не спец, а такой же любитель как и Вы.
«Ну, а вот такие «ADC1->CFGR|=0×2000; //регулярное преобразование» конструкции вам понятны сходу ?»
да абсолютно, привычно. на си то я давно кодю, и бинарные операции для моих глаз и мозга вполне дружественны =)
главное точно знаешь как фактически меняешь настройки в железке.
@Alexey
Я не на этом акцент хотел сделать, а на использовании числовых значений (0х2000), на мой взгляд оптимальнее использовать символьные представления:
ADC1->CFGR|=ADC_CFGR_CONT;
При этом становиться понятно (конечно, при наличии знаний и опыта), что это не «регулярное преобразование», а включение режима «непрерывного (циклического) преобразования» для группы каналов работающих в режиме регулярных преобразований.
Верно) регулярное преобразование, а не единоразовое, т.е. в цикле не прерывающиеся)
ну да, константы тоже можно использовать)
эх.. не суть.
на данный момент понять почему не работает я не смог.
А Вы имеете стм32ф3 ?
@Alexey
Все таки — непрерывное (циклическое преобразования), а не регулярное.
Регулярное подразумевает с заданным периодом.
Применительно к АЦП, у STM32 имеется два режима преобразований: регулярный и инжектированный.
В регулярном режиме, результат преобразования с одного (или нескольких) каналов помещается в единственный регистр, что требует при обработки нескольких каналов либо «самостоятельно» забирать данные по окончании преобразования, либо использовать ПДП для пересылки данных.
В режиме инжектирования, результат преобразования с одного или нескольких каналов распределяется по нескольких регистрам. Конкретное кол-во регистров нужно уточнять в документации.
Да, у меня есть палата STM32F3-Discovery и один мк STM32F373 (могу ошибаться), но я его ещё да же не распаковывал.
по моему ошибка в калибровке. может вместо
ADC1->CR2 |= ADC_CR2_CAL;
while ((ADC1->CR2 & ADC_CR2_RSTCAL) == ADC_CR2_CAL){}
нужно
ADC1->CR2 |= ADC_CR2_CAL;
while ((ADC1->CR2 & ADC_CR2_CAL) == ADC_CR2_CAL){}
??? или я ошибаюсь?
@Kvark85
Да, вы правы.
Господа, подскажите пожалуйста, кто знает. Флаг окончания преобразования для регулярных регистров. В документации написано, что он сбрасывается программно, либо аппаратно при чтении из регистра данных. В моем случае данные я сохраняю в память с использованием DMA. Сбрассывается ли этот флаг аппаратно при отправке запроса к DMA? Мне нужно это для того, чтобы знать, можно ли запускать следующее преобразование.
Исправите пожалуйста ссылку на пример. Спасибо
Поддерживаю.. очень хочется попробовать.
@юра
@Maxim
С радостью бы выложил, но к сожалению не смог найти данный пример
where is the download link? I can not find
@lcd connection to stm32f100rb
We are pleased to be published, but unfortunately I could not find this example
Lost when moving.
thank you. if you find it share with me ,please.