Статьи » Разработки |
2020-02-11 в 15:45 (последнее изменение 2021-03-09 в 01:37)
Когда мы хотим сделать одно преобразование одного канала АЦП - мы ждем результата АЦП в цикле, который не является эффективным способом использования ресурсов процессора. Лучше запустить преобразование и дождаться полного прерывания преобразования. Таким образом, процессор может выполнять другие задачи, а не ждать завершения преобразования АЦП. На этот раз мы рассмотрим другой пример, в котором мы настроим более одного канала и прочитаем значения АЦП, используя процедуру обслуживания прерываний. Как работает многоканальное преобразование АЦП?Если нам нужно преобразовать несколько каналов непрерывно, нам нужно настроить регистры последовательности (ADC_SQRx). Существует три регистра последовательности: ADC_SQR1, ADC_SQR2 и ADC_SQR3, где мы можем установить максимум 16 каналов в любом порядке. Последовательность преобразования начинается с настроек SQ1 [4: 0] в регистре ADC_SQR3. Биты [4: 0] содержат номер канала АЦП. Все 16 последовательных каналов могут быть установлены одинаково через все регистры SQR. Затем в регистре ADC_SQR1 есть четыре бита, помеченные как L [3: 0], где вы можете установить число повторений чтения последовательности. Еще одна вещь, о которой нам нужно позаботиться - установить время выборки для каждого канала. Как мы знаем, каждый канал в последовательности может быть установлен на разное время преобразования. Время выборки для каждого канала может быть установлено в двух регистрах: ADC_SMPR1 и ADC_AMPR2. Есть три бита для каждого канала в последовательности. Если вы используете стандартную периферийную библиотеку, настройка многоканального АЦП становится и легкой задачей. Настройка многоканального преобразования АЦП с записью DMAДавайте напишем пример, где мы будем читать первые 8 каналов АЦП четыре раза, используя режим сканирования. Затем мы вычисляем среднее значение каждого канала и затем выводим результаты на экран терминала с помощью UART. Мы запишем значения АЦП в память, используя канал DMA. Как только все данные будут сохранены в памяти, будет сгенерировано полное прерывание передачи DMA для запуска усреднения и вывода. В техническом описании STM32F100x мы находим, что выводам АЦП назначены альтернативные функции следующим образом:
Для первых восьми каналов нам необходимо установить контакты A0 - A7 в качестве аналоговых входов. Затем мы можем настроить режим преобразования АЦП. Кроме того, нам нужно настроить режим преобразования сканирования, чтобы иметь возможность проходить по всем каналам, выбранным в регистрах ADC1_SQRx. В периферийной библиотеке это выглядит так: Код
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
Затем мы должны включить режим непрерывного преобразования, поскольку мы хотим несколько раз циклически переключаться между списками каналов: Код
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
Затем мы указываем количество каналов для преобразования в режиме сканирования: Код
ADC_InitStructure.ADC_NbrOfChannel = 8;
Следующая вещь - указать, какие каналы и в каком порядке нам нужно конвертировать. Для этого мы настраиваем каждый канал индивидуально с помощью команд: Код
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_41Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_41Cycles5); ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_41Cycles5); ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_41Cycles5); ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 5, ADC_SampleTime_41Cycles5); ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 6, ADC_SampleTime_41Cycles5); ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 7, ADC_SampleTime_41Cycles5); ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 8, ADC_SampleTime_41Cycles5); Я выбрал все восемь каналов подряд от 0 до 7. Но вы можете изменить числа, как вам нравится. Остальное - настроить DMA, где он копирует значения АЦП в память при каждом событии EOC. После того, как DMA копирует заранее определенное количество значений, оно генерирует прерывание. Тогда мы можем манипулировать данными так, как нам нравится. Как и в нашем примере, мы усредняем несколько экземпляров. Это результат на экране терминала. Вы можете подключить потенциометр или любой другой аналоговый датчик к каждому каналу, чтобы увидеть его значение АЦП. Рабочий код на C многоканального АЦПВот полный основной исходный код, если вы хотите проанализировать или использовать фрагменты для своих целей: Код
// Includes --------------*/ #include "stm32f10x.h" #include "usart.h" #include <stdio.h> #define ARRAYSIZE 8*4 #define ADC1_DR ((uint32_t)0x4001244C) volatile uint16_t ADC_values[ARRAYSIZE]; volatile uint32_t status = 0; void ADCInit(void); void DMAInit(void); int main(void) { uint8_t index; //initialize USART1 Usart1Init(); ADCInit(); DMAInit(); //Enable DMA1 Channel transfer DMA_Cmd(DMA1_Channel1, ENABLE); //Start ADC1 Software Conversion ADC_SoftwareStartConvCmd(ADC1, ENABLE); //wait for DMA complete while (!status){}; ADC_SoftwareStartConvCmd(ADC1, DISABLE); //print averages /*for(index = 0; index<8; index++) { printf("ch%d = %d ",index, ADC_values[index]); }*/ for(index = 0; index<8; index++){ printf("\r\n ADC value on ch%d = %d\r\n", index, (uint16_t)((ADC_values[index]+ADC_values[index+8] +ADC_values[index+16]+ADC_values[index+24])/4)); } while (1) { //interrupts does the job } } void ADCInit(void){ //--Enable ADC1 and GPIOA-- RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; //Variable used to setup the GPIO pins //==Configure ADC pins (PA0 -> Channel 0 to PA7 -> Channel 7) as analog inputs== GPIO_StructInit(&GPIO_InitStructure); // Reset init structure, if not it can cause issues... GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1| GPIO_Pin_2| GPIO_Pin_3| GPIO_Pin_4| GPIO_Pin_5| GPIO_Pin_6| GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init(GPIOA, &GPIO_InitStructure); ADC_InitTypeDef ADC_InitStructure; //ADC1 configuration ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //We will convert multiple channels ADC_InitStructure.ADC_ScanConvMode = ENABLE; //select continuous conversion mode ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//! //select no external triggering ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //right 12-bit data alignment in ADC data register ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //8 channels conversion ADC_InitStructure.ADC_NbrOfChannel = 8; //load structure values to control and status registers ADC_Init(ADC1, &ADC_InitStructure); //wake up temperature sensor //ADC_TempSensorVrefintCmd(ENABLE); //configure each channel ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_41Cycles5); ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_41Cycles5); ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_41Cycles5); ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_41Cycles5); ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 5, ADC_SampleTime_41Cycles5); ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 6, ADC_SampleTime_41Cycles5); ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 7, ADC_SampleTime_41Cycles5); ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 8, ADC_SampleTime_41Cycles5); //Enable ADC1 ADC_Cmd(ADC1, ENABLE); //enable DMA for ADC ADC_DMACmd(ADC1, ENABLE); //Enable ADC1 reset calibration register ADC_ResetCalibration(ADC1); //Check the end of ADC1 reset calibration register while(ADC_GetResetCalibrationStatus(ADC1)); //Start ADC1 calibration ADC_StartCalibration(ADC1); //Check the end of ADC1 calibration while(ADC_GetCalibrationStatus(ADC1)); } void DMAInit(void){ //enable DMA1 clock RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //create DMA structure DMA_InitTypeDef DMA_InitStructure; //reset DMA1 channe1 to default values; DMA_DeInit(DMA1_Channel1); //channel will be used for memory to memory transfer DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //setting normal mode (non circular) DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //medium priority DMA_InitStructure.DMA_Priority = DMA_Priority_High; //source and destination data size word=32bit DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //automatic memory destination increment enable. DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //source address increment disable DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //Location assigned to peripheral register will be source DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //chunk of data to be transfered DMA_InitStructure.DMA_BufferSize = ARRAYSIZE; //source and destination start addresses DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADC1_DR; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADC_values; //send values to DMA registers DMA_Init(DMA1_Channel1, &DMA_InitStructure); // Enable DMA1 Channel Transfer Complete interrupt DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE); DMA_Cmd(DMA1_Channel1, ENABLE); //Enable the DMA1 - Channel1 NVIC_InitTypeDef NVIC_InitStructure; //Enable DMA1 channel IRQ Channel */ 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); }
Посмотрите еще эти статьи
Управляем вытяжкой на кухне пультом от телевизора на Arduino Вывод картинки на дисплей 128x64 Детектор микронаушников Ethernet shield w5100 таймер включения на веб странице Вывод различных данных на дисплей LCD 128x64 Подключение датчиков газа и дыма серии MQ к Arduino Не пропустите обновления! Подписывайтесь на нашу группу Вконтакте. Так же у нас есть Telegram канал. Вам понравился наш материал? Поделитесь с коллегами! Просмотров: 12892. Оценка статьи: 3.5 из 5. Уже оценило 22 читателя |
Всего комментариев: 1 | ||
|