Микроконтроллеры

Информация о пользователе

Привет, Гость! Войдите или зарегистрируйтесь.


Вы здесь » Микроконтроллеры » Архив » STM32F103 ADC1 от TIM1_CC1+DMA


STM32F103 ADC1 от TIM1_CC1+DMA

Сообщений 1 страница 19 из 19

1

stm32f103c8t6
Поставил цель запустить преобразование ацп1 от сравнения 1 канала 1 таймера и потом передавать через DMA. Есть опыт запуска от 3 таймера по TIM3_TRGO на SPL.
Думаю заодно будут исходник для начинающих
Пока хочу запустить без DMA. Поделился исходником NewWriter,но возник вопрос не будет ли управление выводом PA8 от сравнения 1 канала 1 таймера ? Мне просто нужен запуск от таймера ацп,а вывод оставлю свободным.

Код:
#include "adc.h"


void adc_init(void)
{
	// АЦП у нас 1 канал,он на PA0 , запуск от внешнего тригера TRM1 1 канала
	
	RCC->AHBENR|=RCC_AHBENR_DMA1EN;
	RCC->APB2ENR|=RCC_APB2ENR_AFIOEN|RCC_APB2ENR_IOPAEN|RCC_APB2ENR_ADC1EN|RCC_APB2ENR_TIM1EN;
	
	// тактирование ацп 
	RCC->CFGR&=~(RCC_CFGR_ADCPRE);
	RCC->CFGR|=RCC_CFGR_ADCPRE_1;
	
	// аналоговый вход
	GPIOA->CRL&= ~(GPIO_CRL_MODE0);
	GPIOA->CRL&= ~(GPIO_CRL_CNF0); 
	
	// настройка ацп: 
	ADC1->SMPR2 |= ADC_SMPR2_SMP0_2 ;                 // AN0: 41.5 мкс 
	 ADC1->CR2 |= ADC_CR2_ADON;                                // включаем
	/* регулярная группа */
   ADC1->SQR3 = 1;                                 // 1 канал в первую группу
	
	// Калибровка ацп
      ADC1->CR2 |= ADC_CR2_CAL;                                  // запуск калибровки
     while (ADC1->CR2 & ADC_CR2_CAL);                         // ожидаем конца калибровки
	
	 
	 ADC1->CR2 |=ADC_CR2_EXTTRIG;                    // запуск от сравнения по TIM1_CC1
	 ADC1->CR2 &=~(ADC_CR2_EXTSEL);                  //  000 это по TIM1_CC1
	 
	 // дма передача
	 //ADC1->CR2 |=ADC_CR2_DMA;
	 
	 // прерывание от ацп1
    ADC1->SR&=~ADC_SR_EOC;   // очищаем флаг преобразования
	 ADC1->CR1|=ADC_CR1_EOCIE;  // включаем прерывание
	 NVIC_SetPriority(ADC1_2_IRQn,0);
	  NVIC_EnableIRQ(ADC1_2_IRQn);
    
    ////////////////////////////////////////////////////////////////
    // Настраиваем таймер 1 для ацп1
    ///////////////////////////////////////////////////////////////
    TIM1->PSC = (SystemCoreClock/1000)-1;  // 1 ms
    TIM1->ARR = 2000;  // 2 секунды период
    TIM1->CCR1 = 700;  // через 0.7 начать преобразование
    TIM1->CCMR1 |= TIM_CCMR1_OC1M;                // Ch1: PWM mode2
                TIM1->CCER |= TIM_CCER_CC1E;                      //<! Ch1 out enable
                TIM1->BDTR |= TIM_BDTR_MOE;                        //<! main out enable (!!!)
                TIM1->CR1 |= TIM_CR1_CEN;                              // TIM1 enable

               /* ---- АЦП запущен, с AN1 измеряется ---- */
    
}


Есть хорошая табличка.
http://s8.uploads.ru/t/tZ2G1.png

ацп работает от таймера. А вот почему на выходе PA8 нет,может порт не настроен,буду смотреть дальше.
http://s5.uploads.ru/t/jvEdc.png

Отредактировано CERGEI (2017-04-08 04:35:46)

2

Как я и говорил вывод надо переводить в альтернативную функцию,тогда он подключается к таймеру сравнения.

Вот весь код с DMA и TIM1_CC1
К тому же заметил,прерывание от АЦП не наступает когда работает DMA на АЦП.

adc.h

Код:
#ifndef INIT_ADC
#define INIT_ADC


#include "stm32f10x.h"

extern uint16_t buff[10];

void adc_init(void);

#endif

adc.c

Код:
#include "adc.h"


void adc_init(void)
{
	// АЦП у нас 1 канал,он на PA0 , запуск от внешнего тригера TRM1 1 канала
	
	RCC->AHBENR|=RCC_AHBENR_DMA1EN;
	RCC->APB2ENR|=RCC_APB2ENR_AFIOEN|RCC_APB2ENR_IOPAEN|RCC_APB2ENR_ADC1EN|RCC_APB2ENR_TIM1EN;
	
	// тактирование ацп 
	RCC->CFGR&=~(RCC_CFGR_ADCPRE);
	RCC->CFGR|=RCC_CFGR_ADCPRE_1;
	
	// аналоговый вход
	GPIOA->CRL&= ~(GPIO_CRL_MODE0);
	GPIOA->CRL&= ~(GPIO_CRL_CNF0); 
	 
	
	// Выход TIM1_CC1  = PA8
	//GPIOA->CRH&= ~(GPIO_CRH_MODE8);
	//GPIOA->CRH|=GPIO_CRH_MODE8;
	//GPIOA->CRH&= ~(GPIO_CRH_CNF8);
	//GPIOA->CRH|=GPIO_CRH_CNF8_1;
	
	// настройка ацп: 
	ADC1->SMPR2 |= ADC_SMPR2_SMP0_2 ;                 // AN0: 41.5 мкс 
	 ADC1->CR2 |= ADC_CR2_ADON;                                // включаем
	/* регулярная группа */
   ADC1->SQR3 = 1;                                 // 1 канал в первую группу
	
	// Калибровка ацп
      ADC1->CR2 |= ADC_CR2_CAL;                                  // запуск калибровки
     while (ADC1->CR2 & ADC_CR2_CAL);                         // ожидаем конца калибровки
	
	 
	 ADC1->CR2 |=ADC_CR2_EXTTRIG;                    // запуск от сравнения по TIM1_CC1
	 ADC1->CR2 &=~(ADC_CR2_EXTSEL);                  //  000 это по TIM1_CC1

  // настройка дма канала для ацп
	
	DMA1_Channel1->CPAR = (uint32_t)&(ADC1->DR); // откуда забираем
	DMA1_Channel1->CMAR = (uint32_t)buff; // буфер в RAM
	DMA1_Channel1->CCR &= ~DMA_CCR1_DIR; // указываем направление передачи из перефирии в память
	DMA1_Channel1->CNDTR = 0x0A; // передаем 10 значений
	DMA1_Channel1->CCR &= ~DMA_CCR1_PINC; // адрес перефирии не инкриминируем
	DMA1_Channel1->CCR |= DMA_CCR1_MINC; // а вот адрес в папмяти инкриминируем
	DMA1_Channel1->CCR |= DMA_CCR1_PSIZE_0; // размерность перефирии 16 бит
	DMA1_Channel1->CCR |= DMA_CCR1_MSIZE_0; // размерность памяти 16 бит
	DMA1_Channel1->CCR |= DMA_CCR1_PL; // приоритет высокий 
	DMA1_Channel1->CCR |= DMA_CCR1_CIRC; // циклический режим
	DMA1_Channel1->CCR |= DMA_CCR1_EN; // включаем 1 канал DMA
	
	 ADC1->CR2 |=ADC_CR2_DMA;
	 
	 // прерывание от ацп1
    ADC1->SR&=~ADC_SR_EOC;   // очищаем флаг преобразования
	 ADC1->CR1|=ADC_CR1_EOCIE;  // включаем прерывание
	 NVIC_SetPriority(ADC1_2_IRQn,0);
	  //NVIC_EnableIRQ(ADC1_2_IRQn);
    
    ////////////////////////////////////////////////////////////////
    // Настраиваем таймер 1 для ацп1
    ///////////////////////////////////////////////////////////////
    TIM1->PSC = (SystemCoreClock/1000)-1;  // 1 ms
    TIM1->ARR = 2000;  // 2 секунды период
    TIM1->CCR1 = 700;  // через 0.7 начать преобразование
    TIM1->CCMR1 |= TIM_CCMR1_OC1M;                // Ch1: PWM mode2
    TIM1->CCER |= TIM_CCER_CC1E;                      //<! Ch1 out enable
    TIM1->BDTR |= TIM_BDTR_MOE;                        //<! main out enable (!!!)
    TIM1->CR1 |= TIM_CR1_CEN;                              // TIM1 enable
    
}

http://sg.uploads.ru/t/pZMXb.png

Отредактировано CERGEI (2017-04-11 03:26:08)

3

Перенесём топик в STM32 ADC ?

4

Можно,оба варианта оставить. 1 сообщение-вариант это от таймера с прерыванием и 2 сообщение таймер+DMA

5

изучал тему ацп на F1 и нашел интересную инфу по L

на 17 канале АЦП1 находит внутреннее опорное напряжение .
калибруется на заводе при 3В и записывается в память

пункт 6.3.3 Datasheet

1.202 1.224 1.242 V

VREFINT_CAL Raw data acquired at
temperature of 30 °C, VDDA= 3 V
0x1FF8 0078-0x1FF8 0079

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

6

Встроенный ацп оставляет желать лучшего. Хочу попробовать ADS1115 : 12 бит точность,встроенный опорник,изменяемый коэффициент усиления и 4 входа или 2 дифференциальных.

7

ну это да...
в разработке что на работе тоже нужна точность и используем AD7686BRM с внешними опорниками .   Но для дома как то жаба душит за такие деньги ацп внешнее брать. вот и пытаюсь штатное заставить работать. Но насколько я понял - дело это не простое , особенно в корпусах где нет Vref-

8

А как насчет непрерывного преобразования через АЦП?
Почему-то у меня вот такой код:

Код:
static inline void adc_setup(){
    GPIOB->CRL |= CRL(0, CNF_ANALOG|MODE_INPUT);
    uint32_t ctr = 0;
    // Enable clocking
    RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
    RCC->CFGR &= ~(RCC_CFGR_ADCPRE);
    RCC->CFGR |= RCC_CFGR_ADCPRE_DIV8; // ADC clock = RCC / 8
    // wake up ADC
    ADC1->CR2 |= ADC_CR2_ADON;
    // sampling time - 239.5 cycles for channels 8, 16 and 17
    ADC1->SMPR2 = ADC_SMPR2_SMP8;
    ADC1->SMPR1 = ADC_SMPR1_SMP16 | ADC_SMPR1_SMP17;
    // we have three conversions in group -> ADC1->SQR1[L] = 2, order: 8->16->17
    ADC1->SQR3 = 8 | (16<<5) | (17<<10);
    ADC1->SQR1 = ADC_SQR1_L_1;
    // calibration
    ADC1->CR2 |= ADC_CR2_RSTCAL;
    while((ADC1->CR2 & ADC_CR2_RSTCAL) && ++ctr < 0xfffff);
    ADC1->CR2 |= ADC_CR2_CAL;
    ctr = 0; while((ADC1->CR2 & ADC_CR2_CAL) && ++ctr < 0xfffff);
    // DMA configuration
    RCC->AHBENR |= RCC_AHBENR_DMA1EN;
    DMA1_Channel1->CPAR = (uint32_t) (&(ADC1->DR));
    DMA1_Channel1->CMAR = (uint32_t)(ADC_array);
    DMA1_Channel1->CNDTR = NUMBER_OF_ADC_CHANNELS * 9;
    DMA1_Channel1->CCR |= DMA_CCR_MINC | DMA_CCR_MSIZE_0 | DMA_CCR_PSIZE_0
                          | DMA_CCR_CIRC | DMA_CCR_PL | DMA_CCR_EN;
    ADC1->CR1 |= ADC_CR1_SCAN; // scan mode
    // continuous mode & DMA; enable vref & Tsens; start
    ADC1->CR2 |= ADC_CR2_CONT | ADC_CR2_DMA | ADC_CR2_TSVREFE;
    // turn ON ADC
    ADC1->CR2 |= ADC_CR2_ADON;
    ADC1->CR2 |= ADC_CR2_SWSTART;
    DMA1_Channel1->CCR |= DMA_CCR_EN;
}

запускает АЦП (значения ADC1->DR меняются), но ПДП не работает! В принимающем массиве нули... Что за проблема может быть?

9

Вот же чертова магия! Чуть поменял порядок — и заработало! Ну и где про это в даташите написано?

Код:
static inline void adc_setup(){
    GPIOB->CRL |= CRL(0, CNF_ANALOG|MODE_INPUT);
    uint32_t ctr = 0;
    // Enable clocking
    RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
    RCC->CFGR &= ~(RCC_CFGR_ADCPRE);
    RCC->CFGR |= RCC_CFGR_ADCPRE_DIV8; // ADC clock = RCC / 8
    // sampling time - 239.5 cycles for channels 8, 16 and 17
    ADC1->SMPR2 = ADC_SMPR2_SMP8;
    ADC1->SMPR1 = ADC_SMPR1_SMP16 | ADC_SMPR1_SMP17;
    // we have three conversions in group -> ADC1->SQR1[L] = 2, order: 8->16->17
    ADC1->SQR3 = 8 | (16<<5) | (17<<10);
    ADC1->SQR1 = ADC_SQR1_L_1;
    ADC1->CR1 |= ADC_CR1_SCAN; // scan mode
    // DMA configuration
    RCC->AHBENR |= RCC_AHBENR_DMA1EN;
    DMA1_Channel1->CPAR = (uint32_t) (&(ADC1->DR));
    DMA1_Channel1->CMAR = (uint32_t)(ADC_array);
    DMA1_Channel1->CNDTR = NUMBER_OF_ADC_CHANNELS * 9;
    DMA1_Channel1->CCR |= DMA_CCR_MINC | DMA_CCR_MSIZE_0 | DMA_CCR_PSIZE_0
                          | DMA_CCR_CIRC | DMA_CCR_PL | DMA_CCR_EN;
    // continuous mode & DMA; enable vref & Tsens; wake up ADC
    ADC1->CR2 |= ADC_CR2_DMA | ADC_CR2_TSVREFE | ADC_CR2_CONT | ADC_CR2_ADON;
                // | ADC_CR2_EXTTRIG | ADC_CR2_EXTSEL | ADC_CR2_JEXTSEL;
    // calibration
    ADC1->CR2 |= ADC_CR2_RSTCAL;
    while((ADC1->CR2 & ADC_CR2_RSTCAL) && ++ctr < 0xfffff);
    ADC1->CR2 |= ADC_CR2_CAL;
    ctr = 0; while((ADC1->CR2 & ADC_CR2_CAL) && ++ctr < 0xfffff);
    // turn ON ADC
    ADC1->CR2 |= ADC_CR2_SWSTART;
    ADC1->CR2 |= ADC_CR2_ADON;
}

10

Eddy_Em написал(а):

Вот же чертова магия!

Удивляюсь как тебе ещё не надоела эта write-only тарабарская грамота )

11

vt написал(а):

write-only тарабарская грамота )

Ты о чем? В этой "грамоте" все понятно. В отличие от тарабарского калокуба, где вообще в исходниках черт ногу сломает! Да еще и косяков полно...

12

Eddy_Em написал(а):

Ты о чем? В этой "грамоте" все понятно.

Всё это забывается через неделю, если на таком же уровне въехать во что-то другое

13

vt написал(а):

Всё это забывается через неделю, если на таком же уровне въехать во что-то другое

но ведь без проблем вспоминается при необходимости... не?

14

RA написал(а):

но ведь без проблем вспоминается при необходимости... не?

С трудом. Много времени тратится на это, и потом, как обычно, с позиции нового опыта хочется все переписать с нуля заново, лучше и круче. Ну как обычно :)

15

MasterAlexei написал(а):

с позиции нового опыта хочется все переписать с нуля заново

Бывает :) Но это разве плохо? У меня времени на повторный въезд уходит не много. А вот на переписывание - много. Но ведь можно и без переписывания. Бери и пользуй что уже есть. Есть время - доводи.

Код:
float adc_vref(void)
{
uint32_t v = 0;
ADC1->CR2 |= ADC_CR2_TSVREFE;
ADC1->SQR3 |= 17;//ADC_SQR3_SQ1_0 | ADC_SQR3_SQ1_4;
ADC1->SMPR1 |= ADC_SMPR1_SMP17_0 | ADC_SMPR1_SMP17_1 ;
ADC1->CR2 |= ADC_CR2_ADON;
for (uint8_t i = 0; i < 10; i++)
	{
        ADC1->CR2 |= ADC_CR2_SWSTART;
        while ((ADC1->SR & ADC_SR_EOC) != ADC_SR_EOC){}
        v += ADC1->DR;
	}
ADC1->CR2 &= ~ADC_CR2_TSVREFE;
ADC1->CR2 &= ~ADC_CR2_ADON;
ADC1->SMPR1 &= ~(ADC_SMPR1_SMP17_0 | ADC_SMPR1_SMP17_1) ;
return  (float)(v)/10.0;//что-то не то!!!!!!!!!!!!!!!!!!!!
}

у меня чаще возникает вопрос как оно работало?

Отредактировано RA (2019-05-20 10:48:20)

16

RA написал(а):

но ведь без проблем вспоминается при необходимости... не?

Попробуй )
Детально разобраться с каким-то другим [не менее сложным] мк, а потом вернуться к stm32

17

vt написал(а):

Попробуй )
Детально разобраться с каким-то другим [не менее сложным] мк, а потом вернуться к stm32

ну с esp32 разбирался и на глубину регистров не лез. обратно вернулся - ну да... было прикольно. открываю то что писал пару месяцев назад, и на первый взгляд - "да ну на х., опять во всем этом разбираться", через 15 минут, все разложил по полочкам картина стала понятна, за исключением деталей, которые в общих чертах в мозгу уже всплыли... ну дальше дело техники...  :D все нах  в помойку и пишем по новой. шучу - беру старое, запускаю - довожу, переделываю.
да ладно бог с ним.
Вы/ты что предлагаете?

Отредактировано RA (2019-05-20 10:58:44)

18

RA написал(а):

ну с esp32 разбирался и на глубину регистров не лез

Это не то
Чтоб прочувствовать ппц нужен сопоставимый уровень детализации и сложности

RA написал(а):

Вы/ты что предлагаете?

Сводить всё к такому уровню, который не забывается
Не к и-фейсу с ацп stm32, а к и-фейсу с абстрактным ацп с интуитивно понятными параметрами и возможностями

Отредактировано vt (2019-05-20 11:44:42)

19

RA написал(а):

но ведь без проблем вспоминается при необходимости... не?

В любом случае, лучше иметь под рукой кусок рабочего кода, чем заново лезть в даташиты/мануалы и тратить пару дней на разбирательства с чем-нибудь простым!

vt написал(а):

Не к и-фейсу с ацп stm32, а к и-фейсу с абстрактным ацп с интуитивно понятными параметрами и возможностями

В случае микроконтроллеров такого не бывает! Это — фантастика! Невозможно написать оптимальную header-only библиотеку, которая будет работать со всеми микроконтроллерами!
Можно посмотреть, к чему приводит попытка это сделать. Взять тот же голимый SPL или калокуб. Хуже параши еще поискать... Тем более, что и в SPL, и в калокубе нарушен главный принцип библиотек для микроконтроллеров: они должны быть header-only! Никаких быдлофункций! Только макросы и static inline. Ну и, понятное дело, никаких ассертов и прочей ерунды там тоже быть не должно.
Когда в быдлокод от ST'шных китаез/индусов посмотришь, плакать хочется! Явно писали какие-то индусокодеры, которые до этого с микроконтроллерами дела не имели, а писали свой быдлокод только под компьютеры! Да еще и, небось, не под операционными системами, а под прошивками для игровых приставок...


Вы здесь » Микроконтроллеры » Архив » STM32F103 ADC1 от TIM1_CC1+DMA