3 / 3 / 0
Регистрация: 12.11.2018
Сообщений: 506
1

ADC режимы работы

21.06.2022, 14:19. Показов 873. Ответов 27

Есть необходимость в совершении нескольких измерений АЦП в различное время. Т.е. есть несколько не связанных между собой параметров, которые надо измерять по каким-либо событиям (прерывания, таймеры и тд).
Из того, что я изучил про АЦП, наиболее подходящим является использование очередность опроса - Rank. В целом можно продумать последовательность возникновения событий и настроить очередность опроса.

Однако, что если данные события, по которым надо производить измерение АЦП, будут происходить с разной частотой?
Допустим, настроил я очередность опроса АЦП параметров 1, 2, 3. Где по возникновению 1го события будет измерен параметр 1, 2го события - параметр 2 и тд. Но в процессе работы кода может произойти так, что событие 2 произойдет раньше события 1 и данные будут записаны не правильно. Либо же событие 2 успеет произойти несколько раз, прежде чем будет событие 3.

Иначе говоря, как сделать так, чтобы можно было запускать конкретный канал АЦП в нужное время не зависимо от других каналов?
__________________
Помощь в написании контрольных, курсовых и дипломных работ, диссертаций здесь
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
21.06.2022, 14:19
Ответы с готовыми решениями:

STM32F4 ADC режимы работы. Помогите разобраться
Здравствуйте! Я начал разбираться с АЦП на STM32F4Dyscovery. Вычитал, что у АЦП существуют два...

ADC режимы Continuous conversion и Scan
Изучаю RM0090 Reference manual. Не могу уловить разницу между режимами Continuous conversion и...

Режимы работы по кнопке (STM32F3Discovery kit)
Всем привет! Только начал изучать STM32F3Discovery kit, научился мигать диодами и даже создавать...

Режимы индикации или разные режимы работы светодиода
Добрый день! Помогите пожалуйста с задачей. Микроконтроллер attiny13a Необходимо реализовать...

режимы работы в БД
Всем привет. Требуется хранить в MySQL графики работы предприятий, т.е. время открытия и время...

27
2782 / 2020 / 451
Регистрация: 11.09.2009
Сообщений: 7,516
21.06.2022, 16:07 2
Цитата Сообщение от DmitryDDDD Посмотреть сообщение
как сделать так, чтобы ...
Держите в голове, что АЦП в контроллере на самом деле всего один, а "каналы" - это всего лишь аналоговый мультиплексор на его входе. Чтобы запустить измерение, нужно выбрать канал и запустить АЦП (перед этим настроенный, возможно один раз в начале). Тогда вся логическая цепочка у вас в голове легко выстроится.

Цитата Сообщение от DmitryDDDD Посмотреть сообщение
что если данные события, по которым надо производить измерение АЦП, будут происходить с разной частотой?
Или каждый раз задерживать программу на время измерения, или можно запустить работу АЦП в цикле, измеряя последовательно по всем имеющимся каналам с каким-то периодом, в прерывании по таймеру. Результаты рассовываются в отдельные переменные для каждого канала. Когда придёт какое-то событие (неважно когда и в какой последовательности), оно заберёт из своей переменной последнее сделанное измерение своего канала. То, что измерение получено с какой-то задержкой (и даже меняющейся), в большинстве случаев неважно. Если результат занимает несколько ячеек памяти, то на время чтения надо запрещать прерывания, чтобы не прочитать одно слово от предыдущего отсчёта, а второе - от следующего.
А когда точное знание задержки измерения важно, требуется минимальное, а каналов куча, то на каждый такой канал ставится свой АЦП. Это уже не системы на одном контроллере.
0
194 / 149 / 34
Регистрация: 15.11.2012
Сообщений: 741
22.06.2022, 02:31 3
При этом если источники данных (сами аналоговые сигналы) у вас высокоимпедансные, будут сюрпризы - результат текущего измерения будет (немного, но систематически) зависеть от значения сигнала при предыдущем измерении (в другом канале). Это связано с работой самого АЦП, а именно с тем, что на входе у него стоит небольшая емкость, которая в некоторой степени сохраняет предыдущее значение сигнала. И когда вы этот вход (и конденсатор на нем) подключаете к другому входу - напряжение там станет другим.
Здесь есть детальный разбор ситуации: http://www.leoniv.diod.club/ar... stm32.html
0
2782 / 2020 / 451
Регистрация: 11.09.2009
Сообщений: 7,516
22.06.2022, 02:52 4
alexey6689, такое может происходить, если не следовать рекомендациям даташита по настройкам АЦП. При правильном применении ничего подобного не наблюдается.
0
3 / 3 / 0
Регистрация: 12.11.2018
Сообщений: 506
22.06.2022, 06:09  [ТС] 5
i8085, про мультиплексор я понимаю, мне не совсем понятно почему я не могу задать работу конкретного канала.
Вот в HAL самое обычное измерение:

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
    if(hadc->Instance == ADC3)
    {
        Data = HAL_ADC_GetValue(&hadc3);
    }
}
 
void TIM2_Callback(void)
{
    if(LL_TIM_IsActiveFlag_UPDATE(TIM5))
    {
        HAL_ADC_Start_IT(&hadc3);
    }
    LL_TIM_ClearFlag_UPDATE(TIM5);
}
Может, конечно, это проблема в HAL, нигде не выбирается какой канал будем оцифровывать, что Data это данные 9го канала и тд. Может на CMSIS есть возможность именно перед измерением выставить мультиплексор на нужный канал и считать данные, измеренные АЦП на этом канале, в нужную переменную?

Нужен код, примерно с такой идеей:

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
    if(hadc->Instance == ADC3)
    {
        if(CHANNEL == 9)
        {
               Data = HAL_ADC_GetValue(&hadc3);
        }     
    }
}
 
void TIM2_Callback(void)
{
    if(LL_TIM_IsActiveFlag_UPDATE(TIM5))
    {
        HAL_ADC_Start_IT(&hadc3, CHANNEL_9);
    }
    LL_TIM_ClearFlag_UPDATE(TIM5);
}
0
Модератор
Эксперт по электронике
8657 / 6463 / 871
Регистрация: 14.02.2011
Сообщений: 22,572
22.06.2022, 11:31 6
Цитата Сообщение от DmitryDDDD Посмотреть сообщение
Может, конечно, это проблема в HAL, нигде не выбирается какой канал будем оцифровывать,
посмотри на инициализацию ADC(увидишь выбранные каналы) и прочитай про него, и узнай что такое регулярные и инжекторные каналы
0
2782 / 2020 / 451
Регистрация: 11.09.2009
Сообщений: 7,516
22.06.2022, 15:16 7
Цитата Сообщение от DmitryDDDD Посмотреть сообщение
Вот в HAL ...
Ну так и задайте вопрос в форуме про хал. Первый ваш вопрос был вполне понятен и к месту. А это уже второй вопрос, в общем-то не имеющий отношения к первому. Мало ли кто чего в какой библиотеке написал... У него и спрашивайте.
0
Нарушитель
196 / 135 / 26
Регистрация: 14.02.2013
Сообщений: 975
22.06.2022, 15:39 8
Что hal что cmsis все равно нужно и придется сваливать выбранные регулярные каналы в один массив а потом в основном цикле программы его разгребать. Но есть четыре не зависимых регистра инжектированых каналов которые можно спокойно дожить каждый в отдельную переменную и ещё один регулярный, и того получитс 5 каналов.
0
3 / 3 / 0
Регистрация: 12.11.2018
Сообщений: 506
23.06.2022, 10:53  [ТС] 9
VladimirU,
Пока только такое решение придумал:
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//1 участок кода (прерывание, таймер и тд)
HAL_ADCEx_InjectedStart(&hadc3);
case = 1;
 
//2 участок кода (прерывание, таймер и тд)
HAL_ADCEx_InjectedStart(&hadc3);
case = 2;
 
//3 участок кода (прерывание, таймер и тд)
HAL_ADCEx_InjectedStart(&hadc3);
case = 3;
 
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
    if(hadc->Instance == ADC3)
    {
        switch 
        {
          case '1':
          adc1 = HAL_ADCEx_InjectedGetValue(&hadc3, ADC_INJECTED_RANK_1);
          break;
 
          case '2':
          adc2 = HAL_ADCEx_InjectedGetValue(&hadc3, ADC_INJECTED_RANK_2);
          break;
 
          case '3':
          adc3 = HAL_ADCEx_InjectedGetValue(&hadc3, ADC_INJECTED_RANK_3);
          break;
        }
    }
}
Не знаю на сколько такое решение можно считать правильным и оптимальным, но, я думаю, оно должно дать нужный результат
0
2894 / 1401 / 171
Регистрация: 28.10.2011
Сообщений: 5,184
Записей в блоге: 6
23.06.2022, 21:51 10
Какой-то странный код и компилироваться не будет. С switch и case напутали.
0
3 / 3 / 0
Регистрация: 12.11.2018
Сообщений: 506
24.06.2022, 06:42  [ТС] 11
locm,
По логике вот этот код должен нормально работать, не совсем понимаю, почему он показался Вам странным:
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/* Вызывается работа АЦП из разных участков кода: прерывания, циклы, таймеры и тд */
//1 участок кода (прерывание, таймер и тд)
AdcState= 1;
HAL_ADCEx_InjectedStart(&hadc3);
 
//2 участок кода (прерывание, таймер и тд)
AdcState= 2;
HAL_ADCEx_InjectedStart(&hadc3);
 
//3 участок кода (прерывание, таймер и тд)
AdcState= 3;
HAL_ADCEx_InjectedStart(&hadc3);
 
 
void HAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef* hadc)
{
    if(hadc->Instance == ADC3)
    {
        switch (AdcState)
        {
            case 1:
                adc1= HAL_ADCEx_InjectedGetValue(&hadc3, ADC_INJECTED_RANK_1);
 
                break;
 
            case 2:
                adc2= HAL_ADCEx_InjectedGetValue(&hadc3, ADC_INJECTED_RANK_2);
                break;
 
            case 3:
                adc3= HAL_ADCEx_InjectedGetValue(&hadc3, ADC_INJECTED_RANK_3);
                break;
 
            default:
                __NOP();
 
                HAL_ADCEx_InjectedStop_IT(&hadc3);
        }
    }
}
Но происходит измерение только 1го case. Буду благодарен, если подскажите, почему так.
Ну а чтобы пока работало, как вариант, это по каждому вызову HAL_ADCEx_InjectedStart делать все 3 измерения, а уже в case разгребать результат

Добавлено через 5 минут
Не, так тоже не работает, почему-то выполняется только первый case, в остальные не попадает
0
2894 / 1401 / 171
Регистрация: 28.10.2011
Сообщений: 5,184
Записей в блоге: 6
24.06.2022, 10:33 12
Цитата Сообщение от DmitryDDDD Посмотреть сообщение
не совсем понимаю, почему он показался Вам странным
Потому что нарушает синтаксис Си и компилироваться не будет.
0
3 / 3 / 0
Регистрация: 12.11.2018
Сообщений: 506
24.06.2022, 10:36  [ТС] 13
locm, Можете показать ошибку? Я не пойму, так как проект компилируется без проблем, но работает некорректно
0
2894 / 1401 / 171
Регистрация: 28.10.2011
Сообщений: 5,184
Записей в блоге: 6
24.06.2022, 10:43 14
Цитата Сообщение от DmitryDDDD Посмотреть сообщение
Можете показать ошибку?
Переменная AdcState объявлена в глобальной области с квалификатором volatile?
Ставьте точки останова на запись в переменную и убедитесь что участки кода выполняются.
0
3 / 3 / 0
Регистрация: 12.11.2018
Сообщений: 506
24.06.2022, 11:52  [ТС] 15
locm,
В глобальной области
static unsigned char AdcState = 0;

Добавлено через 50 минут
Насколько я в итоге понял, проблема была в другом: происходило наложение вызовов АЦП. Т.е. оно еще не успев оцифровать первый сигнал, прерывалось и перезапускалось вторым. Помогло увеличение таймингов между вызовами
0
Нарушитель
196 / 135 / 26
Регистрация: 14.02.2013
Сообщений: 975
24.06.2022, 13:29 16
C++ (Qt)
1
2
3
4
5
6
7
8
9
10
void HAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef* hadc)
{
    if(hadc->Instance == ADC3)
    {
                adc1= HAL_ADCEx_InjectedGetValue(&hadc3, ADC_INJECTED_RANK_1);
                adc2= HAL_ADCEx_InjectedGetValue(&hadc3, ADC_INJECTED_RANK_2);
                adc3= HAL_ADCEx_InjectedGetValue(&hadc3, ADC_INJECTED_RANK_3);
                HAL_ADCEx_InjectedStop_IT(&hadc3);
        }
    }
Зачем в прерывании устраивать калоборот из циклов? Сделайте вот так а результаты разгребайте в основном цикле.

Добавлено через 11 минут

C++ (Qt)
1
2
3
4
5
6
7
8
9
10
11
12
13
uint8_t cout_inj=0;
uint16_t adc[3];
 
void HAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef* hadc)
{
    if(hadc->Instance == ADC3)
    {
cout_inj++
                adc[cout_inj]= HAL_ADCEx_InjectedGetValue(&hadc3, cout_inj);
if(cout_inj>2)
                HAL_ADCEx_InjectedStop_IT(&hadc3);
        }
    }
Можно вот так сделать.
0
3 / 3 / 0
Регистрация: 12.11.2018
Сообщений: 506
24.06.2022, 14:00  [ТС] 17
VladimirU,
Цитата Сообщение от VladimirU Посмотреть сообщение
Зачем в прерывании устраивать калоборот из циклов?
Так в этом и была суть вопроса...
Важное замечание: напряжение, которое надо измерять не всегда присутствует на линиях, мы не можем в любое время его померить, а должны измерять в определенные моменты времени.

Допустим у нас есть 3 таймера, в прерывании которых АЦП измеряет 3 величины с каких-то 3х датчиков.
Если сделать "жесткую" логику, где мы будем знать, что таймер 1 срабатывает каждые x1 микросекунд, таймер 2 - x2 микросекунд и таймер 3 - x3 микросекунд.
То легко можно сделать так
Цитата Сообщение от VladimirU Посмотреть сообщение
void HAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef* hadc)
{
    if(hadc->Instance == ADC3)
    {
                adc1= HAL_ADCEx_InjectedGetValue(&hadc3, ADC_INJECTED_RANK_1);
                adc2= HAL_ADCEx_InjectedGetValue(&hadc3, ADC_INJECTED_RANK_2);
                adc3= HAL_ADCEx_InjectedGetValue(&hadc3, ADC_INJECTED_RANK_3);
                HAL_ADCEx_InjectedStop_IT(&hadc3);
        }
    }
А где-то в другом месте разгрести результат.

Однако ситуация меняется, когда время срабатывания таймеров в процессе работы кода меняются. Такое тоже может быть, наступили какие-то условия, и таймер 2 стал срабатывать раньше таймера 1.

Так вот, АЦП запускается в 3х местах в коде, но мы не знаем в конкретный момент кем было запущено АЦП из-за условия выше, про смету времени работы таймеров.
Как я написал выше, измеряемое напряжение находится на линии конкретное время, а не всегда, поэтому при каждом измерении 3х каналов АЦП на 2х из них будет 0, а на одном нужное измерение. Вот мне и надо как-то связать коллбэк АЦП с местом, откуда было запущено АЦП для того, чтобы знать, какая переменная хранит в себе результат измерений
0
Нарушитель
196 / 135 / 26
Регистрация: 14.02.2013
Сообщений: 975
24.06.2022, 14:45 18
C++ (Qt)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
void HAL_ADCEx_InjectedConvCpltCallback(ADC_HandleTypeDef* hadc)
{
    if(hadc->Instance == ADC3)
    {
if(HAL_ADCEx_InjectedGetValue(&hadc3, ADC_INJECTED_RANK_1) !=adc1)
adc1 = HAL_ADCEx_InjectedGetValue(&hadc3, ADC_INJECTED_RANK_1);
 
else if(HAL_ADCEx_InjectedGetValue(&hadc3, ADC_INJECTED_RANK_2) !=adc2)
adc2 = HAL_ADCEx_InjectedGetValue(&hadc3, ADC_INJECTED_RANK_2);
 
else if(HAL_ADCEx_InjectedGetValue(&hadc3, ADC_INJECTED_RANK_3) !=adc3)
adc3 = HAL_ADCEx_InjectedGetValue(&hadc3, ADC_INJECTED_RANK_3);
    }
    }
Таким образом новое значение АЦП будет присвоено переменным ADCx только тогда когда произойдёт изменение на входах АЦП. То есть прерывание будет возникать когда на одном из любого канала произошло изменение уровня но данные запишутся только в одну нужную переменную.

Добавлено через 26 минут
Да и юзать в прерывании целую функцию для того что бы лишь прочитать значения из трёх регистров как то не по феншёю.
Думаю будет разумнее на прямую обращаться к регистрам данных.
C++ (Qt)
1
2
3
4
5
6
7
8
if(ADC3->JDR1 != adc1)
adc1 = ADC3->JDR1;
 
else if(ADC3->JDR2 != adc2)
adc2 = ADC3->JDR2;
 
else if(ADC3->JDR3 != adc3)
adc2 = ADC3->JDR3;
1
2894 / 1401 / 171
Регистрация: 28.10.2011
Сообщений: 5,184
Записей в блоге: 6
24.06.2022, 17:41 19
Цитата Сообщение от VladimirU Посмотреть сообщение
Таким образом новое значение АЦП будет присвоено переменным ADCx только тогда когда произойдёт изменение на входах АЦП.
Оно может измениться из-за шума, наводок и как написал DmitryDDDD, измеряемое напряжение присутствует не всегда, а в определенные моменты.
1
3 / 3 / 0
Регистрация: 12.11.2018
Сообщений: 506
24.06.2022, 18:04  [ТС] 20
Цитата Сообщение от VladimirU Посмотреть сообщение
Думаю будет разумнее на прямую обращаться к регистрам данных.
Благодарю, действительно так будет лучше.
Цитата Сообщение от VladimirU Посмотреть сообщение
Таким образом новое значение АЦП будет присвоено переменным ADCx только тогда когда произойдёт изменение на входах АЦП
Вашу идею я понял, рациональное зерно в ней есть, но locm, правильно подметил про шумы и присутствие в определенные моменты
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
24.06.2022, 18:04
Помогаю со студенческими работами здесь

Режимы работы
Помогите, пожалуйста, понять пункт 4, что за режимы работы? "На вход подается", то есть просто...

РЕжимы работы БТ
вот читал книгу юный радиолюбитель - но там про режимы только вродь ключевой - это когда вообще...

Режимы работы
какие возможны режимы работы apache?

Режимы работы транзистора
Всем привет, хотелось бы уточнить у знающих людей работу транзистора в разных режимах. Хочу...

Режимы работы звезды
Здравствуйте, уважаемые электрики! Прошу помочь самоучке разобраться в кое-каких вопросах....

Режимы работы PHP
Режимы работы PHP(1.npm module;2.FastCGI;3.CGI;4.Apache module;5.UNIX application)


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2022, CyberForum.ru