В каждом доме найдется старый мобильный телефон, который можно с удовольствием выпотрошить и из деталек собрать что-нибудь нужное новое. Из старого телефона я забрал вибромоторчик, миниатюрный микрофон и пару светодиодов, решив собрать простейшего электронного таракана.
Для начала была мысль сделать на деталях от старой материнской платы, но мы не ищем легких путей. И знаем, что для того чтобы из топора сварить суп, нужно просто добавить воды, капусты, мяска и не забыть посолить хорошенько. Вот и к паре светодиодов, моторчику и микрофону, достаточно было добавить контроллер STM32F030 (у меня небольшой запас), несколько конденсаторов и резисторов, припаять это все на плату и за два вечера получился вот такой электронный таракан.
Контроллер улавливает звук с микрофона включает моторчик. Поскольку не требуется распознавание звука или какое-то преобразование, то используется простейшая схема подключения микрофона на двух резисторах и конденсаторе. Чувствительности 12 битного аналого-цифрового преобразователя STM32F030 вполне хватает, чтобы распознавать громкие звуки, например, хлопки по столу или щелканье пальцами рядом с “ухом” и не отвлекаться на простые разговоры. Ухо у таракана только одно (всего один микрофон был телефоне) и это ухо будет расположено у таракана на спине. Там же будет и его мозг. Энергию будем получать из стандартной батарейки CR2032, которая даст нам 3вольта, что для контроллера вполне достаточно, а вот держатель для батарейки я выпаял из той же материнской платы.
Схема
Кварца нет, для работы АЦП он не нужен. Есть выключатель и конденсаторы подавления помех. Как потом оказалось, ставил не зря. Микромоторчик создает очень сильные помехи по питанию, но схема все-таки работает.
Была мысль подключить кнопку для выбора режимов и дополнительный светодиод индикации, внизу на схеме, но в итоговом варианте не задействовал. Для программирования используется разъем SWD. Моторчик не проверял на потребление, но помня, что моторчики по определению не могут есть мало, сразу запитал его через транзистор W1P , которых на старой материнской плате огромное количество, а выпаивать SMD транзисторы при помощи паяльного фена чрезвычайно просто. Параллельно моторчкиу поставил диод, который необходим для предохранения транзистора от пробоя при остановке моторчика. Схема и плата проектировались в программе Eagle.
Плата делалась односторонней, но все-таки пришлось два проводка пустить поверху.
На STM32 даже в контроллерах начального уровня STM32F030F4 в которых всего 16 килобайт памяти есть 12 битный аналогово-цифровой преобразователь на 11 каналов. Хотя сам преобразователь всего один, можно считывать аналоговые данные с 11 ножек контроллера и преобразовывать их в цифровой сигнал. Единственное нужно помнить, что ног у контроллера в этом корпусе всего 20, поэтому либо мы используем их на вывод, либо на вход. В моем случае я задействовал ноги PA1 – для входа данных с микрофона, PA9 и PA10 для включения светодиодов- левого и правого “глаза”, PA6 для включения мотора.
12 битный преобразователь означает, что на входе мы получаем цифровые значения от 0 до 4095, что в программе и можно отслеживать. Я “подтянул” PA1 к питанию через внешний резистор в 10 килоом, что дало мне стабильное значение 4095 в случае, если на микрофоне нет звуков вообще. Если попытаться включить преобразователь на ноге без подтяжки, то значения будут случайным образом плясать в широких пределах, в моем случае это было от 1000 до 2500. Микрофон, подключенный по простой схеме, создает небольшое, но вполне достаточное для отслеживания падение напряжения. В моем случае ниже 4056 для довольно громких звуков. Это показывает, что чувствительность такой схемы очень низка, и лучше поставить усилитель, но для простого датчика на громкий хлопок этого вполне достаточно.
Для запуска АЦП на STM32 необходимо программно проделать следующее:
1.Сконфигурировать ноги для ввода-вывода
2. Включить тактирование АЦП
3. Откалибровать АЦП
4. Включить АЦП
5. Задать конфигурацию АЦП
6. Произвести измерения (либо считывать данные, если измерения проводятся постоянно)
Саму программу я творчески списал из примеров http://www.st.com/web/en/catalog/tools/FM147/CL1794/SC961/SS1743/PF260157?icmp=pf260157_pron_pr-stm32snippets_mar2014
Конфигурирование GPIO для всех нужных выводов
__INLINE void ConfigureGPIO(void)
{
/* (1) Enable the peripheral clock of GPIOA */
/* (2) Select output mode (01) on GPIOA */
RCC->AHBENR |= RCC_AHBENR_GPIOAEN; /* включить тактирование для GPIOA */
GPIOA->MODER = (GPIOA->MODER & ~(GPIO_MODER_MODER1|GPIO_MODER_MODER10|GPIO_MODER_MODER9|GPIO_MODER_MODER5| GPIO_MODER_MODER6)) \
| (GPIO_MODER_MODER1_0|GPIO_MODER_MODER10_0|GPIO_MODER_MODER9_0| GPIO_MODER_MODER5_0|GPIO_MODER_MODER6_0); /* включить режим вывода для всего подряд */
}
Используется внутренний кварц
__INLINE void SetClockForADC(void)
{
/* (1) Enable the peripheral clock of the ADC */
/* (2) Start HSI14 RC oscillator */
/* (3) Wait HSI14 is ready */
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; /* (1) */
RCC->CR2 |= RCC_CR2_HSI14ON; /* (2) */
while ((RCC->CR2 & RCC_CR2_HSI14RDY) == 0) /* (3) */
{
/* For robust implementation, add here time-out management */
}
}
Калибровка
__INLINE void CalibrateADC(void)
{
/* (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 */
}
__NOP();__NOP(); /* This 2 NOPs are to ensure 2 ADC Cycles
before setting ADEN bit */
}
Включение преобразователя
__INLINE void EnableADC(void)
{
/* (1) Enable the ADC */
/* (2) Wait until ADC ready */
ADC1->CR |= ADC_CR_ADEN; /* (1) */
while ((ADC1->ISR & ADC_ISR_ADRDY) == 0) /* (2) */
{
/* For robust implementation, add here time-out management */
}
}
Нужно включить только те каналы, которые необходимы
__INLINE void ConfigureADC(void)
{
/* (1) Select HSI14 by writing 00 in CKMODE (reset value) */
/* (2) Select the auto off mode */
/* (3) Select CHSEL01 */
/* (4) Select a sampling mode of 111 i.e. 239.5 ADC clk to be greater than 17.1us */
//ADC1->CFGR2 &= ~ADC_CFGR2_CKMODE; /* (1) */
ADC1->CFGR1 |= ADC_CFGR1_AUTOFF; /* (2) */
ADC1->CHSELR = ADC_CHSELR_CHSEL1; /* (3) */
ADC1->SMPR |= ADC_SMPR1_SMPR_0 | ADC_SMPR1_SMPR_1 | ADC_SMPR1_SMPR_2; /* (4) */
}
Теперь основной цикл, в котором считываем данные и реагируем на них
while (1) /* Loop till the measure is in the range */
{
//delay_ms(10);
/* Performs the AD converion */
ADC1->CR |= ADC_CR_ADSTART; /* start the ADC conversion */
while ((ADC1->ISR & ADC_ISR_EOC) == 0); /* wait end of conversion */
val = ADC1->DR; // получили данные из АЦП
if (val<4056){ // если меньше, значит на микрофон что-то прилетело
GPIOA->BSRR
= (1<<10); //включили один глаз PA10
GPIOA->BSRR
= (1<<9); // включили второй глаз PA9
GPIOA->BSRR
= (1<<6); // включили моторчик PA6
delay_ms(500); // подождали полсекунды, пусть поработает
GPIOA->BRR
= (1<<10); //выключили один глаз PA10
GPIOA->BRR
= (1<<9); // выключили второй глаз PA9
GPIOA->BRR = (1<<6); // выключить моторчик
delay_ms(200); // чуть подождали, поскольку звук от мотора по новой активирует микрофон
}
}
DisableADC();
while(1); /* Endless loop */
}
Еще, если двигатель не работает, то периодически подмаргиваем глазками
SysTick_Config(48000);/* 1ms config */
Здесь обработка
void SysTick_Handler(void)
{
static uint32_t long_counter = LONG_DELAY;
static uint32_t short_counter = SHORT_DELAY;
static uint16_t error_temp = 0;
if (long_counter– == 0)
{
if(error == 0)
{
/* the following instruction can only be used if no ISR modifies GPIOC ODR
either by writing directly it or by using GPIOC BSRR or BRR
else a toggle mechanism must be implemented using GPIOC BSRR and/or BRR
*/
//GPIOC->ODR ^= (1<<9);//toggle green led on PC9
long_counter = LONG_DELAY;
}
else if (error != 0xFF)
{
/* orange led blinks according to the code error value */
error_temp = (error << 1) – 1;
short_counter = SHORT_DELAY;
long_counter = LONG_DELAY << 1;
GPIOA->BSRR = (1<<10); //set orange led on PA10
GPIOA->BSRR = (1<<9); //set orange led on PA9
}
}
if (error_temp > 0)
{
if (short_counter– == 0)
{
//GPIOC->ODR ^= (1 << 8); //toggle orange led
GPIOA->ODR ^= (1<<10); //toggle
led on PA10
GPIOA->ODR ^= (1<<9); //toggle
led on PA9
short_counter = SHORT_DELAY;
error_temp–;
}
}
}
Исходники платы для Eagle можно скачать тут>>>
Полный проект исходников для CooCox выложен здесь>>>
В итоге, если добавить к паре светодиодов контроллер, то вполне можно собрать какую-нибудь игрушку. Смотрим видео еще раз