CMSIS
Настройка преобразования первых 8ми регулярных каналов АЦП по запуску из программы с использованием канала DMA
// настройка ADC1
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; // такты на ADC1
ADC1->SMPR2 |= ADC_SMPR2_SMP0 | ADC_SMPR2_SMP1
| ADC_SMPR2_SMP2 | ADC_SMPR2_SMP3 | ADC_SMPR2_SMP4
| ADC_SMPR2_SMP5 | ADC_SMPR2_SMP6 | ADC_SMPR2_SMP7; // количество циклов преобразования 239.5
ADC1->SQR1 |= ADC_SQR1_L_2 | ADC_SQR1_L_1 | ADC_SQR1_L_0; // длина последовательности регулярных каналов = 8;
// добавление в последовательность каналов
ADC1->SQR2 |= ADC_SQR2_SQ8_2 | ADC_SQR2_SQ8_1 | ADC_SQR2_SQ8_0 //канал 7
| ADC_SQR2_SQ7_2 | ADC_SQR2_SQ7_1; // канал 6
ADC1->SQR3 |= ADC_SQR3_SQ6_2 | ADC_SQR3_SQ6_0 //канал 5
| ADC_SQR3_SQ5_2 //канал 4
| ADC_SQR3_SQ4_1 | ADC_SQR3_SQ4_0 //канал 3
| ADC_SQR3_SQ3_1 //канал 2
| ADC_SQR3_SQ2_0; // канал 1
//канал 0 включен по умолчанию т.к. в младших битах SQ3 0
// ADC1->CR2 |= ADC_CR2_CONT; // включаем если нужно непрерывное преобразование последовательности в цикле
ADC1->CR2 |= ADC_CR2_DMA //включаем работу с DMA
| ADC_CR2_EXTTRIG //включаем работу от внешнего события
| ADC_CR2_EXTSEL //выбираем триггером запуска регулярной последовательности событие SWSTART
| ADC_CR2_JEXTSEL; // выбираем триггером запуска выделенной последовательности событие JSWSTART дабы эти каналы не отнимали у мк времени
ADC1->CR1 |= ADC_CR1_SCAN; // включаем автоматический перебор всех каналов в последовательности
//макросы для включения/выключения АЦП с DMA
#define ADC_ON ADC1->CR2 |= ADC_CR2_ADON; \
ADC1->CR2 |= ADC_CR2_SWSTART; \
DMA1_Channel1->CCR |= DMA_CCR1_TCIE | DMA_CCR1_EN; //включаем преобразование прерывание DMA
#define ADC_OFF ADC->CR2 &= (~(ADC_CR2_ADON)); \
DMA1_Channel1->CCR &= (~(DMA_CCR1_TCIE | DMA_CCR1_EN)); //выключаем преобразование и прерывание DMA
===============================================================
Установим предварительный делитель и разрешим тактирование модуля:
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;
}
}
================================================================
/* (1) Enable the peripheral clock of the ADC */
/* (2) Set peripheral prescaler to /2 so PCLK = HCLK/2 = 24MHz */
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
RCC->CFGR |= RCC_CFGR_PPRE_2;
/* (1) Ensure that ADEN = 0 */
/* (2) Clear ADEN */
/* (3) Launch the calibration by setting ADCAL */
/* (4) Wait until ADCAL=0 */
if ((ADC1->CR & ADC_CR_ADEN) != 0) /* (1) */
{
ADC1->CR &= (uint32_t)(~ADC_CR_ADEN); /* (2) */
}
ADC1->CR |= ADC_CR_ADCAL; /* (3) */
while ((ADC1->CR & ADC_CR_ADCAL) != 0) /* (4) */
{
/* For robust implementation, add here time-out management */
}
// Configure ADC pin, PA0-PA4 analog
RCC->AHBENR |= RCC_AHBENR_GPIOAEN ;
GPIOA->MODER |= GPIO_MODER_MODER0|
GPIO_MODER_MODER1|
GPIO_MODER_MODER2|
GPIO_MODER_MODER3|
GPIO_MODER_MODER4
;
do
{
/* For robust implementation, add here time-out management */
ADC1->CR |= ADC_CR_ADEN; /* (1) */
}while ((ADC1->ISR & ADC_ISR_ADRDY) == 0) /* (2) */;
/* (1) Select PCLK/2 by writing 01 in CKMODE */
/* (2) Select the continuous mode */
/* (3) Select CHSEL1, CHSEL9, CHSEL10 and CHSEL17 */
/* (4) Select a sampling mode of 111 i.e. 239.5 ADC clk to be greater than 17.1us */
/* (5) Enable interrupts on overrrun */
/* (6) Wake-up the VREFINT (only for VBAT, Temp sensor and VRefInt) */
ADC1->CFGR2 |= ADC_CFGR2_CKMODE_0; /* (1) */
ADC1->CFGR1 |= ADC_CFGR1_CONT; /* (2)*/
ADC1->CHSELR = ADC_CHSELR_CHSEL0 |
ADC_CHSELR_CHSEL1 |
ADC_CHSELR_CHSEL2 |
ADC_CHSELR_CHSEL3 |
ADC_CHSELR_CHSEL4;
ADC1->SMPR |= ADC_SMPR_SMP_0 | ADC_SMPR_SMP_1 | ADC_SMPR_SMP_2; /* (4) */
//ADC1->IER = ADC_IER_OVRIE; /* (5) */
//ADC->CCR |= ADC_CCR_VREFEN; /* (6) */
/* (1) Enable the peripheral clock on DMA */
/* (2) Enable DMA transfer on ADC and circular mode */
/* (3) Configure the peripheral data register address */
/* (4) Configure the memory address */
/* (5) Configure the number of DMA tranfer to be performs on DMA channel 1 */
/* (6) Configure increment, size, interrupts and circular mode */
/* (7) Enable DMA Channel 1 */
RCC->AHBENR |= RCC_AHBENR_DMA1EN; /* (1) */
ADC1->CFGR1 |= ADC_CFGR1_DMAEN | ADC_CFGR1_DMACFG; /* (2) */
DMA1_Channel1->CPAR = (uint32_t) (&(ADC1->DR)); /* (3) */
DMA1_Channel1->CMAR = (uint32_t)(ADC_array); /* (4) */
DMA1_Channel1->CNDTR = NUMBER_OF_ADC_CHANNEL; /* (5) */
DMA1_Channel1->CCR |= DMA_CCR_MINC | DMA_CCR_MSIZE_0 | DMA_CCR_PSIZE_0 \
| DMA_CCR_TEIE | DMA_CCR_CIRC; /* (6) */
DMA1_Channel1->CCR |= DMA_CCR_EN; /* (7) */
ADC1->CR |= ADC_CR_ADSTART; /* start the ADC conversions */
================================================================
Config ADC for STM32F051R8 for read with Timer trigger#include "stm32f0xx.h"
int32_t volatile static mcu_temperature;
void ADC_ini(void);
void Timer_ini(void);
void ADC_ini(){
RCC->CR2 |= RCC_CR2_HSI14ON;
while (!(RCC->CR2 & RCC_CR2_HSI14RDY));
RCC->APB2ENR = RCC_APB2ENR_ADC1EN; //RCC_APB2ENR_ADCEN
ADC1->CHSELR = ADC_CHSELR_CHSEL16;
ADC1->CR |= ADC_CR_ADCAL;
while ((ADC1->CR & ADC_CR_ADCAL) != 0);
ADC1->SMPR |= ADC_SMPR1_SMPR_0 | ADC_SMPR1_SMPR_1 | ADC_SMPR1_SMPR_2;
ADC1->CFGR1 &= (uint32_t)(~ADC_CFGR1_CONT);
ADC1->CFGR1 |= ADC_CFGR1_EXTEN_0;
ADC1->CFGR1 |= ADC_CFGR1_EXTSEL_0 | ADC_CFGR1_EXTSEL_1;
ADC->CCR |= ADC_CCR_TSEN;
ADC1->CR |= ADC_CR_ADEN;
while ((ADC1->ISR & ADC_ISR_ADRDY) == 0);
ADC1->CR |= ADC_CR_ADSTART;
}
void Timer_ini(void){
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
TIM3->PSC = 480-1;
TIM3->ARR = 5000;
TIM3->CR2 |= TIM_CR2_MMS_1;
//TIM3->DIER |= TIM_DIER_TIE;//TIM3_DIER_TIE;
TIM3->CR1 |= TIM_CR1_ARPE;
TIM3->CR1 |= TIM_CR1_CEN;
}
int main(void){
ADC_ini();
Timer_ini();
/* (1) Enable the peripheral clock of GPIOC */
/* (2) Select output mode (01) on GPIOB pin 2,8,9 */
RCC->AHBENR |= RCC_AHBENR_GPIOCEN; /* (1) */
GPIOC->MODER = (GPIOC->MODER & ~(GPIO_MODER_MODER2)) | (GPIO_MODER_MODER2_0); /* (2) */
GPIOC->MODER = (GPIOC->MODER & ~(GPIO_MODER_MODER9)) | (GPIO_MODER_MODER9_0); /* (2) */
GPIOC->MODER = (GPIOC->MODER & ~(GPIO_MODER_MODER8)) | (GPIO_MODER_MODER8_0); /* (2) */
while(1)
{
if ((ADC1->ISR & ADC_ISR_EOC))
{
mcu_temperature = ADC1->DR; // ADC->DR
GPIOC->ODR ^= ( 1 << 2 );
GPIOC->ODR ^= ( 1 << 8 );
GPIOC->ODR ^= ( 1 << 9 );
}
}
}
HAL
STM32F0 АЦП с DMA 5 АЦП входов
/* Includes ------------------------------------------------------------------*/
#include "stm32f0xx_hal.h"
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private variables ---------------------------------------------------------*/
ADC_HandleTypeDef hadc;
DMA_HandleTypeDef hdma_adc;
UART_HandleTypeDef huart1;
/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
volatile uint16_t ADC_DMA_ARRAY[5];
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
void Error_Handler(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_ADC_Init(void);
static void MX_USART1_UART_Init(void);
/* USER CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*/
/* USER CODE END PFP */
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration----------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* Configure the system clock */
SystemClock_Config();
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_ADC_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
HAL_ADC_Start_DMA(&hadc, (uint32_t *)&ADC_DMA_ARRAY, 5);
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/** System Clock Configuration
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;
RCC_PeriphCLKInitTypeDef PeriphClkInit;
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL2;
RCC_OscInitStruct.PLL.PREDIV = RCC_PREDIV_DIV1;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
{
Error_Handler();
}
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1;
PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK1;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
/* SysTick_IRQn interrupt configuration */
HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}
/* ADC init function */
static void MX_ADC_Init(void)
{
ADC_ChannelConfTypeDef sConfig;
/**Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc.Instance = ADC1;
hadc.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc.Init.Resolution = ADC_RESOLUTION_12B;
hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD;
hadc.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc.Init.LowPowerAutoWait = DISABLE;
hadc.Init.LowPowerAutoPowerOff = DISABLE;
hadc.Init.ContinuousConvMode = ENABLE;
hadc.Init.DiscontinuousConvMode = DISABLE;
hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc.Init.DMAContinuousRequests = ENABLE;
hadc.Init.Overrun = ADC_OVR_DATA_PRESERVED;
if (HAL_ADC_Init(&hadc) != HAL_OK)
{
Error_Handler();
}
/**Configure for the selected ADC regular channel to be converted.
*/
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;
sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
Error_Handler();
}
/**Configure for the selected ADC regular channel to be converted.
*/
sConfig.Channel = ADC_CHANNEL_2;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
Error_Handler();
}
/**Configure for the selected ADC regular channel to be converted.
*/
sConfig.Channel = ADC_CHANNEL_4;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
Error_Handler();
}
/**Configure for the selected ADC regular channel to be converted.
*/
sConfig.Channel = ADC_CHANNEL_5;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
Error_Handler();
}
/**Configure for the selected ADC regular channel to be converted.
*/
sConfig.Channel = ADC_CHANNEL_6;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
Error_Handler();
}
}
/* USART1 init function */
static void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 9600;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
}
/**
* Enable DMA controller clock
*/
static void MX_DMA_Init(void)
{
/* DMA controller clock enable */
__HAL_RCC_DMA1_CLK_ENABLE();
/* DMA interrupt init */
/* DMA1_Channel1_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
}
/** Configure pins as
* Analog
* Input
* Output
* EVENT_OUT
* EXTI
*/
static void MX_GPIO_Init(void)
{
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOF_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @param None
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler */
/* User can add his own implementation to report the HAL error return state */
while(1)
{
}
/* USER CODE END Error_Handler */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t* file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif
/**
* @}
*/
/**
* @}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
/* Includes ------------------------------------------------------------------*/
#include "stm32f0xx_hal.h"
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private variables ---------------------------------------------------------*/
ADC_HandleTypeDef hadc;
DMA_HandleTypeDef hdma_adc;
UART_HandleTypeDef huart1;
/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
volatile uint16_t ADC_DMA_ARRAY[5];
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
void Error_Handler(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_ADC_Init(void);
static void MX_USART1_UART_Init(void);
/* USER CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*/
/* USER CODE END PFP */
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration----------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* Configure the system clock */
SystemClock_Config();
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_ADC_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
HAL_ADC_Start_DMA(&hadc, (uint32_t *)&ADC_DMA_ARRAY, 5);
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/** System Clock Configuration
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;
RCC_PeriphCLKInitTypeDef PeriphClkInit;
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL2;
RCC_OscInitStruct.PLL.PREDIV = RCC_PREDIV_DIV1;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
{
Error_Handler();
}
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1;
PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK1;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
/* SysTick_IRQn interrupt configuration */
HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}
/* ADC init function */
static void MX_ADC_Init(void)
{
ADC_ChannelConfTypeDef sConfig;
/**Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc.Instance = ADC1;
hadc.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc.Init.Resolution = ADC_RESOLUTION_12B;
hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD;
hadc.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc.Init.LowPowerAutoWait = DISABLE;
hadc.Init.LowPowerAutoPowerOff = DISABLE;
hadc.Init.ContinuousConvMode = ENABLE;
hadc.Init.DiscontinuousConvMode = DISABLE;
hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc.Init.DMAContinuousRequests = ENABLE;
hadc.Init.Overrun = ADC_OVR_DATA_PRESERVED;
if (HAL_ADC_Init(&hadc) != HAL_OK)
{
Error_Handler();
}
/**Configure for the selected ADC regular channel to be converted.
*/
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;
sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
Error_Handler();
}
/**Configure for the selected ADC regular channel to be converted.
*/
sConfig.Channel = ADC_CHANNEL_2;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
Error_Handler();
}
/**Configure for the selected ADC regular channel to be converted.
*/
sConfig.Channel = ADC_CHANNEL_4;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
Error_Handler();
}
/**Configure for the selected ADC regular channel to be converted.
*/
sConfig.Channel = ADC_CHANNEL_5;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
Error_Handler();
}
/**Configure for the selected ADC regular channel to be converted.
*/
sConfig.Channel = ADC_CHANNEL_6;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
Error_Handler();
}
}
/* USART1 init function */
static void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 9600;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
}
/**
* Enable DMA controller clock
*/
static void MX_DMA_Init(void)
{
/* DMA controller clock enable */
__HAL_RCC_DMA1_CLK_ENABLE();
/* DMA interrupt init */
/* DMA1_Channel1_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
}
/** Configure pins as
* Analog
* Input
* Output
* EVENT_OUT
* EXTI
*/
static void MX_GPIO_Init(void)
{
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOF_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @param None
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler */
/* User can add his own implementation to report the HAL error return state */
while(1)
{
}
/* USER CODE END Error_Handler */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t* file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif
/**
* @}
*/
/**
* @}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
SPL
/* Initialize the ADC_Mode member */ADC_InitStruct->ADC_Mode = ADC_Mode_Independent;
/* initialize the ADC_ScanConvMode member */
ADC_InitStruct->ADC_ScanConvMode = ENABLE;
/* Initialize the ADC_ContinuousConvMode member */
ADC_InitStruct->ADC_ContinuousConvMode = DISABLE;
/* Initialize the ADC_ExternalTrigConv member */
ADC_InitStruct->ADC_ExternalTrigConv = ADC_ExternalTrigConv_Ext_IT11_TIM8_TRGO;
/* Initialize the ADC_DataAlign member */
ADC_InitStruct->ADC_DataAlign = ADC_DataAlign_Right;
/* Initialize the ADC_NbrOfChannel member */
ADC_InitStruct->ADC_NbrOfChannel = 8;
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 0, ADC_SampleTime_1Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_1Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 2, ADC_SampleTime_1Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 3, ADC_SampleTime_1Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 4, ADC_SampleTime_1Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 5, ADC_SampleTime_1Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 6, ADC_SampleTime_1Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 7, ADC_SampleTime_1Cycles5);
void ADC_DiscModeChannelCountConfig (ADC1,8);
ADC_DiscModeCmd(ADC1, ENABLE);
=====================================================================
For STM32F4 ContinuousConvMode with DMA for 8 channel
uint16_t adc_buffer[8];
void dma_setup(void)
{
DMA_DeInit(DMA2_Stream0);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
DMA_InitTypeDef dma2_setup;
dma2_setup.DMA_Channel = DMA_Channel_0; //channel0 привязан к АЦП1
dma2_setup.DMA_PeripheralBaseAddr = (uint32_t) &ADC1->DR; //копируем из АЦП1
dma2_setup.DMA_Memory0BaseAddr = (uint32_t) &adc_buffer[0]; //адрес буфера, т.е. куда копировать
dma2_setup.DMA_BufferSize = 8; //sizeof(adc_buffer); //размер буфера
dma2_setup.DMA_DIR = DMA_DIR_PeripheralToMemory; //из периферии в память
dma2_setup.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //не изменяем адрес периферии, все время копируем только из АЦП1
dma2_setup.DMA_MemoryInc = DMA_MemoryInc_Enable; //а вот адрес памяти изменяем, чтобы каждое новое значение записывалось в след. элемент массива
dma2_setup.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //размер данных буфера 16бит
dma2_setup.DMA_Mode = DMA_Mode_Circular; //циклический режим
dma2_setup.DMA_Priority = DMA_Priority_High; //высший приоритет
dma2_setup.DMA_FIFOMode = DMA_FIFOMode_Disable; //фифо не используем
dma2_setup.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; //относится к фифо
dma2_setup.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //размер данных периферии 16бит
dma2_setup.DMA_MemoryBurst = DMA_MemoryBurst_Single; //относится к пакетной пересылке
dma2_setup.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA2_Stream0, &dma2_setup);
DMA_Cmd(DMA2_Stream0, ENABLE);
}
void adc_setup(void)
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
GPIO_InitTypeDef pa_setup;
pa_setup.GPIO_Mode = GPIO_Mode_AIN;
pa_setup.GPIO_OType = GPIO_OType_PP;
pa_setup.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_4 | GPIO_Pin_6 | GPIO_Pin_7;
pa_setup.GPIO_PuPd = GPIO_PuPd_NOPULL;
pa_setup.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &pa_setup);
//////////////////////////////////
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
GPIO_InitTypeDef pc_setup;
pc_setup.GPIO_Mode = GPIO_Mode_AIN;
pc_setup.GPIO_OType = GPIO_OType_PP;
pc_setup.GPIO_Pin = GPIO_Pin_0 ;
pc_setup.GPIO_PuPd = GPIO_PuPd_NOPULL;
pc_setup.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &pc_setup);
/////////////////////////////////
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
GPIO_InitTypeDef pb_setup;
pb_setup.GPIO_Mode = GPIO_Mode_AIN;
pb_setup.GPIO_OType = GPIO_OType_PP;
pb_setup.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
pb_setup.GPIO_PuPd = GPIO_PuPd_NOPULL;
pb_setup.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &pb_setup);
ADC_InitTypeDef ADC_setup;
ADC_CommonInitTypeDef ADC_CommonInitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
ADC_setup.ADC_ScanConvMode = ENABLE;
ADC_setup.ADC_ContinuousConvMode = ENABLE;
ADC_setup.ADC_DataAlign = ADC_DataAlign_Right;
ADC_setup.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
ADC_setup.ADC_NbrOfConversion = 8; // 1 channel
ADC_setup.ADC_Resolution = ADC_Resolution_12b;
ADC_Init(ADC1, &ADC_setup);
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_3Cycles);
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_3Cycles);
ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 3, ADC_SampleTime_3Cycles);
ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 4, ADC_SampleTime_3Cycles);
ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 5, ADC_SampleTime_3Cycles);
ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 6, ADC_SampleTime_3Cycles);
ADC_RegularChannelConfig(ADC1, ADC_Channel_9, 7, ADC_SampleTime_3Cycles);
ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 8, ADC_SampleTime_3Cycles);
ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
ADC_CommonInit(&ADC_CommonInitStructure);
ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);
ADC_DMACmd(ADC1, ENABLE);
ADC_Cmd(ADC1, ENABLE);
ADC_SoftwareStartConv(ADC1); // Start ADC1 conversion
}
===================================================================
For STM32L1
void init_dma(void)
{
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_InitTypeDef dma_setup;
dma_setup.DMA_BufferSize = 8;
dma_setup.DMA_DIR = DMA_DIR_PeripheralSRC; //периферия источник(source)
dma_setup.DMA_M2M = DMA_M2M_Disable;
dma_setup.DMA_MemoryBaseAddr = (uint32_t)&adc_buf[0]; //source
dma_setup.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //2 bytes
dma_setup.DMA_MemoryInc = DMA_MemoryInc_Enable;
dma_setup.DMA_Mode = DMA_Mode_Circular;
dma_setup.DMA_PeripheralBaseAddr = (uint32_t)&(ADC1->DR);
dma_setup.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
dma_setup.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
dma_setup.DMA_Priority = DMA_Priority_Medium;
DMA_Init(DMA1_Channel1, &dma_setup);
DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE); //включаем прерывание по половине буфера
DMA_ITConfig(DMA1_Channel1, DMA_IT_HT, ENABLE); //включаем прерывание по окончанию буфера
DMA_Cmd(DMA1_Channel1, ENABLE);
NVIC_EnableIRQ(DMA1_Channel1_IRQn);
=====================================================================
#define ADC_CH 8 //количество каналов
#define OVER_SAMPL 32 //количество семплов для усреднения
uint32_t ADC_SUM[ADC_CH]; //буффер для суммирования
uint16_t curr_sample;//считаем количество семлов
uint16_t AI[ADC_CH],ADC_VAL[ADC_CH];//АI- финальный буфер, ADC_VAL- в нее пишем DMA
//Настраиваем АЦП на работу в режиме DMA с прерыванием по Transfer complete
void Setup_ADC1(void)
{
//==Definitions==
#define ADC1_DR_Address ((uint32_t)0x4001244C)
NVIC_InitTypeDef NVIC_InitStructure;
DMA_InitTypeDef DMA_InitStructure; //Variable used to setup the DMA
ADC_InitTypeDef ADC_InitStructure;
//--Enable DMA1 clock--
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
//==Configure DMA1 - Channel1==
DMA_DeInit(DMA1_Channel1); //Set DMA registers to default values
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address; //Address of peripheral the DMA must map to
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) & ADC_VAL; //Variable to which ADC values will be stored
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = ADC_CH; //Buffer size (8 because we using 8 channels)
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;
//Настройка Прерывание -по окончании трансфера
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn ;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
DMA_ITConfig(DMA1_Channel1, DMA1_IT_TC1, ENABLE);
DMA_Init(DMA1_Channel1, &DMA_InitStructure); //Initialise the DMA
//Настройка параметров ADC1 - Channel 0 -7
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
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 = ADC_CH; //We using 8 channels
ADC_Init(ADC1, &ADC_InitStructure); //Initialise ADC1
//Порядок оцифровки
#define ADC_SampleTime ADC_SampleTime_239Cycles5
ADC_RegularChannelConfig(ADC1, ADC_Channel_9, 1, ADC_SampleTime);
ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 2, ADC_SampleTime);
ADC_RegularChannelConfig(ADC1, ADC_Channel_15, 3, ADC_SampleTime);
ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 4, ADC_SampleTime);
ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 5, ADC_SampleTime);
ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 6, ADC_SampleTime);
ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 7, ADC_SampleTime);
ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 8, ADC_SampleTime);
ADC_DMACmd(ADC1, ENABLE); //Enable ADC1 DMA
ADC_Cmd(ADC1, ENABLE); //Enable ADC1
//Калибровка ADC1
//Enable ADC1 reset calibaration register
ADC_ResetCalibration(ADC1);
while (ADC_GetResetCalibrationStatus(ADC1)); //Check the end of ADC1 reset calibration register
//Start ADC1 calibaration
ADC_StartCalibration(ADC1);
while (ADC_GetCalibrationStatus(ADC1)); //Check the end of ADC1 calibration
}
в main стартуем наш АЦП и прерывания
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
DMA_Cmd(DMA1_Channel1, ENABLE);
В прерывании суммируем наши показания и инкрементируем curr_sample до получения OVER_SAMPL, если достигаем ее все выключаем и готовы отдать данные
void DMA1_Channel1_IRQHandler(void)
{
DMA_ClearITPendingBit( DMA1_IT_TC1);
DMA_ITConfig(DMA1_Channel1, DMA1_IT_TC1, DISABLE);
if(curr_sample<OVER_SAMPL)
{
ADC_SUM[0]+=ADC_VAL[0];
ADC_SUM[1]+=ADC_VAL[1];
ADC_SUM[2]+=ADC_VAL[2];
ADC_SUM[3]+=ADC_VAL[3];
ADC_SUM[4]+=ADC_VAL[4];
ADC_SUM[5]+=ADC_VAL[5];
ADC_SUM[6]+=ADC_VAL[6];
ADC_SUM[7]+=ADC_VAL[7];
}
curr_sample++;
DMA_ITConfig(DMA1_Channel1, DMA1_IT_TC1, ENABLE);
if(curr_sample>=OVER_SAMPL)
DMA_ITConfig(DMA1_Channel1, DMA1_IT_TC1, DISABLE);
}
некоторое количество раз в секунду вызывается промежуточная обработка результатов и заново разрешаем прерывания.
void ADC_AVERAGING(void)
{
int16_t i;
if(curr_sample>=OVER_SAMPL)
{
for(i=0;i<ADC_CH;i++)
{
//averaging
AI[i]=(AI[i]*2+(ADC_SUM[i]/OVER_SAMPL))/3;
ADC_SUM[i]=0;
}
curr_sample=0;
DMA_ITConfig(DMA1_Channel1, DMA1_IT_TC1, ENABLE);
}
}
=====================================================================
// 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);
}
======================================================================
ADC_InitStructure.ADC_Mode = ADC_Mode_RegSimult; // только режим одновременных преобразований регулярных каналов
ADC_DiscModeChannelCountConfig(ADC2, 3); // настраиваем прерывающийся режим с периодичностью в 1 канал для АЦП
ADC_DiscModeCmd(ADC2, ENABLE); // включаем прерывающийся режим для АЦП
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; // включаем непрерывный режим преобразований
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // выбор программного запуска преобразований
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; // правое выравнивание данных
ADC_InitStructure.ADC_NbrOfChannel = 5; // выбор количества преобразуемых каналов
ADC_Init(ADC2, &ADC_InitStructure); // инициализация АЦП выбранными выше параметрами
/* ADC2 regular channels configuration */
ADC_RegularChannelConfig(ADC2, ADC_Channel_1, 1, ADC_SampleTime_55Cycles5); // задание последовательности преобразований и времени выборки для АЦП2
ADC_RegularChannelConfig(ADC2, ADC_Channel_3, 2, ADC_SampleTime_55Cycles5); // задание последовательности преобразований и времени выборки для АЦП2
ADC_RegularChannelConfig(ADC2, ADC_Channel_5, 3, ADC_SampleTime_55Cycles5); // задание последовательности преобразований и времени выборки для АЦП2
ADC_RegularChannelConfig(ADC2, ADC_Channel_7, 4, ADC_SampleTime_55Cycles5); // задание последовательности преобразований и времени выборки для АЦП2
ADC_RegularChannelConfig(ADC2, ADC_Channel_9, 5, ADC_SampleTime_55Cycles5); // задание последовательности преобразований и времени выборки для АЦП2
/* Enable ADC2 external trigger conversion */
ADC_ExternalTrigConvCmd(ADC2, ENABLE); // включение режима запуска АЦП от внешнего триггерного сигнала
Выбираем пустой источник триггера (т.е. говорим, что запуск преобразования у нас вручную):
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
Потом разрешаем преобразование от триггера:
ADC_ExternalTrigConvCmd(ADC2, ENABLE);
А как стартануть преобразованию, если источник триггера не указан?
Только такой командой:
ADC_SoftwareStartConvCmd(ADC2, ENABLE);
В .h файле смотреть дефайны ADC_ExternalTrigConv_* для источника триггера.
======================================================================
#include <stm32f0xx_adc.h>
#include <stm32f0xx_rcc.h>
#include <stm32f0xx_gpio.h>
#include <stm32f0xx_misc.h>
#include <stm32f0xx_dma.h>
ADC_InitTypeDef A;
GPIO_InitTypeDef G;
NVIC_InitTypeDef N;
DMA_InitTypeDef D;
#define DMA_ADCMode
#ifndef DMA_ADCMode
/*********************************************************/
/*********************************************************/
/*********** Scan mode, single shot conversion **********/
/*********************************************************/
/*********************************************************/
//Variables to store current states
volatile uint8_t Converted = 0, CChan = 0;
//Variable to store current conversions
volatile uint16_t Conversions[3] = {0};
//End of conversion interrupt handler!
void ADC1_COMP_IRQHandler(void){
//Check for end of conversion
if(ADC_GetITStatus(ADC1, ADC_IT_EOC)){
//Clear interrupt bit
ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);
//Switch statement dependent on current channel. First
//channel is initialised as zero.
switch(CChan){
case 0:
Conversions[0] = ADC_GetConversionValue(ADC1);
CChan = 1;
break;
case 1:
Conversions[1] = ADC_GetConversionValue(ADC1);
CChan = 2;
break;
case 2:
Conversions[2] = ADC_GetConversionValue(ADC1);
CChan = 0;
Converted = 1;
break;
}
}
}
int main(void)
{
//Enable clocks
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
//Configure PA0, PA1 and PA2 as analog inputs
G.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2;
G.GPIO_Mode = GPIO_Mode_AN;
G.GPIO_OType = GPIO_OType_PP;
G.GPIO_PuPd = GPIO_PuPd_NOPULL;
G.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOA, &G);
//Configure ADC in 12bit mode with upward scanning
A.ADC_ContinuousConvMode = DISABLE;
A.ADC_DataAlign = ADC_DataAlign_Right;
A.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
A.ADC_Resolution = ADC_Resolution_12b;
A.ADC_ScanDirection = ADC_ScanDirection_Upward;
ADC_Init(ADC1, &A);
ADC_Cmd(ADC1, ENABLE);
//Enable end of conversion interrupt
ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);
ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE);
//Enable ADC1 interrupt
N.NVIC_IRQChannel = ADC1_COMP_IRQn;
N.NVIC_IRQChannelPriority = 1;
N.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&N);
//Configure the channels to be converted, in this case C0, C1 and
//C2, corresponding to PA0, PA1 and PA2 respectively
ADC_ChannelConfig(ADC1, ADC_Channel_0, ADC_SampleTime_239_5Cycles);
ADC_ChannelConfig(ADC1, ADC_Channel_1, ADC_SampleTime_239_5Cycles);
ADC_ChannelConfig(ADC1, ADC_Channel_2, ADC_SampleTime_239_5Cycles);
//Wait for the ADC to be ready!
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_ADRDY));
while(1)
{
//Start conversion
ADC_StartOfConversion(ADC1);
//Wait for conversion
while(!Converted);
//Reset converted flag (placement of breakpoint!
Converted = 0;
}
}
#else
/*********************************************************/
/*********************************************************/
/*********** Scan mode, single shot conversion **********/
/*********************************************************/
/*********************************************************/
//Variable to store current conversion status
volatile uint8_t Converted = 0;
//Variable to store conversions
uint16_t Conversions[3];
//Interrupt for DMA1 Channel 1. Used to notify the end of
//conversions.
void DMA1_Channel1_IRQHandler(void){
if(DMA_GetITStatus(DMA1_IT_TC1)){
DMA_ClearITPendingBit(DMA1_IT_TC1);
Converted = 1;
}
}
int main(void)
{
//Enable clocks
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
//Configure PA0, PA1 and PA2 as analog inputs
G.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2;
G.GPIO_Mode = GPIO_Mode_AN;
G.GPIO_OType = GPIO_OType_PP;
G.GPIO_PuPd = GPIO_PuPd_NOPULL;
G.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOA, &G);
//Configure ADC for DMA
A.ADC_ContinuousConvMode = DISABLE;
A.ADC_DataAlign = ADC_DataAlign_Right;
A.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
A.ADC_Resolution = ADC_Resolution_12b;
A.ADC_ScanDirection = ADC_ScanDirection_Upward;
ADC_Init(ADC1, &A);
ADC_Cmd(ADC1, ENABLE);
ADC_DMACmd(ADC1, ENABLE);
//Configure the corresponding DMA stream for the ADC
D.DMA_BufferSize = 3; //Three variables
D.DMA_DIR = DMA_DIR_PeripheralSRC; //ADC peripheral is the data source
D.DMA_M2M = DMA_M2M_Disable; //Disable memory to memory mode
D.DMA_MemoryBaseAddr = (uint32_t) &Conversions[0]; //Pointer to variables array
D.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //'Conversions' is 16bits large (HWord)
D.DMA_MemoryInc = DMA_MemoryInc_Enable; //Enable memory increment
D.DMA_Mode = DMA_Mode_Normal; //Non circular DMA mode
D.DMA_PeripheralBaseAddr = (uint32_t) &ADC1->DR; //Pointer to ADC data register!
D.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //ADC1->DR is 16bits!
D.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //Disable peripheral increment
D.DMA_Priority = DMA_Priority_Low; //A low priority DMA stream, not a big deal here!
DMA_Init(DMA1_Channel1, &D);
DMA_Cmd(DMA1_Channel1, ENABLE);
//Enable transfer complete interrupt for DMA1 channel 1
DMA_ClearITPendingBit(DMA1_IT_TC1);
DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);
N.NVIC_IRQChannel = DMA1_Channel1_IRQn;
N.NVIC_IRQChannelPriority = 1;
N.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&N);
//Configure channels to be converted
ADC_ChannelConfig(ADC1, ADC_Channel_0, ADC_SampleTime_239_5Cycles);
ADC_ChannelConfig(ADC1, ADC_Channel_1, ADC_SampleTime_239_5Cycles);
ADC_ChannelConfig(ADC1, ADC_Channel_2, ADC_SampleTime_239_5Cycles);
//Wait for ADC to be ready!
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_ADRDY));
while(1)
{
//Disable the DMA channel
DMA_Cmd(DMA1_Channel1, DISABLE);
//Re-initialize channel
DMA_Init(DMA1_Channel1, &D);
//Enable the DMA channel
DMA_Cmd(DMA1_Channel1, ENABLE);
//Kick off the first conversion!
ADC_StartOfConversion(ADC1);
//Wait for converted data
while(!Converted);
//Reset conversion flag (Breakpoint to read data here!)
Converted = 0;
}
}
#endif
Комментариев нет:
Отправить комментарий