STM32 с нуля. ADC. Аналого-цифровой преобразователь.

Пришло время разобраться, что из себя представляет модуль ADC в микроконтроллерах STM32. Давайте по привычной схеме, сначала теория, под конец небольшая программка для работы с аналого-цифровым преобразователем.

Начнем-с… Вот некоторые характеристики аналого-цифрового преобразователя в STM32f10x:

  • АЦП является 12-ти битным.
  • Возможна генерация прерывания по окончанию преобразования, по окончанию преобразования с инжектированного канала, а также возможно прерывание от Analog Watchdog (что это такое расскажу чуть ниже).
  • Возможно одиночное преобразование и преобразование в непрерывном режиме.
  • Самокалибровка.
  • Запуск преобразования от внешнего события.
  • Работа с ПДП (DMA, прямой доступ к памяти).

Вот структурная схема из даташита:

STM32 ADC.

Пока не забыл про Analog Watchdog, опишу его работу.

Он нужен для того, чтобы следить, что напряжение попадает в определенные пределы. Причем он может сканировать как конкретный канал, так и группу каналов. В регистры ADC_HTR и  ADC_LTR заносим значения верхнего и нижнего порога соответственно. И в случае, если проверяемое напряжение выходит за эти пределы, генерируется прерывание. Полезнейшая вещица!

Каналы АЦП делятся на регулярные и инжектированные. Причем, если запустить измерение инжектированных каналов, то измерение регулярных будет приостановлено. Еще одна очень полезная функция.

Как и в микроконтроллерах AVR возможно выравнивание результата по правому или по левому краю. Тут правда результат 12-ти битный. Но суть та же. Вот как все это выглядит в регистрах:

Выравнивание результата АЦП.

Интересная штука – прерывание от внешнего события. Забегая вперед скажу, что такое безобразие мы устроим в нашей программе, так что на практике и разберемся, зачем это нужно и как работает!

Пробежим быстренько по основным регистрам для работы с АЦП. Хоть мы и не будем с ними взаимодействовать напрямую, так как используем библиотеку Standard Peripheral Library, все равно надо уметь работать непосредственно с регистрами. Не буду переписывать весь даташит, это скучно как писать, так и читать потом 🙂 Расскажу про основные:

  • Регистр ADC_SR. Статусный регистр. Тут хранятся флаги – например, флаг, сигнализирующий об окончании преобразования.
  • Регистр ADC_CR1. Контрольный регистр. Тут всякие биты, разрешающие или запрещающие те или иные события. Например, включение Analog Watchdog для регулярных каналов, аналогично для инжектированных, включение различных прерываний – все это сидит здесь, в этом регистре.
  • Регистр ADC_CR2. Еще один контрольный регистр. А что вы хотели? Много режимов, нужно больше контроля 🙂 Здесь спрятались биты, отвечающие за использование АЦП совместно с DMA, а также за запуск преобразования от внешнего источника. Кроме того, тут можно разрешить или запретить использование датчика температуры. Важный бит – SWSTART, который запускает преобразование регулярных каналов.
  • Регистры ADC_SMPRx отвечают за время выборки.
  • В ADC_SQR и ADC_JSQR выбираем номера нужных нам каналов.
  • И, наконец, регистр данных – ADC_DR – там и только там мы будем забирать наши драгоценные, полученные с огромным трудом результаты!

Давайте-ка перейдем к практике. Ставим задачу, это, кстати, самое главное. Порой гораздо сложнее поставить задачу, чем ее решить 🙂 Будем брать на входе данные, и прогонять их через аналого-цифровой преобразователь. Банально, так что немного усложним. Запускать преобразование будем от внешнего источника, а точнее от таймера, а точнее от события, вызываемого переполнением таймера! Вот такой вот будем делать пример.

Открываем файлы stm32f10x_adc.c и stm32f10x_adc.h и ищем что-нибудь, что нам может помочь. И очень быстро в структуре ADC_InitTypeDef находим интересное поле uint32_t ADC_ExternalTrigConv. То что надо! Но что же туда записать? Идем дальше и находим такой кусок:

#define ADC_ExternalTrigConv_T1_CC1                              ((uint32_t)0x00000000)
#define ADC_ExternalTrigConv_T1_CC2                              ((uint32_t)0x00020000)
#define ADC_ExternalTrigConv_T2_CC2                              ((uint32_t)0x00060000)
#define ADC_ExternalTrigConv_T3_TRGO                             ((uint32_t)0x00080000)
#define ADC_ExternalTrigConv_T4_CC4                              ((uint32_t)0x000A0000)
#define ADC_ExternalTrigConv_Ext_IT11_TIM8_TRGO                  ((uint32_t)0x000C0000)

Это ни что иное, как возможные значения поля ADC_ExternalTrigConv. Мы хотим прерывание по переполнению таймера 3, например, но, тысяча чертей, тут такого нет. Зато есть что-то таинственное под названием ADC_ExternalTrigConv_T3_TRGO. А это такая фишка – Trigger Output Mode. То есть в качестве внешнего события мы можем выбрать то, что нам нужно, но это уже в настройках таймера. Так что идем ковырять файл stm32f10x_tim.c и находим там функцию TIM_SelectOutputTrigger (TIM_TypeDef* TIMx, uint16_t TIM_TRGOSource). В списке ее возможных аргументов без труда находим нужный – TIM_TRGOSource_Update. Вот и все! Мозговой штурм окончен 🙂 Переходим к реализации примера.

Не буду приводить полный код, includ’ы и defin’ы всякие, только суть:

// Тактирование, тактирование и еще раз тактирование
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

// Инициализируем таймер
TIM_TimeBaseStructInit(&timer);
timer.TIM_Prescaler = 720;
timer.TIM_Period = 50;
TIM_TimeBaseInit(TIM3, &timer);
ADC_StructInit(&adc);

// Режим – от триггера(внешнего события)
adc.ADC_Mode = ADC_Mode_AlterTrig;
adc.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T3_TRGO;
ADC_Init(ADC1, &adc);

// PA0 – вход АЦП
GPIO_StructInit(&port);
port.GPIO_Mode = GPIO_Mode_AF_PP;
port.GPIO_Pin = GPIO_Pin_0;
port.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOA, &port);

Про инициализацию таймеров, а также портов GPIO можно прочитать в более ранних статьях:

/***************************************************************************************/
int main()
{
	__enable_irq();
	initAll();
	
	// Включаем таймер и прерывание
	TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
	TIM_Cmd(TIM3, ENABLE);
	TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_Update);
	
	// Включаем преобразование АЦП от внешнего события
	ADC_ExternalTrigConvCmd(ADC1, ENABLE);
	NVIC_EnableIRQ(TIM3_IRQn);
	
	while(1)
	{
		// Вечное безделие…
		__NOP();
	}
}


/***************************************************************************************/
// Тут просто тупим и зачищаем флаг
void TIM3_IRQHandler()
{
	TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
}


/***************************************************************************************/

Отлично! Программа готова. Но что мы вообще измерять то собрались? Откуда брать сигнал? А в этом нам снова поможет язык сценариев Keil’а. Создаем в папке проекта(!) файл adc.ini и пишем в него:

/***************************************************************************************/
signal void adc(void) 
{
	float f;
	
	for (f = 0.0; f < 3.3; f += 0.1)
	{
		swatch (0.1);				  
		ADC1_IN0 = f;				 
	}
}
/***************************************************************************************/

То есть эмулируем изменяющийся со временем, но не непрерывно, входной сигнал на нулевом канале АЦП1.

Запускаем отладчик. Заходим в View – Serial Windows – ADC и выбираем ADC1. Находим регистр Data  в открывшемся окне, тут то мы и будем отслеживать результат преобразования. Пишем в командной строке:

include adc.ini

Запускаем программу и после этого запускаем функцию adc(). Подробнее про язык сценариев – в статье отладка программы в Keil.

Пример программы для ADC в STM32.

Видим, как меняются данные в регистре данных.  Программа работает правильно, так что можем переходить к дальнейшему изучению STM32! Продолжение следует…

Поделиться!

Подписаться
Уведомление о
guest
28 Комментарий
старее
новее большинство голосов
Inline Feedbacks
View all comments
Алиса Алексеева
7 лет назад

=) от схемы – искренне получила удовольствие:) вот еще у Вас здесь погуляю, может и другие слова пойму:)

Владимир
7 лет назад

У меня такой вопрос — можно ли в STM32 объеденять каналы в дифиренциальные пары (как в AVR) и измерять напряжение между этими каналами?
Если можно — то как?
Спасибо.

Владимир
Reply to  Aveal
7 лет назад

вот задача:
мерять ток на шунте при заряде и разряде аккумулятора.
Так как меняется знак тока — приходится применять ОУ в режиме дифф-усилителя с искуственной средней точкой. Ну или использовать спец. микросхему вроде ACS712.
На attiny261 можно сделать очень красиво — но только один канал, а нужно 4 канала и stm32f100 здесь смотриться очень заманчиво…

ATH
ATH
6 лет назад

Изучал ваш код, заметил: //PA0 – вход АЦП
GPIO_StructInit(&port);
port.GPIO_Mode = GPIO_Mode_AF_PP;
port.GPIO_Pin = GPIO_Pin_0;
port.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOA, &port);
Вроде надо GPIO_Mode_AN

Pashtet
Pashtet
6 лет назад

Здравствуйте, у меня вопрос. Короче в программирования контроллеров я новичок, сейчас разбираюсь с АЦП, написал программу на STM32F103ZE которая должна измерять напряжение которое подается на вход АЦП. Все норм компилится, но когда дэбажу код то вижу что никакого результата я не получаю, что это может быть???
Вот код:
#include “stm32f10x.h”
#include “stm32f10x_adc.h”
#include “stm32f10x_gpio.h”
#include “stm32f10x_rcc.h”

GPIO_InitTypeDef port;
ADC_InitTypeDef adc;

void leds_init ()
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_StructInit(&port);
port.GPIO_Mode = GPIO_Mode_AF_PP;
port.GPIO_Pin = GPIO_Pin_1;
GPIO_Init(GPIOA, &port);
}
void adc_init ()
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
ADC_StructInit(&adc);
adc.ADC_Mode = ADC_Mode_Independent;
adc.ADC_ContinuousConvMode = DISABLE;
adc.ADC_DataAlign = ADC_DataAlign_Right;
adc.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
adc.ADC_ScanConvMode = DISABLE;
adc.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &adc);
ADC_Cmd(ADC1, ENABLE);
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));
}
u16 readADC1(u8 channel)
{
ADC_RegularChannelConfig(ADC1, channel, 1, ADC_SampleTime_1Cycles5);
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
return ADC_GetConversionValue(ADC1);
}
void Delay(unsigned int Val)
{
for (; Val !=0; Val–);
}
int main(void)
{
leds_init();
adc_init();
do{
unsigned int bin_code = readADC1(ADC_Channel_1);
double voltage = bin_code*2.96/0xfff;
Delay(500000);
}while(1);
}

Pashtet
Pashtet
6 лет назад

Нет все норм работает, только не возвращает значение bin_code вследствие и voltage почему незнаю, может нужно попробывать сгенерировать сигнал как у вас в примере, может после етого он будет хоть чтото подсчитывать.

Евгений
Евгений
6 лет назад

хотелось бы еще примерчик с инжектированным каналом АЦП

Alfis
Alfis
6 лет назад

//МК stm32f10x. PC0,PC1,PC4 соответствуют каналом АЦП 10, 11, 14. Данные после запука программы можно найти в Регистрах данных JDR1,JDR2,JDR3 в ADC1 (во время прошивки железа). Работает (но если записать в периуд DataConvX то TIM6 работать небудет!)
Я подовал на вход напряжение 3.3В через переменный резисто! Меняя сопротивление мы изменяем периуд моргания диодов. Хотя и без этого АЦП считовал сигнал!
// Пример с инжекторными каналоми
#include “stm32f10x.h”
#include “stm32f10x_rcc.h”
#include “stm32f10x_gpio.h”
#include “stm32f10x_tim.h”
#include “stm32f10x_adc.h”
#include “misc.h”

void ADCsInj_ini(void) //АЦП 1
{
//#1
GPIO_InitTypeDef port;
ADC_InitTypeDef adc;
TIM_TimeBaseInitTypeDef timer;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOС, ENABLE);

GPIO_StructInit(&port);
port.GPIO_Pin = GPIO_Pin_1GPIO_Pin_0| GPIO_Pin_4;
port.GPIO_Mode = GPIO_Mode_AIN;
port.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_Init(GPIOС, &port);

//Таймер для запуска
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
TIM_TimeBaseStructInit( &timer);
timer.TIM_Prescaler = 2400-1;// Такт 100мкс
timer.TIM_Period = 5000;//0.5с
TIM_TimeBaseInit(TIM3, &timer);
//АЦП
RCC_ADCCLKConfig( RCC_PCLK2_Div8);//Делет частоту МК на 8
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

ADC_StructInit( &adc);
adc.ADC_Mode = ADC_Mode_Independent;
adc.ADC_ScanConvMode = ENABLE; //Включаем сканирование каналов
adc.ADC_ContinuousConvMode = DISABLE;//Включено разовое выполнение
adc.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T3_TRGO;// Внешний тригер запуска
adc.ADC_DataAlign = ADC_DataAlign_Right;
adc.ADC_NbrOfChannel = 3;// число каналов
ADC_Init(ADC1, &adc);

//#2
// Задаем число коналов с которых снимаем сигнал не более 4
ADC_InjectedSequencerLengthConfig(ADC1, 3);
// Приписываем каналу регистр данных JDRx в который будут записыватьс наши данные, где х 3-й параметр. Ну и задаем скорость отцыфроки для каждого канала
ADC_InjectedChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_71Cycles5);
ADC_InjectedChannelConfig(ADC1, ADC_Channel_11, 2, ADC_SampleTime_71Cycles5);
ADC_InjectedChannelConfig(ADC1, ADC_Channel_14, 3, ADC_SampleTime_71Cycles5);
//Запускаем АЦП от Тригера (из adc.ADC_ExternalTrigConv)
ADC_ExternalTrigConvCmd(ADC1, ENABLE);
ADC_AutoInjectedConvCmd(ADC1, ENABLE);//Авто запуск инжекторных каналов после регулярных

ADC_ITConfig(ADC1, ADC_IT_JEOC, ENABLE);
ADC_Cmd(ADC1, ENABLE);
NVIC_EnableIRQ(ADC1_IRQn);

// Настройка TIM3 который запускает АЦП
TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_Update);
TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
TIM_Cmd(TIM3, ENABLE);
NVIC_EnableIRQ( TIM3_IRQn);

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

void TIMBasT_ini(void)//Тамер для моргания диодами
{
TIM_TimeBaseInitTypeDef timer;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
// RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);

TIM_TimeBaseStructInit( &timer);
timer.TIM_Prescaler =24000 – 1;
timer.TIM_Period = 500; 0.5мс
TIM_TimeBaseInit(TIM6, &timer);

TIM_ITConfig(TIM6, TIM_IT_Update, ENABLE);
TIM_Cmd(TIM6, ENABLE);
NVIC_EnableIRQ(TIM6_DAC_IRQn);
}

uint16_t DataConvX, DataConv;//extern DataConvX, DataConvY, DataConvZ,
uint8_t ADC_Data_TX;
uint8_t i;

int main(void)
{
__enable_irq ();
ADCsInj_ini();
Ports_ini();
TIMBasT_ini();
GPIO_SetBits(GPIOC,GPIO_Pin_8);
GPIO_SetBits(GPIOC,GPIO_Pin_9);
ADC_Data_TX = 0;
i=0;
while (1)
{
__NOP();
}
}

//Таймер для управления периудом переключений свето диодов
void TIM6_DAC_IRQHandler (void)
{
if (ADC1->JDR1 != 0x0000)// без этого условия АЦП записывае в периуда 0х0000 (как я понил таймер уходит в бесконечное прирывани) и перстает переключать диоды
{
if (TIM_GetITStatus(TIM6,TIM_IT_Update)!= RESET)
{
TIM_TimeBaseInitTypeDef timer;
//TIM_TimeBaseStructInit( &timer);
timer.TIM_Prescaler = 24000-1;
timer.TIM_Period =(uint16_t) (ADC1->JDR1);//записываем периуд из регистра ADC1->JDR1
TIM_TimeBaseInit(TIM6, &timer);
GPIO_Write(GPIOC, GPIO_ReadOutputData(GPIOC)^(GPIO_Pin_8|GPIO_Pin_9));//моргание
TIM_ClearITPendingBit(TIM6, TIM_IT_Update);
}
}
else
{
//Выключаем регисты
GPIO_ResetBits(GPIOC,GPIO_Pin_8);
GPIO_ResetBits(GPIOC,GPIO_Pin_9);
}
}

void TIM3_IRQHandler(void)
{
if (i== 2) //Каждые 2 прерывания запукается АЦП
{
i=0;
ADC_Data_TX = 0;
ADC_ExternalTrigConvCmd(ADC1, ENABLE); //Запуск АЦТ от Тригера TIM3 (очень важная функция)
}
if (TIM_GetITStatus(TIM3,TIM_IT_Update)!= RESET)
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
i++;
}
}

void ADC1_IRQHandler(void)
{
if (ADC_Data_TX != 1)
{
if (ADC_GetITStatus(ADC1, ADC_IT_JEOC)!= RESET)
{
ADC_ClearITPendingBit(ADC1, ADC_IT_JEOC);
DataConvX = ADC_GetInjectedConversionValue(ADC1, ADC_InjectedChannel_1);
DataConvY = ADC_GetInjectedConversionValue(ADC1, ADC_InjectedChannel_2);
DataConvZ = ADC_GetInjectedConversionValue(ADC1, ADC_InjectedChannel_3);
ADC_Data_TX = 1; //Типа флага что даннае пришли
ADC_ExternalTrigConvCmd(ADC1, DISABLE);
}
}
}

Alfis
Alfis
6 лет назад

//Еще добавьте фукцию для свето диодов
void Ports_ini(void)
{
GPIO_InitTypeDef port;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);

GPIO_StructInit(&port);
port.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_9;
port.GPIO_Mode = GPIO_Mode_Out_PP;
port.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOC, &port);
}

Alfis
Alfis
6 лет назад

Подробнее можно посмотреть на сайте по данной ссылке http://amberclan.clan.su/publ/uroki_po_programmirovaniju_stm32/urok_5_adc/7-1-0-93

Александр
Александр
6 лет назад

Скажите пожалуйста, почему не работает программа?
Заранее спасибою
#include
#include

#define LED_PORT GPIOD
#define LED_GREEN (1<<12)
#define LED_ORANGE (1<<13)
#define LED_RED (1<<14)
#define LED_BLUE (1< AHB1ENR |= RCC_AHB1ENR_GPIODEN;
RCC -> AHB1ENR |= RCC_AHB1ENR_GPIOAEN;

GPIOA -> MODER &= ~GPIO_MODER_MODER0;
LED_PORT -> MODER |= GPIO_MODER_MODER12_0 | GPIO_MODER_MODER13_0 | GPIO_MODER_MODER14_0 |GPIO_MODER_MODER15_0;

}

void switch_led_off(void)
{
LED_PORT->ODR &= ~GPIO_ODR_ODR_15;
}

void switch_leds_off(void)
{
LED_PORT->ODR &= ~GPIO_ODR_ODR_12;
LED_PORT->ODR &= ~GPIO_ODR_ODR_13;
LED_PORT->ODR &= ~GPIO_ODR_ODR_14;
LED_PORT->ODR &= ~GPIO_ODR_ODR_15;
}

void setup_adc (void)
{

ADC_InitTypeDef ADC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
ADC_CommonInitTypeDef ADC_CommonInitStructure;// adc_init
ADC_DeInit();

RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
//
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
/* ???????? ?????????????? ?????????, ? ?? ?? ???????????? ???????? */
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConvEdge_None;
ADC_InitStructure.ADC_NbrOfConversion = 1;
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC_Init(ADC1, &ADC_InitStructure);

// ????? ??????
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, 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_DiscModeCmd(ADC1, DISABLE);
ADC_EOCOnEachRegularChannelCmd(ADC1, DISABLE);
ADC_DMARequestAfterLastTransferCmd(ADC1, DISABLE);
ADC_DMACmd(ADC1, DISABLE);

// ???????? ???
ADC_Cmd(ADC1, ENABLE);
}
int main(void)
{
int a=0, x=0;
setup_leds();
setup_adc ();

while(1)
{
ADC_RegularChannelConfig(ADC1,ADC_Channel_1, 1, ADC_SampleTime_3Cycles);
ADC_SoftwareStartConv(ADC1);
while (x3000)
{
LED_PORT->ODR = LED_BLUE | LED_ORANGE | LED_RED | LED_GREEN;
while (x<1000000)
{
x++;
}x=0;
}

if (a<2000)
{
switch_leds_off();
while (x<1000000)
{
x++;
}x=0;
}

}
}

Александр Смирнов
Александр Смирнов
5 лет назад

Есть вопрос. Стоит задача выходной сигнал микрофона преобразовать в цифровой вид, а затем подать на вход следующего каскада приемопередатчика. Посоветуйте, пожалуйста, микросхемку ацп с логарифматором, позволяющую произвести такую операцию.

Zender12
Zender12
5 лет назад

Именно на проверке флага происходит зависание, подскажите в чём проблема могу скинуть свой код

Виталий
Виталий
5 лет назад

1) Я правильно понял из datasheet, что если я беру контроллер любой 100-й серии в корпусе с 64 выводами и менее, я не найду у него снаружи ножек для подключения опорного напряжения, там вроде как эти ножки соединены с ножками аналогового питания???
2) Где вообще можно посмотреть цоколёвку на конкретный микроконтроллер, скажем развожу я плату под STM32F100RB6, на какую ножку подавать питание на какую кварц вешать? В основном конечно про питание интересует, в datasheet что-то нет распиновки, как например для тех же ATmega?
3) НУ и самое интересное, что в голове у меня не укладывается, перерыл satasheet но не нашёл, если брать АЦП в той же ATmega, то для него есть формула, которая позволяет определить входное напряжение, зная опорное значение напряжения и разрядность АЦП. А что в свои регистры АЦП записывает STM???? Это уже будут 12 разрядов измеренного напряжения с учётом всех коэффициентов, мне его останется перевести только в десятичный вид? или что?

Виталий
Виталий
5 лет назад

Да действительно, нашёл satasheet, там есть полезная для меня информация, буду разбираться, спасибо

Алексей
Алексей
5 лет назад

Подскажите пожалуйста, что и как настраивать если надо проводить измерения напряжения по 6 каналам одновременно? Если мне надо делать измерения по всем 6 каналам одновременно по переполнению таймера, то как я пойму результат измерения какого канала я забираю из регистра данных? Регистр то один, а каналов несколько. Или так вообще не может быть, и надо перед каждым измерением перенастраивать АЦП на измерение с другого канала по очереди?

Андрей
Андрей
5 лет назад

Микроконтроллер STM32L151CBT6. Нужно сделать программу обслуживания АЦП. Канал один всего используется, но начало преобразования должно происходить при нажатии на кнопку (к одному из выводов ее цепляем), а конец по истечению 3 секунд, то есть таймер нужен, как понимаю. Можете подсказать, пожалуйста, как это осуществить? Желательно с примером кода.

jura
jura
5 лет назад

У меня сейчас та же проблема. Если Вы разобрались, не будете ли любезны и мне подсказать. На мыло Shchavinsky@inbox.ru
Заранее спасибо

Alex
Alex
3 лет назад

А почему может не быть такого VTREG как ADC1_IN1 в кейле? Пробовал на разных камнях stm32f103.

Alex
Alex
Reply to  Alex
3 лет назад

Ответ кроется тут http://www.keil.com/support/docs/3726.htm.

Присоединяйтесь!

Profile Profile Profile Profile Profile
Vkontakte
Twitter

Язык сайта

Июль 2020
Пн Вт Ср Чт Пт Сб Вс
« Июн    
 12345
6789101112
13141516171819
20212223242526
2728293031  

© 2013-2020 MicroTechnics.ru