Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: STM32 АЦП. Пожалуйста помогите разобраться
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
TmYAG
Я начал разбираться с АЦП на STM32F4Discovery.
Вычитал, что у АЦП существуют два метода опроса: регулярные каналы и инжектированные каналы.
Разницу между ними я более менее понял. Первая группа запись данных в один регистр, вторая группа результат можно записать в один из 4-х регистров.
Я стараюсь писать код используя CMSIS.
К порту PA1 прикрутил переменный резистор, и по изменению значения должны зажигаться диоды на PD12, 13, 14, 15 но почему-то не работает, подскажите пожалуйста что не так.
Код:
CODE
#include "stm32f4xx.h"
uint32_t LED_STATE = 0xF000;
uint32_t res;
int main()
{
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN|RCC_AHB1ENR_GPIODEN;
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
//GPIOA->MODER |= 0x0000000C;
GPIOD->MODER |= 0x55000000;
GPIOD->OTYPER |= 0;
GPIOD->OSPEEDR |= 0;
ADC1->SMPR2 |= (ADC_SMPR2_SMP1_2 | ADC_SMPR2_SMP1_1 | ADC_SMPR2_SMP1_0); //Задаем время выборки
ADC1->CR2 |= ADC_CR2_JEXTSEL; //Преобразование инжектированной группы
ADC1->CR2 |= ADC_CR2_JEXTEN; //Разрешаем внешний запуск инжектированной группы
ADC1->CR2 |= ADC_CR2_CONT; //Преобразования запускаются одно за другим
ADC1->CR1 |= ADC_CR1_JAUTO;
ADC1->JSQR |= (1<<15); //Задаем номер канала - ADC1
ADC1->CR2 |= ADC_CR2_ADON;//Теперь включаем АЦП
ADC1->CR2 |= ADC_CR2_JSWSTART; //Запуск преобразований
while (!(ADC1->SR & ADC_SR_JEOC)); //ждем пока первое преобразование завершится
while(1)
{

res = ADC1->JDR1;
if (res > 1024)
{
GPIOD->ODR=LED_STATE;
}
}
}

Поскольку я разбираюсь в режимах преобразования, то я так же пробовал написать код и для регулярных каналов:
Код:
CODE
#include "stm32f4xx.h"
uint32_t LED_STATE = 0xF000;
int main()
{
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN|RCC_AHB1ENR_GPIODEN;
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
GPIOA->MODER |= 0x0000000C;
GPIOD->MODER |= 0x55000000;
GPIOD->OTYPER |= 0;
GPIOD->OSPEEDR |= 0;

ADC1->SMPR2 |= (ADC_SMPR2_SMP1_2 | ADC_SMPR2_SMP1_1 | ADC_SMPR2_SMP1_0);
ADC1->CR2 |= ADC_CR2_EXTSEL;
ADC1->CR2 |= ADC_CR2_EXTEN;
ADC1->CR2 |= ADC_CR2_CONT;
ADC1->SQR1 |= (1<<15); //Задаем номер канала - ADC1
ADC1->CR2 |= ADC_CR2_ADON;//Теперь включаем АЦП
ADC1->CR2 |= ADC_CR2_SWSTART; //Запуск преобразований
while (!(ADC1->SR & ADC_SR_EOC)); //ждем пока первое преобразование завершится
uint32_t res;
while(1)
{
res = ADC1->DR;
if (res > 1024)
{
GPIOD->ODR=LED_STATE;
}
}
}

Но он тоже не работает(((
Если честно то я ковырял пример отсюда: http://blog.radiotech.kz/blog/52.html
И еще ну совсем нубский вопрос. Не могу понять связь между каналом АЦП и пином с которого я хочу снять сигнал. Объясните пожалуйста.
Мануал читал. Из него я понял, что на некоторых пинах висит АЦП, так кажется на PA1 висит ADC1, а что с каналами?
Спасибо!
vovanse
Цитата(TmYAG @ Dec 23 2014, 12:25) *
Мануал читал. Из него я понял, что на некоторых пинах висит АЦП, так кажется на PA1 висит ADC1, а что с каналами?


Так там же и написано, какие каналы. Например "ADC123_IN1". Канал 1 для АЦП 1,2 и 3. Или "АDC12_IN7" - канал 7 для АЦП 1 и 2.
glags
Цитата(TmYAG @ Dec 23 2014, 07:25) *
Я начал разбираться с АЦП на STM32F4Discovery.


Во первых в первой проге заремарен GPIOA->MODER |= 0x0000000C;
во вторых RCC->APB2ENR |= RCC_APB2ENR_ADC1EN | RCC_APB2ENR_SYSCFGEN; надо ещё включить тактирование на SYSCFG
в третьих перед каждым забором данных из АЦП его надо запускать как я написал ниже. sm.gif

ADC1->CR2 |= ADC_CR2_JSWSTART;
while (!(ADC1->SR & ADC_SR_JEOC));
ADC1->SR =0; // Очищаем флаг «преобразование завершено»
res = ADC1->JDR1;
TmYAG
Цитата(glags @ Dec 23 2014, 12:15) *
Во первых в первой проге заремарен GPIOA->MODER |= 0x0000000C;


А я пытался сконфигурировать PA1 как альтернативную функцию.

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

ADC1->CR2 |= ADC_CR2_JSWSTART;
while (!(ADC1->SR & ADC_SR_JEOC));
ADC1->SR =0; // Очищаем флаг «преобразование завершено»
res = ADC1->JDR1;

Изменил код, но проблема не исчезла=(
glags
Цитата(TmYAG @ Dec 23 2014, 20:02) *
А я пытался сконфигурировать PA1 как альтернативную функцию.


Изменил код, но проблема не исчезла=(

Полностью рабочий код инициализации ЦАП:
ADC1->CR2 |= ADC_CR2_CONT; //Преобразования запускаются одно за другим
ADC1->CR1 |= ADC_CR1_JAUTO | ADC_CR1_SCAN; //Разрешить автопреобразование инжектированной группы и SCAN означает брать номера каналов для сканирования из JSQR.
ADC1->JSQR |= (ADC_JSQR_JSQ4_0 ); //Задаем номер канала (выбран ADC1)
ADC1->CR2 |= ADC_CR2_ADON;//Теперь включаем АЦП

Здесь получаем данные:
ADC1->CR2 |= ADC_CR2_JSWSTART;
while (!(ADC1->SR & ADC_SR_JEOC));
ADC1->SR =0; // Очищаем флаг «преобразование завершено»
res = ADC1->JDR1;
TmYAG
Цитата(glags @ Dec 23 2014, 19:46) *
Полностью рабочий код инициализации ЦАП:
ADC1->CR2 |= ADC_CR2_CONT; //Преобразования запускаются одно за другим
ADC1->CR1 |= ADC_CR1_JAUTO | ADC_CR1_SCAN; //Разрешить автопреобразование инжектированной группы и SCAN означает брать номера каналов для сканирования из JSQR.
ADC1->JSQR |= (ADC_JSQR_JSQ4_0 ); //Задаем номер канала (выбран ADC1)
ADC1->CR2 |= ADC_CR2_ADON;//Теперь включаем АЦП

Здесь получаем данные:
ADC1->CR2 |= ADC_CR2_JSWSTART;
while (!(ADC1->SR & ADC_SR_JEOC));
ADC1->SR =0; // Очищаем флаг «преобразование завершено»
res = ADC1->JDR1;

Спасибо, большое, завтра попробую в камне.
А что насчет регулярных каналов? По идее те же яйца только в профиль.
Код
ADC1->CR2 |= ADC_CR2_CONT; //Преобразования запускаются одно за другим
    ADC1->CR1 |= ADC_CR1_AUTO | ADC_CR1_SCAN; //Разрешить автопреобразование инжектированной группы и SCAN означает брать номера каналов для сканирования из JSQR.
    ADC1->JSQR |= (ADC_SQR_SQ4_0 ); //Задаем номер канала (выбран ADC1)
    ADC1->CR2 |= ADC_CR2_ADON;//Теперь включаем АЦП

Здесь получаем данные:
ADC1->CR2 |= ADC_CR2_SWSTART;
while (!(ADC1->SR & ADC_SR_EOC));
ADC1->SR =0; // Очищаем флаг «преобразование завершено»
res = ADC1->DR;
Наверно так, да?
И что на счет конфигурации пина PA1? надо его на альтернативную функцию настраивать? Ведь SYSCFG мы для тактирования альтернативных функций и включали.
glags
Там целая наука. Дело в том что инжектированных каналов может быть максимум 4 и каждый имеет свой регистр данных, а регулярных 16 и у них один регистр данных на всех. По этому данные из них можно читать либо по прерыванию (окончание преобразования) либо DMA. Здесь если всё писать то целая статья выйдет. Дело в том что АЦП не сложный, но навороченный. Например он может вызывать прерывания при достижении определённого уровня сигнала на входе и т.д. Скажу только про один момент с инжектированными каналами. В инжектированных каналах есть одно неудобство, когда конфигуришь один канал, то в регистр ADC1->JSQR надо заполнять его как четвёртый. Это многих сбивает с толку.
Да SYSCFG мы для тактирования альтернативных функций и включали. Но для конфигурации порта мы их не используем. Просто в модере включаем аналоговый режим. Альтернативные функции касаются таймеров уартсов и прочего что конфигурится в GPIOA->AFR. Здесь на всякий случай sm.gif.
Просьба, если всё запашет, то отпишитесь.
TmYAG
Цитата(glags @ Dec 23 2014, 20:31) *
Просьба, если всё запашет, то отпишитесь.

CODE
#include "stm32f4xx.h"
uint32_t LED_STATE = 0xF000;
uint32_t res;
int main()
{
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN|RCC_AHB1ENR_GPIODEN;
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN|RCC_APB2ENR_SYSCFGEN;
GPIOA->MODER |= 0x0000000C;
GPIOD->MODER |= 0x55000000;
GPIOD->OTYPER |= 0;
GPIOD->OSPEEDR |= 0;
ADC1->SMPR2 |= (ADC_SMPR2_SMP1_2 | ADC_SMPR2_SMP1_1 | ADC_SMPR2_SMP1_0); //Задаем время выборки
ADC1->CR2 |= ADC_CR2_JEXTSEL; //Преобразование инжектированной группы
ADC1->CR2 |= ADC_CR2_JEXTEN; //Разрешаем внешний запуск инжектированной группы
ADC1->CR2 |= ADC_CR2_CONT; //Преобразования запускаются одно за другим
ADC1->CR1 |= ADC_CR1_JAUTO;
//ADC1->CR1 |= 0x00000020;
ADC1->JSQR |= ADC_JSQR_JSQ4_0; //Задаем номер канала - ADC1
ADC1->CR2 |= ADC_CR2_ADON;//Теперь включаем АЦП
while(1)
{
ADC1->CR2 |= ADC_CR2_JSWSTART; //Запуск преобразований
while (!(ADC1->SR & ADC_SR_JEOC)); //ждем пока первое преобразование завершится
ADC1->SR = 0;
res = ADC1->JDR1;
if (res > 1024)
{
GPIOD->ODR = LED_STATE;
}
else
{
GPIOD->ODR ^= LED_STATE;
}
}
}

Ну эта бредятина работает в режиме отладки. Проблема была в JSQR.
С регулярными каналами пока бьюсь. Потом планирую перейти на прерывания по EOC
Вот не могу понять связь между JSQR и PA1 по какому принципу я должен выставлять биты в регистре, чтобы читать именно из этого пина?

Цитата(TmYAG @ Dec 25 2014, 08:37) *
Ну эта бредятина работает в режиме отладки. Проблема была в JSQR.

И не только)
Код
GPIOD->ODR ^= LED_STATE;
- неправильно) У меня всегда диоды будут вкл)))
В общем инжектированные каналы работают) буду ковырять регулярные и прерывания затем.

Цитата(TmYAG @ Dec 25 2014, 09:03) *
буду ковырять регулярные и прерывания затем.


CODE
#include "stm32f4xx.h"
uint32_t LED_STATE = 0xF000;
uint32_t res;
int main()
{
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN|RCC_AHB1ENR_GPIODEN;
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN|RCC_APB2ENR_SYSCFGEN;
GPIOA->MODER |= 0x0000000C;
GPIOD->MODER |= 0x55000000;
GPIOD->OTYPER |= 0;
GPIOD->OSPEEDR |= 0;
ADC1->SMPR2 |= (ADC_SMPR2_SMP1_2 | ADC_SMPR2_SMP1_1 | ADC_SMPR2_SMP1_0); //Задаем время выборки
ADC1->CR2 |= ADC_CR2_EXTSEL; //Преобразование инжектированной группы
ADC1->CR2 |= ADC_CR2_EXTEN; //Разрешаем внешний запуск инжектированной группы
ADC1->CR2 |= ADC_CR2_CONT; //Преобразования запускаются одно за другим
ADC1->SQR3 |= ADC_SQR3_SQ1_0; //Задаем номер канала - ADC1
ADC1->CR2 |= ADC_CR2_ADON;//Теперь включаем АЦП
while(1)
{
ADC1->CR2 |= ADC_CR2_SWSTART; //Запуск преобразований
while (!(ADC1->SR & ADC_SR_EOC)); //ждем пока первое преобразование завершится
ADC1->SR = 0;
res = ADC1->DR;
if (res > 1024)
{
GPIOD->ODR = LED_STATE;
}
else
{
GPIOD->BSRRH |= (1 << 15)|(1 << 14)|(1 << 13)|(1 << 12);
}
}
}

Собственно методом тыка заработали и регулярные каналы)))
glags
Цитата(TmYAG @ Dec 25 2014, 11:12) *
Вот не могу понять связь между JSQR и PA1 по какому принципу я должен выставлять биты в регистре, чтобы читать именно из этого пина?


Привожу таблицу зависимости между ADC_JDR1 и JSQR

Кол-во каналов (JL) ADC_JDR1 ADC_JDR2 ADC_JDR3 ADC_JDR4
4.................................JSQ1........JSQ2...........JSQ3........JSQ4
3.................................JSQ2........JSQ3...........JSQ4
2.................................JSQ3........JSQ4
1.................................JSQ4
Например если один канал, то заполняем биты JSQ4 и данные будут при этом читатся из ADC1->JDR1
В регистре JSQR 4 бита для JSQ4, если их заполнить 0011 это будет третий канал ADC что соответствует РА3.
KARLSON
Здравствуйте. Я только осваиваю АЦП у STM32.
Поставил задачу. Имеем 2 канала IN0 и IN1 ADC1. Сделать регулярными каналами. Значения забирать из регистра данных через прерывания.
Когда пробовал с одним каналом, то всё хорошо. Для интереса замерил частоту преобразования ~27 кГц.
Когда стал пробовать уже два канала, то по идее частота не должна уменьшиться в 2 раза (если прерывание будет после преобразования каждого канала)? А она уже 14 кГц. Что меня наводит на мысль, что прерывание происходит после 2-х преобразований.
Как надо настроить ADC для правильного прерывания?
CODE
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

// ноги PA0 и PA1 по умолчанию настроены на вход

ADC_StructInit(&ADC_InitStructure);
ADC_InitStructure.ADC_Mode = ADC_Mode_RegSimult; //Режим Регулярный Одновременный
ADC_InitStructure.ADC_ScanConvMode = ENABLE; // режим сканирования
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; // многократное измерение
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // без внешнего триггера
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //выравнивание битов результат - прижать вправо
ADC_InitStructure.ADC_NbrOfChannel = 2; //количество каналов
ADC_Init(ADC1, &ADC_InitStructure);
ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE); // прерывание от окончании преобразования

ADC_Cmd(ADC1, ENABLE); // включение ADC
// настройка канала
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5);

// калибровка АЦП
ADC_ResetCalibration(ADC1);
while (ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while (ADC_GetCalibrationStatus(ADC1));



/* NVIC configuration */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel = ADC1_2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

ADC_SoftwareStartConvCmd(ADC1, ENABLE);

прерывание
Код
void ADC1_2_IRQHandler (void)
{
    ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);
    switch(cnt_number_channel_ADC1++)
    {
    case 0: ADC1_IN0_Value = ADC1->DR;
            break;
    case 1: cnt_number_channel_ADC1 = 0;
            ADC1_IN1_Value = ADC1->DR;
            break;
    default: break;
    }
              // передаем
    FlagADC1 = 1;
}
adnega
Цитата(KARLSON @ Aug 14 2015, 16:18) *
Что меня наводит на мысль, что прерывание происходит после 2-х преобразований.
Как надо настроить ADC для правильного прерывания?

Почитать мануал:
Цитата
The EOC bit is set in the ADC_SR register:
• At the end of each regular group sequence if the EOCS bit is cleared to 0
• At the end of each regular channel conversion if the EOCS bit is set to 1

PS. В линейке STM32 очень много семейств с совершенно различными АЦП. Лучше указывать конкретный камень.
uriy
Если хотите читать несколько регулярных каналов то только по DMA.
Если не хотите DMA можно использовать инжектированные каналы.
Там до 4 каналов можно читать из разных регистров.

Вот из моих проектов два канала по DMA.
АЦП тактируется от TIM3.
Прерывание по DMA по заполнению половины буферы и всего буфера.

CODE

void InitADC(void)
{
ADC_InitTypeDef ADC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
DMA_InitTypeDef DMA_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOC, &GPIO_InitStructure);

/* DMA1 channel1 configuration ----------------------------------------------*/
//DMA_DeInit(DMA1_Channel1);
//DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1->DR;
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;
#warning check it
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)adc_buf;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = ADC_BUF_SIZE;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
DMA_ClearITPendingBit(DMA1_IT_HT1);
DMA_ClearITPendingBit(DMA1_IT_TC1);

DMA_ITConfig(DMA1_Channel1, (DMA_IT_TC | DMA_IT_HT), ENABLE);
//DMA_ITConfig(DMA1_Channel1, DMA1_IT_TC1, ENABLE);
/* Enable DMA1 channel1 */
DMA_Cmd(DMA1_Channel1, ENABLE);

NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
NVIC_EnableIRQ(DMA1_Channel1_IRQn);

/* ADC1 configuration ------------------------------------------------------*/
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T3_TRGO;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 2;
ADC_Init(ADC1, &ADC_InitStructure);

/* ADC1 regular channel Configuration */
ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_239Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 2, ADC_SampleTime_239Cycles5);

ADC_ExternalTrigConvCmd(ADC1, ENABLE);

/* Enable ADC1 DMA */
ADC_DMACmd(ADC1, ENABLE);

/* Enable ADC1 */
ADC_Cmd(ADC1, ENABLE);

/* Enable ADC1 reset calibaration register */
ADC_ResetCalibration(ADC1);
/* Check the end of ADC1 reset calibration register */
while (ADC_GetResetCalibrationStatus(ADC1));

/* Start ADC1 calibaration */
ADC_StartCalibration(ADC1);
/* Check the end of ADC1 calibration */
while (ADC_GetCalibrationStatus(ADC1));

}


void InitTimer3(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

/* TIM3 clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

/* Time base configuration */
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Period = 23999; /// 1 khz clock required nned to check core frequency
TIM_TimeBaseStructure.TIM_Prescaler = 2;
TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

/* TIM3 TRGO selection */
TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_Update);

/* TIM3 enable counter */
TIM_Cmd(TIM3, ENABLE);
}


void DMA1_Channel1_IRQHandler(void)
{
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;

if (DMA_GetITStatus(DMA1_IT_HT1))
{
DMA_ClearITPendingBit(DMA1_IT_HT1);
xSemaphoreGiveFromISR(xSemaphoreADC_DMA_HT, &xHigherPriorityTaskWoken );
}
if (DMA_GetITStatus(DMA1_IT_TC1))
{
DMA_ClearITPendingBit(DMA1_IT_TC1);
xSemaphoreGiveFromISR(xSemaphoreADC_DMA_TC, &xHigherPriorityTaskWoken );
}
DMA_ClearITPendingBit(DMA1_IT_GL1);
portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );
}
KARLSON
Камень 103VG
То что лучше с DMA и по таймеру это понятно. Просто хотелось бы узнать есть такая возможность (регулярные и по прерыванию) или нет.
adnega
Цитата(KARLSON @ Aug 15 2015, 09:20) *
Камень 103VG
То что лучше с DMA и по таймеру это понятно. Просто хотелось бы узнать есть такая возможность (регулярные и по прерыванию) или нет.

В 103 бита EOCS нет. Поэтому EOC только после обработки всех регулярных каналов.
Но это не страшно: просто в прерывании настраивайте следующий канал и запускайте одиночное преобразование.
А потом переходите на DMA (если кГц преобразований важны).
KARLSON
Теперь всё ясно. Благодарю. Буду переходить на DMA.
smk
Для пытливых умов. Код инициализации АЦП и ДМА. Результат преобразований один раз складывается в массив My_mass из N элементов. Один канал. Код перезапуска ниже. Ну тоесть пнули, тысячу отсчетов в массив из тысячи элементов аппаратно сложилось.
Инициализация:
Код
    RCC->APB2ENR |=(1<<8);                                                        //
    ADC1->CR2 |= (1<<0);                                                            //ADON: A/D Converter ON    
    ADC->CCR |= (1<<16)|(1<<17);             //TSVREFE: Temperature sensor and VREFINT enable & VBATE: VBAT enable
    ADC1->CR2 |= ADC_CR2_DDS;                                                             //DMA: Direct memory access mode (for single ADC mode)
    ADC1->CR2 |= ADC_CR2_DMA;                                                             //DMA disable selection (for single ADC mode)
    ADC1->CR2 |= (1<<1);                                                            //CONT: Continuous conversion
    ADC1->SMPR2 |= (1<<12);         //IN4 = 111: 480 cycles
    ADC1->SQR1 &= ~((1<<20)|(1<<21)|(1<<22)|(1<<23));            //11: 1 conversions
    ADC1->SQR3 = 4;                                                                        //
    
    RCC->AHB1ENR |=(1<<22);                                                      //DMA1EN: DMA2 clock enable
    DMA2_Stream0->CR &= ~((1<<27)|(1<<26)|(1<<25));      //000: channel 0 selected
    DMA2_Stream0->CR |= (1<<13);                                        //MSIZE[1:0]: Memory data size 01: half-word (16-bit)
    DMA2_Stream0->CR |= (1<<11);                                          //PSIZE[1:0]: Peripheral data size 01: Half-word (16-bit)
    DMA2_Stream0->CR |= (1<<10);                                          //MINC: Memory increment mode
    DMA2_Stream0->CR |= DMA_SxCR_PL;
    DMA2_Stream0->CR &= ~((1<<7)|(1<<6));                       //DIR[1:0]: Data transfer direction 00: Peripheral-to-memory
    DMA2_Stream0->NDTR = sample_size_adc;                                              //NDT[15:0]: Number of data items to transfer
    DMA2_Stream0->PAR = (uint32_t)&ADC1->DR;                  //PAR[31:0]: Peripheral address
    DMA2_Stream0->M0AR = (uint32_t)&My_mass;              //M0A[31:0]: Memory 0 address
    DMA2_Stream0->CR |= (1<<0);                                              //EN: Stream enable / flag stream ready when read low
    ADC1->CR2 |= (1<<30);                                                          //SWSTART: Start conversion of regular channels


Код "пинка" для перезапуска:
Код
        DMA2_Stream0->CR &= ~DMA_SxCR_EN;
        DMA2_Stream0->M0AR = (uint32_t)&My_mass;        
        DMA2_Stream0->NDTR = sample_size_adc; //NDT[15:0]: Number of data items to transfer
        DMA2->LIFCR |= ((1UL<<5)|(1<<4));
        ADC1->SR &= ~ADC_SR_OVR;
        ADC1->CR2 &= ~ADC_CR2_DMA;
        ADC1->CR2 |= ADC_CR2_DMA;
        DMA2_Stream0->CR |= DMA_SxCR_EN;//EN: Stream enable / flag stream ready when read low
        ADC1->CR2 |= ADC_CR2_SWSTART;


sample_size_adc это число выборок, которые хотите сделать. Размер массива, куда сложится, должен быть не менее числа выборок.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.