[Беспроводные технологии, Программирование микроконтроллеров, Разработка для интернета вещей, Производство и разработка электроники, Интернет вещей] ИК датчик движения на STM32

Автор Сообщение
news_bot ®

Стаж: 6 лет 9 месяцев
Сообщений: 27286

Создавать темы news_bot ® написал(а)
19-Сен-2020 02:31

Приветствую, в этой статье вы узнаете: как сделать датчик движения с использованием ИК диода и ИК приемника на STM32 с минимальным использованием ядра (т.е. с максимальной загрузкой периферии) на регистрах, используя таймеры.
В статье максимально подробно рассмотрены вопросы программирования. Материал рассчитан на начальный уровень подготовки, но также и подойдет для более опытных. Часть подробностей скрыта под спойлерами для снижения загруженности статьи. Оглавление:Общая картинаСхемы устройствРабота ИК диода и ИК приемникаКод для передатчика ИК сигналаКод для приемника ИК сигналаПолный кодДополнительноЗаключениеОбщая картинаИспользуются два устройства: передатчик ИК сигнала и приемник ИК сигнала.
Общий принцип работы следующий: передатчик ИК сигнала излучает сигнал инфракрасного диапазона длин волн, а приемник ИК сигнала их принимает. Между этими устройствами образуется «луч», пересечение которого каким-либо объектом фиксируется приемным устройством. В качестве ИК диода используется TSAL6200, в качестве ИК приемника – TSOP4856.
TSAL6200
TSOP4856Схемы устройствОпустим цепи питания и другие элементы реальных устройств и рассмотрим только необходимые.Передатчик ИК сигнала состоит из микроконтроллера STM32L151C8T6, полевого транзистора 2N7002, резистора 1 Ом и ИК диода TSAL6200. Ниже представлена электрическая схема передатчика.
Транзистор необходим для усиления тока, протекающего через ИК диод, так как выходной ток с пина МК ограничен (выходной ток с пина МК STM32L151C8T6 не более 25 мА, максимальный постоянный ток через ИК диод TSAL6200 100 мА).Выбор транзистора на ваше усмотрение. Здесь выбран транзистор 2N7002, потому что он дешевый и его характеристик достаточно для моего использования. Стоит выбирать транзистор с меньшим пороговым напряжением затвора (Gate Threshold Voltage), так как на затвор вы сможете подать напряжение не более напряжения питания без дополнительных цепей, в нашем случае 3.3 В.Точный расчет величины сопротивления резистора является трудным, конечно, существуют специальные формулы, но я предлагаю подобрать резистор опытным путем. Так в моем случае используется резистор 1 Ом, амплитуда тока цепи составляет 20 мА, дальность связи при таком токе достигает 14 м в зависимости от конфигурации передаваемого сообщения (количество импульсов в пачке, скважность, об этом ниже). Если вам требуется большая дальность, следует или подобрать резистор поменьше, или увеличить напряжение питания для диода, или выбрать транзистор с меньшим пороговым напряжением и меньшим падением напряжения на сток-истоке.Приемник ИК сигнала состоит из микроконтроллера STM32L151C8T6, ИК приемника TSOP4856, резистора 100 Ом и конденсатора 0.1 мкФ. Ниже представлена электрическая схема приемника.
Работа ИК диода и ИК приемникаПри протекании тока через диод TSAL6200 излучается ИК сигнал с длиной волны 940 нм. Это излучение необходимо промодулировать меандром с частотой 56 кГц (приемник настроен на данную частоту, см. даташит). Передавать следует пачки таких импульсов с определенной скважностью.Эти пачки принимается устройством TSOP4856. Пока принимается одна пачка, на выходном выводе приемника низкое значение напряжения, как только на входе нет импульсов, приемник поднимает выходной вывод к напряжению питания. Ниже на картинках представлены временные диаграммы данного процесса. На первой картинке показана работа приемника во время приема одной пачки. На второй картинке показана работа приемника при приеме множества пачек.
Остается только запрограммировать работу передатчика и приемника.Код передатчика ИК сигналаБудем использовать таймеры для формирования пачек импульсов с выводом сигнала на конкретный пин. Таким образом, ядро МК только инициализирует эти таймеры, а дальше может заниматься какой-нибудь другой работой.Выбор пина должен быть обоснован. Необходимо выбирать такой пин, к которому может быть подключен какой-либо канал (кроме четвертого) любого General-purpose таймера. В нашем случае выбран пин PB6, к нему подключается первый канал TIM4. Приемник же будет подключен к пину PB7, второй канал TIM4.
Будем использовать два таймера: TIM4 будет генерировать меандр с частотой 56 кГц, TIM2 будет управлять таймером TIM4, т.е. создавать пачки из меандра. TIM2 будет работать в режиме Master, TIM4 – Slave. Почему именно таймер TIM2? Он выбран исходя из таблицы даташита подключений таймеров друг к другу.
Приступим к написанию функции инициализации таймеров Tim_Init_Transmitter(). Код написан для пустого проекта. Сначала объявляем функцию, в теле main её инициализируем, а после мэйна прописываем уже саму функцию.
#include "main.h"
void Timer_Init_Transmitter(void);
int main(void)
{
   RCC->ICSCR |= RCC_ICSCR_MSIRANGE_6;   // MSI 4.194 MHz enable
   Timer_Init_Transmitter();
   while(1)
   {
   }
}
void Timer_Init_Transmitter(void)
{
}
RCC->ICSCR |= RCCICSCRMSIRANGE_6 - эта строчка просто устанавливает частоту 4.194 МГц МК. У вас может быть любая другая частота.Настраиваем пин. Включаем тактирование, включаем альтернативную функцию, выбираем скорость пина повыше (это не является необходимым), выбираем какую именно альтернативную функцию хотим подключить к пину. Под спойлерами описано все подробно.Подробнее про включение тактированияВ даташите находим регистр RCC_AHBENR. Нужно прописать «1» в поле GPIOBEN.
Для этого выбираем в библиотеке CMSIS строчку RCC_AHBENR_GPIOBEN, которая ставит данный бит в единицу.
И изменяем регистр AHBENR следующим образом:RCC->AHBENR |= RCC_AHBENR_GPIOBEN; //GPIO port B clock enableПодробнее про включение альтернативной функцииВ даташите находим регистр GPIOx_MODER. Нужно прописать «10» в поле MODER6 (для пина PB6).
Для этого выбираем в библиотеке CMSIS строчку GPIO_MODER_MODER6_1, которая ставит второй бит в единицу.
В дальнейшем все такие обозначения будут браться аналогично.И изменяем регистр MODER следующим образом:GPIOB->MODER |= GPIO_MODER_MODER6_1;   //Alternative function mode enableВключение высокой скорости для пина делается аналогично включению альтернативной функции.Подробнее про выбор альтернативной функцииОбозначение альтернативных функций представлено на картинке ниже.
Нам нужна альтернативная функция для TIM4 – это AF2. Стоит отметить, что существует два регистра для выбора альтернативных функций: нижний GPIOx_AFRL для пинов с номерами от 0 до 7 и верхний GPIOx_AFRH для пинов с номерами от 8 до 15. Однако, в библиотеке CMSIS определен сдвоенный регистр AFR[2], прописав в скобки «0», мы выбираем нижний регистр, прописав «1», выбираем верхний регистр.В даташите находим регистр GPIOx_AFRL.
Нам нужно прописать «0010» в поле AFRL6, для этого нам нужно число в шестнадцатеричной форме 0x2000000, «2» седьмая справа для пина PB6, потому что первые шесть цифр для пинов с номерами от 0 до 5.И изменяем регистр AFRL следующим образом:GPIOB->AFR[0] |= 0x2000000;     //Pin PB6 TIM4 alternative function AF2 enableПолучили следующий код для настройки пина внутри нашей функции:
void Timer_Init_Transmitter (void)
{
   //Settings for GPIO PB6
   RCC->AHBENR |= RCC_AHBENR_GPIOBEN;             //GPIO port B clock enable
   GPIOB->MODER |= GPIO_MODER_MODER6_1;           //Alternative function mode enable
   GPIOB->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR6_1;    //High speed
   GPIOB->AFR[0] |= 0x2000000;                    //Pin PB6 TIM4 alternative function AF2 enable
}
Теперь перейдем к настройке TIM4.Нам нужно выбрать прескейлер PSC (делитель частоты), рассчитать значения для регистров CCR1 (длительность импульса) и ARR (период импульсов), выбрать режим работы таймера ШИМ, выбрать входной управляющий сигнал от таймера TIM2, выбрать режим для Slave и подать выходной сигнал таймера на пин PB6.Включение тактирования осуществляется аналогично включению тактирования GPIO.Подробнее про выбор прескейлера PSC и расчет CCR1 и ARRВыберем значение преселлектора входной частоты равное 0, в этом случае частота тактирования таймера будет равна частоте МК.
И изменяем регистр PSC следующим образом: TIM4->PSC = 0; //Prescaler valueПерейдем к расчету ARR.
Для расчета ARR нам нужно поделить тактовую частоту таймера 4.194 МГц (у вас своя) на 56 кГц. Получаем 74,89, округляем до целого. Я округлил до 75. И вписываем в регистр ARR: TIM4->ARR = 75 //Auto-reload valueОсталось рассчитать CCR1.
Так как у нас простой меандр, то в регистр CCR1 при режиме ШИМ нужно указать половину от значения ARR:TIM4->CCR1 = 37;   //Capture/Compare 1 valueПодробнее про выбор режима работы ШИМ таймераТак как у нас первый канал, то используем регистр CCMR1. Нас интересует в этом регистре поле OC1M. Режим ШИМ позволяет настраивать длительность импульса при фиксированном периоде.
Выберем PMW mode 1, вписав две единицы во второй и третий биты, т.е. «110»:TIM4->CCMR1 |= TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2; //Output compare PMW mode 1 Подробнее про настройку подачи выходного сигнала на пин PB6В регистр CCER в поле CC1E нужно вписать «1», включив подачу выходного сигнала на пин.
Вписываем следующую строчку:TIM4->CCER |= TIM_CCER_CC1E; //OC3 signal is output on the corresponding pinПодробнее про выбор управляющего сигнала и режима Slave
Для управления TIM4 таймером TIM2 нужно выбрать входной сигнал ITR1. Для этого нужно вписать в поле TS регистра TIMx_SMCR «001». Также выберем режим для Slave, вписав «101» в поле SMS. В этом режиме пока входной сигнал ITR1 будет высоким, то TIM4 будет работать и выдавать меандр на пин, как только ITR1 станет низким, TIM4 выключается.
Для этого вписываем:TIM4->SMCR |= TIM_SMCR_TS_0;    //choosing ITR1TIM4->SMCR |= TIM_SMCR_SMS_0 | TIM_SMCR_SMS_2; //Gated ModeДобавим к уже существующему коду, и получим:
void Timer_Init_Transmitter (void)
{
   //Settings for GPIO PB6
   RCC->AHBENR |= RCC_AHBENR_GPIOBEN;             //GPIO port B clock enable
   GPIOB->MODER |= GPIO_MODER_MODER6_1;           //Alternative function mode enable
   GPIOB->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR6_1;    //High speed
   GPIOB->AFR[0] |= 0x2000000;                    //Pin PB6 TIM4 alternative function AF2 enable
   //Settings for TIM4 - Slave
   RCC->APB1ENR |= RCC_APB1ENR_TIM4EN;            //TIM4 clock enable
   TIM4->PSC = 0;                                 //Prescaler value
   TIM4->ARR = 75;                                //Auto-reload value
   TIM4->CCR1 = 37;                               //Capture/Compare 1 value
   TIM4->CCMR1 |= TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2; //Output compare PMW mode 1    enable
   TIM4->CCER |= TIM_CCER_CC1E;                   //OC3 signal is output on the corresponding output pin
   TIM4->SMCR |= TIM_SMCR_TS_0;                   //choosing ITR1
   TIM4->SMCR |= TIM_SMCR_SMS_0 | TIM_SMCR_SMS_2; //Gated Mode
   TIM4->CR1 |= TIM_CR1_CEN;                      //TIM4 enable
}
Включение TIM4 можно производить, а можно и нет, так как управляющий сигнал с TIM2, всё равно включит его.Теперь перейдем к настройке TIM2.Необходимо подать тактирование на таймер, рассчитать PSC, CCR1 и ARR, выбрать режим работы таймера ШИМ, выбрать какой сигнал будет выходным (входным управляющим для TIM4) и включить таймер.Включение тактирования аналогично, как и для таймера TIM4.RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;  //TIM2 clock enableПодробнее про расчет PSC, CCR1 и ARR для TIM2Так как по даташиту на диод в пачке следует использовать не менее 10 импульсов (на самом деле можно и меньше, но качество работы может быть не очень хорошим), то выберем тактирование таймера TIM2 в десять раз меньше, чем TIM4.
Как видим в формуле для расчета частоты тактирования таймера в знаменателе уже есть «+1», т.е. нам нужно записать в регистр PSC «9», чтобы получить частоту в 10 раз меньшую, чем частоту МК.TIM2->PSC = 9;    //Prescaler valueТеперь рассчитаем значение для CCR1: отправлять будем 10 импульсов, следовательно, нам нужно взять значение ARR для TIM4 (период одного импульса, это 75) и умножить на 10, т.е. получаем 750, однако, у нас есть прескейлер, который делит частоту на 10, т.е. нам нужно поделить 750 на 10, в итоге получаем снова 75 (как и в TIM4, но уже с другим прескейлером). Запишем это значение в регистр CCR1 таймера TIM2.TIM2->CCR1 = 75;  //Capture/Compare 1 valueПерейдём к расчету ARR: тут всё просто, допустим, хотим «стрелять» пачками со скважностью 11.2, при этом период излучения пачек будет около 2 мс (из расчета, что 1 мс это 4194000/1000 = 4194 тика, и умножаем на 2, получаем округлённо 8400, а с прескейлером 10, получаем 840 тиков), умножаем длительность пачки 75 на 11.2 и получаем 840, как видите, значения совпадают. Запишем это в регистр ARR.TIM2->ARR = 840;   //Auto-reload valueПо даташиту скважность должна быть не менее 2, но при таких значениях стабильность работы будет хуже, и такая передача данных годится только для коротких посылок. Для длинных посылок и больших пачек скважность должна быть больше 4.Режим работы таймера TIM2 точно такой же, как и у TIM4 - ШИМ.TIM2->CCMR1 |= TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2; //Output compare PMW mode 1 enableПодробнее про выбор выходного сигнала TIM2 (управляющего для TIM4)Найдем регистр TIMx_CR2.
Нам нужно подать сигнал с первого канала (мы же используем CCR1), на выход с таймера TIM2. Этим сигналом является OC1REF. Ниже на картинке можно найти комбинацию битов для этого сигнала – «100».В поле MMS необходимо вписать «1» в третий бит.TIM2->CR2 |= TIM_CR2_MMS_2;    //OC1REF signal is used as trigger output (TRGO)Теперь осталось включить TIM2, записав следующую строчку:TIM2->CR1 |= TIM_CR1_CEN;    //TIM2 enableВ таком случае при включении МК, диод начнет излучать. Если вы хотите включать диод в определенное время, тогда вам в нашей функции необходимо убрать две строчки с включением таймеров, и просто в нужный момент включить TIM2.Добавим эти строчки к предыдущим и получим:
void Timer_Init_Transmitter (void)
{
   //Settings for GPIO PB6
   RCC->AHBENR |= RCC_AHBENR_GPIOBEN;             //GPIO port B clock enable
   GPIOB->MODER |= GPIO_MODER_MODER6_1;           //Alternative function mode enable
   GPIOB->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR6_1;    //High speed
   GPIOB->AFR[0] |= 0x2000000;                    //Pin PB6 TIM4 alternative function AF2 enable
   //Settings for TIM4 - Slave
   RCC->APB1ENR |= RCC_APB1ENR_TIM4EN;            //TIM4 clock enable
   TIM4->PSC = 0;                                 //Prescaler value
   TIM4->ARR = 75;                                //Auto-reload value
   TIM4->CCR1 = 37;                               //Capture/Compare 1 value
   TIM4->CCMR1 |= TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2; //Output compare PMW mode 1 enable
   TIM4->CCER |= TIM_CCER_CC1E;                   //OC3 signal is output on the corresponding output pin
   TIM4->SMCR &= ~TIM_SMCR_TS;                    //clear bits
   TIM4->SMCR |= TIM_SMCR_TS_0;                   //choosing ITR1
   TIM4->SMCR &= ~TIM_SMCR_SMS;                   //clear bits
   TIM4->SMCR |= TIM_SMCR_SMS_0 | TIM_SMCR_SMS_2; //Gated Mode
   TIM4->CR1 |= TIM_CR1_CEN;                      //TIM4 enable
   //Settings for TIM2 - Master
   RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;            //TIM2 clock enable
   TIM2->PSC = 9;                                 //Prescaler value
   TIM2->ARR = 840;                               //Auto-reload value
   TIM2->CCR1 = 75;                               //Capture/Compare 1 value
   TIM2->CCMR1 |= TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2; //Output compare PMW mode 1 enable
   TIM2->CR2 |= TIM_CR2_MMS_2;                    //OC1REF signal is used as trigger output (TRGO)
   TIM2->CR1 |= TIM_CR1_CEN;                      //TIM2 enable
}
Теперь у нас ИК диод будет излучать пачки по 10 импульсов, у которых частота 56 кГц, со скважностью 11.2, т.е. с периодом следования пачек в 2 мс. Заметим, что момент пересечения каким-либо объектом внутри периода пачек не определен, т.е. мы можем судить о пересечении ИК луча только по отсутствию следующей пачки. Таким образом, погрешность измерения момента времени пересечения луча составляем 2 мс.Код приемника ИК сигналаДля приема ИК сигнала снова будем использовать таймеры, а точнее всего один. В микроконтроллерах STM32 таймеры могут быть не только Master или Slave, а могут быть Master/Slave, т.е. они могут управлять сами собой.В разделе статьи «Код передатчика ИК сигнала» мы уже выбрали пин PB7 для приемника ИК сигнала. Схему подключения см. выше. К этому пину подключается второй канал TIM4.Ниже на картинке представлена схема устройства таймера.
Из схемы видно, что у каждого канала есть вход и выход, у таймера есть входной управляющий сигнал TRGI, также показаны тактирование и устройство Trigger controller.Принцип работы для нашего проекта заключается в следующем: пусть наш таймер будет просто считать время, допустим, равное 5 периодам излучаемых пачек ИК сигнала, следовательно, в ARR будет число, равное 5 периодам пачек, т.е. 840 * 5 = 4200. Настроим таймер так, чтобы при приеме каждой пачки таймер перезагружался и начинал считать заново. При достижении таймером числа в регистре ARR таймер выдает прерывание, которое означает, что на вход уже как 5 периодов не приходит пачка, следовательно, луч пересекается каким-то объектом. Вот и вся работа таймера. Осталось настроить TIM4.Создадим новый проект и функцию инициализации таймера:
#iclude "main.h"
void Timer_Init_Receiver(void);
int main(void)
{
   RCC->ICSCR |= RCC_ICSCR_MSIRANGE_6;    // MSI 4.194 MHz enable
   Timer_Init_Receiver();
   while(1)
   {
   }
}
void Timer_Init_Receiver(void)
{
}
Необходимо настроить пин PB7: включить тактирование порта B, настроить альтернативную функцию пина, настроить скорость и выбрать какую конкретную альтернативную функцию подключить к пину. Всё, как и в предыдущем случае.
void Timer_Init_Receiver(void)
{
//Settings for GPIO PB7
  RCC->AHBENR |= RCC_AHBENR_GPIOBEN;          // GPIO port B clock enable
  GPIOB->MODER |= GPIO_MODER_MODER7_1;        // Alternative function mode enable
  GPIOB->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR7_1;  // High speed
  GPIOB->PUPDR |= GPIO_PUPDR_PUPDR7_0;        // pull-up PB7
  GPIOB->AFR[0] |= 0x20000000;                // Pin PB7 TIM4 alternative function AF2 enable
}
Все настройки аналогичны, как и для передатчика, только альтернативная функция немного отличается, «2» нужно вписать уже на восьмое место справа. Еще необходимо подтянуть пин к плюсу прописав строчку:GPIOB->PUPDR |= GPIO_PUPDR_PUPDR7_0;Теперь настроим TIM4.Включим тактирование таймера. Так как у нас второй канал, то будем использовать CCR2. Прескейлер приравняем к 9, чтобы частота тактирования таймера была как у TIM2 передатчика. Для приемника можно приравнять CCR2 и ARR. Как уже рассчитали ранее, вписываем в эти регистры 4200.RCC->APB1ENR |= RCC_APB1ENR_TIM4EN;   // TIM4 clock enableTIM4->PSC = 9;   // Prescaler valueTIM4->ARR = 4200;   // Auto-reload valueTIM4->CCR2 = 4200;    // Capture/Compare 2 valueДалее настроим режим работы таймера. Нам нужно, чтобы он просто считал, и всё. Для этого в даташите находим TIMx_CCMR1 и в поле OC2M вписываем «000», что соответствует Frozen mode. Изменим регистр, обнулив все биты:TIM4->CCMR1 &= ~TIM_CCMR1_OC2M;   // Frozen mode enableНастроим канал на выход, обнулив биты поля CC2S (более подробно написано в даташите), впишем нули в эти биты:TIM4->CCMR1 &= ~TIM_CCMR1_CC2S;   // Output modeТеперь обратимся к картинке со схемой устройства таймера (см. выше). Нам необходимо, чтобы входной сигнал с пина PB7 был управляющим для таймера TIM4, таким сигналом является TI2FP2. Проследите путь этого сигнала от TIMx_CH2 до TRGI. Чтобы выбрать данный сигнал в качестве управляющего, необходимо в регистр TIMx_SMCR в поле TS вписать «110». Сразу же выберем режим для Slave: Reset mode, вписав «100» в поле SMS. Для этого пропишем строчки:TIM4->SMCR |= TIM_SMCR_TS_1 | TIM_SMCR_TS_2;   // Choosing TI2FP2TIM4->SMCR |= TIM_SMCR_SMS_2;    // Reset modeТеперь нам нужно определиться, что будет являться триггером обновления таймера: передний или задний фронт входных импульсов (на самом деле не особо важно, но лучше выбрать по переднему фронту, в нашем случае, это фронт спада напряжения). Для этого нужно использовать пару битовых полей регистра CCER: CC2P и CC2NP, они работают в паре, более подробно написано о них в даташите.
Для нашего случая вписываем «1» в CC2P и «0» в CC2NP. Делаем это так:TIM4->CCER &= ~TIM_CCER_CC2NP;  // This bit is used in conjunction with CC2P.TIM4->CCER |= TIM_CCER_CC2P;    // Inverted/falling edgeРазрешим прерывание по переполнению. Для этого в регистре TIMx_DIER в поле CC2IE впишем «1».
Для этого впишем строчку:TIM4->DIER |= TIM_DIER_CC2IE;  // Capture/Compare 2 interrupt enableВключаем таймер строчкой:TIM4->CR1 |= TIM_CR1_CEN;   // TIM4 enableНе забываем разрешить глобальное прерывание:NVIC_EnableIRQ(TIM4_IRQn);   // TIM4 global Interrupt enableНаша функция инициализации таймера выглядит следующим образом:
void Timer_Init_Receiver(void)
{
//Settings for GPIO PB7
  RCC->AHBENR |= RCC_AHBENR_GPIOBEN;           // GPIO port B clock enable
  GPIOB->MODER |= GPIO_MODER_MODER7_1;         // Alternative function mode enable
  GPIOB->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR7_1;  // High speed
  GPIOB->PUPDR |= GPIO_PUPDR_PUPDR7_0;         // pull-up PB7
  GPIOB->AFR[0] |= 0x20000000;                 // Pin PB7 TIM4 alternative function AF2 enable
  //Settings for TIM4
  RCC->APB1ENR |= RCC_APB1ENR_TIM4EN;          // TIM4 clock enable
TIM4->PSC = 9;                                 // Prescaler value
TIM4->ARR = 4200;                              // Auto-reload value
TIM4->CCR2 = 4200;                             // Capture/Compare 2 value
  TIM4->CCMR1 &= ~TIM_CCMR1_OC2M;              // Frozen mode enable
  TIM4->CCMR1 &= ~TIM_CCMR1_CC2S;              // Output mode
  TIM4->CCER &= ~TIM_CCER_CC2NP;               // This bit is used in conjunction with CC2P.
  TIM4->CCER |= TIM_CCER_CC2P;                 // Inverted/falling edge
  TIM4->SMCR |= TIM_SMCR_TS_1 | TIM_SMCR_TS_2; // Choosing TI2FP2
  TIM4->SMCR |= TIM_SMCR_SMS_2;                // Reset mode
  TIM4->DIER |= TIM_DIER_CC2IE;                // Capture/Compare 2 interrupt enable
  TIM4->CR1 |= TIM_CR1_CEN;                    // TIM4 enable
  NVIC_EnableIRQ(TIM4_IRQn);                   // TIM4 global Interrupt enable
}
Теперь напишем обработчик прерываний:Первое что нужно сделать, это снять флаг в регистре статуса TIMx_SR:TIM4->SR &= ~TIM_SR_CC2IF;Далее можем делать, что хотим. Хоть ставить какой-нибудь флаг, хоть зажигать диод. Допустим будем зажигать светодиод. Настроим пин PB15 на выход, прописав в мэйне:GPIOB->MODER |= GPIO_MODER_MODER15_0;  // PB15 output modeНаш обработчик прерываний будет выглядеть следующим образом:
void TIM4_IRQHandler(void)
{
  TIM4->SR &= ~TIM_SR_CC2IF;
  GPIOB->ODR |= GPIO_ODR_ODR_15;  // Led red on
}
Готово! Теперь при пересечении ИК луча каким-либо объектом, на приемнике будет зажигаться светодиод.Полный кодКод передатчика ИК сигнала
void Timer_Init_Transmitter(void);
int main(void)
{
   RCC->ICSCR |= RCC_ICSCR_MSIRANGE_6;             // MSI 4.194 MHz enable
   Timer_Init_Transmitter();
   while(1)
   {
   }
}
void Timer_Init_Transmitter(void)
{
  //Settings for GPIO PB6
   RCC->AHBENR |= RCC_AHBENR_GPIOBEN;             //GPIO port B clock enable
   GPIOB->MODER |= GPIO_MODER_MODER6_1;           //Alternative function mode enable
   GPIOB->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR6_1;    //High speed
   GPIOB->AFR[0] |= 0x2000000;                    //Pin PB6 TIM4 alternative function AF2 enable
   //Settings for TIM4 - Slave
   RCC->APB1ENR |= RCC_APB1ENR_TIM4EN;            //TIM4 clock enable
   TIM4->PSC = 0;                                 //Prescaler value
   TIM4->ARR = 75;                                //Auto-reload value
   TIM4->CCR1 = 37;                               //Capture/Compare 1 value
   TIM4->CCMR1 |= TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2; //Output compare PMW mode 1 enable
   TIM4->CCER |= TIM_CCER_CC1E;                   //OC3 signal is output on the corresponding output pin
   TIM4->SMCR &= ~TIM_SMCR_TS;                    //clear bits
   TIM4->SMCR |= TIM_SMCR_TS_0;                   //choosing ITR1
   TIM4->SMCR &= ~TIM_SMCR_SMS;                   //clear bits
   TIM4->SMCR |= TIM_SMCR_SMS_0 | TIM_SMCR_SMS_2; //Gated Mode
   TIM4->CR1 |= TIM_CR1_CEN;                      //TIM4 enable
   //Settings for TIM2 - Master
   RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;            //TIM2 clock enable
   TIM2->PSC = 9;                                 //Prescaler value
   TIM2->ARR = 840;                               //Auto-reload value
   TIM2->CCR1 = 75;                               //Capture/Compare 1 value
   TIM2->CCMR1 |= TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2; //Output compare PMW mode 1 enable
   TIM2->CR2 |= TIM_CR2_MMS_2;                    //OC1REF signal is used as trigger output (TRGO)
   TIM2->CR1 |= TIM_CR1_CEN;                      //TIM2 enable
}
Код приемника ИК сигнала
#include “main.h”
void Timer_Init_Receiver(void);
int main(void)
{
   RCC->ICSCR |= RCC_ICSCR_MSIRANGE_6;         // MSI 4.194 MHz enable
  GPIOB->MODER |= GPIO_MODER_MODER15_0;        // PB15 output mode
   Timer_Init_Receiver();
   while(1)
   {
   }
}
void Timer_Init_Receiver(void)
{
//Settings for GPIO PB7
  RCC->AHBENR |= RCC_AHBENR_GPIOBEN;           // GPIO port B clock enable
  GPIOB->MODER |= GPIO_MODER_MODER7_1;         // Alternative function mode enable
  GPIOB->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR7_1;  // High speed
  GPIOB->PUPDR |= GPIO_PUPDR_PUPDR7_0;         // pull-up PB7
  GPIOB->AFR[0] |= 0x20000000;                 // Pin PB7 TIM4 alternative function AF2 enable
  //Settings for TIM4
  RCC->APB1ENR |= RCC_APB1ENR_TIM4EN;          // TIM4 clock enable
TIM4->PSC = 9;                                 // Prescaler value
TIM4->ARR = 4200;                              // Auto-reload value
TIM4->CCR2 = 4200;                             // Capture/Compare 2 value
  TIM4->CCMR1 &= ~TIM_CCMR1_OC2M;              // Frozen mode enable
  TIM4->CCMR1 &= ~TIM_CCMR1_CC2S;              // Output mode
  TIM4->CCER &= ~TIM_CCER_CC2NP;               // This bit is used in conjunction with CC2P.
  TIM4->CCER |= TIM_CCER_CC2P;                 // Inverted/falling edge
  TIM4->SMCR |= TIM_SMCR_TS_1 | TIM_SMCR_TS_2; // Choosing TI2FP2
  TIM4->SMCR |= TIM_SMCR_SMS_2;                // Reset mode
  TIM4->DIER |= TIM_DIER_CC2IE;                // Capture/Compare 2 interrupt enable
  TIM4->CR1 |= TIM_CR1_CEN;                    // TIM4 enable
  NVIC_EnableIRQ(TIM4_IRQn);                   // TIM4 global Interrupt enable
}
void TIM4_IRQHandler(void)
{
  TIM4->SR &= ~TIM_SR_CC2IF;
  GPIOB->ODR |= GPIO_ODR_ODR_15;               // Led red on
}
ДополнительноЕсли хотите, чтобы диод на приемнике светился, когда ИК сигнал принимается, и не светился, когда на входе приемника нет сигнала, то нужно немного изменить код для приемника.Нужно добавить глобальную переменную, например, написав между "инклюдами" и мэйном такую строчку:int StatusDiode = 0;   // 0 - diode is off, 1 - diode is onЭта переменная необходима для запоминания статуса светодиода: выключен или включен.Далее в функции инициализации таймера нужно изменить строчку с разрешением прерывания по переполнению на разрешение прерывания по триггеру, такой строчкой:TIM4->DIER |= TIM_DIER_TIE;  // Trigger interrupt enableИ последнее: изменим обработчик прерываний.Соответственно, если диод был выключен и у нас сработало прерывание по триггеру, то снимаем соответствующий флаг, включаем диод, выключаем разрешение прерывания по триггеру, включаем разрешение прерывания по переполнению, обнуляем счетчик (можно не делать этот пункт) и изменяем статус диода.Если диод был включен, делаем все наоборот: снимаем соответствующий флаг, выключаем диод, выключаем разрешение прерывания по переполнению, включаем разрешение прерывания по триггеру и изменяем статус диода.Стоит помнить, что счетчик не останавливается, если не выключить таймер, однако, при прерывании по переполнению он перезагружается (обновляется регистр CNT), а при прерывании по триггеру обновления не происходит, поэтому мы его и обнуляем.Получим такой обработчик прерываний:
void TIM4_IRQHandler(void)
{
  if (StatusDiode == 0)
  {
      TIM4->SR &= ~TIM_SR_TIF;
      GPIOB->ODR |= GPIO_ODR_ODR_15;         // Led red on
      TIM4->DIER &= ~TIM_DIER_TIE;           // Trigger interrupt disable
      TIM4->DIER |= TIM_DIER_CC2IE;          // Capture/Compare 2 interrupt enable
      TIM4->CNT = 0;
      StatusDiode = 1;
  }
  else
  {
      TIM4->SR &= ~TIM_SR_CC2IF;
      GPIOB->ODR &= ~GPIO_ODR_ODR_15;        // Led red off
      TIM4->DIER &= ~TIM_DIER_CC2IE;         // Capture/Compare 2 interrupt disable
      TIM4->DIER |= TIM_DIER_TIE;            // Trigger interrupt enable
      StatusDiode = 0;
  }
}
Полный код для приемника ИК сигнала
#include “main.h”
void Timer_Init_Receiver(void);
int main(void)
{
   RCC->ICSCR |= RCC_ICSCR_MSIRANGE_6;      // MSI 4.194 MHz enable
  GPIOB->MODER |= GPIO_MODER_MODER15_0;     // PB15 output mode
   Timer_Init_Receiver();
   while(1)
   {
   }
}
void Timer_Init_Receiver(void)
{
//Settings for GPIO PB7
  RCC->AHBENR |= RCC_AHBENR_GPIOBEN;           // GPIO port B clock enable
  GPIOB->MODER |= GPIO_MODER_MODER7_1;         // Alternative function mode enable
  GPIOB->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR7_1;  // High speed
  GPIOB->PUPDR |= GPIO_PUPDR_PUPDR7_0;         // pull-up PB7
  GPIOB->AFR[0] |= 0x20000000;                 // Pin PB7 TIM4 alternative function AF2 enable
  //Settings for TIM4
  RCC->APB1ENR |= RCC_APB1ENR_TIM4EN;          // TIM4 clock enable
TIM4->PSC = 9;                                 // Prescaler value
TIM4->ARR = 4200;                              // Auto-reload value
TIM4->CCR2 = 4200;                             // Capture/Compare 2 value
  TIM4->CCMR1 &= ~TIM_CCMR1_OC2M;              // Frozen mode enable
  TIM4->CCMR1 &= ~TIM_CCMR1_CC2S;              // Output mode
  TIM4->CCER &= ~TIM_CCER_CC2NP;               // This bit is used in conjunction with CC2P.
  TIM4->CCER |= TIM_CCER_CC2P;                 // Inverted/falling edge
  TIM4->SMCR |= TIM_SMCR_TS_1 | TIM_SMCR_TS_2; // Choosing TI2FP2
  TIM4->SMCR |= TIM_SMCR_SMS_2;                // Reset mode
  TIM4->DIER |= TIM_DIER_TIE;                  // Trigger interrupt enable
  TIM4->CR1 |= TIM_CR1_CEN;                    // TIM4 enable
  NVIC_EnableIRQ(TIM4_IRQn);                   // TIM4 global Interrupt enable
}
void TIM4_IRQHandler(void)
{
  if (StatusDiode == 0)
  {
      TIM4->SR &= ~TIM_SR_TIF;
      GPIOB->ODR |= GPIO_ODR_ODR_15;           // Led red on
      TIM4->DIER &= ~TIM_DIER_TIE;             // Trigger interrupt disable
      TIM4->DIER |= TIM_DIER_CC2IE;            // Capture/Compare 2 interrupt enable
      TIM4->CNT = 0;
      StatusDiode = 1;
  }
  else
  {
      TIM4->SR &= ~TIM_SR_CC2IF;
      GPIOB->ODR &= ~GPIO_ODR_ODR_15;          // Led red off
      TIM4->DIER &= ~TIM_DIER_CC2IE;           // Capture/Compare 2 interrupt disable
      TIM4->DIER |= TIM_DIER_TIE;              // Trigger interrupt enable
      StatusDiode = 0;
  }
}
ЗаключениеТаким образом, разработали датчик движения, основанный на работе пересечения каким-либо объектом ИК луча. Система состоит из двух устройств, имеет минимум элементов, проста в реализации, имеет низкую стоимость и высокое быстродействие. При этом ядро МК свободно и может использоваться для других нужд.
===========
Источник:
habr.com
===========

Похожие новости: Теги для поиска: #_besprovodnye_tehnologii (Беспроводные технологии), #_programmirovanie_mikrokontrollerov (Программирование микроконтроллеров), #_razrabotka_dlja_interneta_veschej (Разработка для интернета вещей), #_proizvodstvo_i_razrabotka_elektroniki (Производство и разработка электроники), #_internet_veschej (Интернет вещей), #_stm32, #_ir, #_ik (ик), #_diod (диод), #_datchik (датчик), #_dvizhenija (движения), #_tajmer (таймер), #_infrakrasnyj (инфракрасный), #_si (си), #_programmirovanie (программирование), #_besprovodnye_tehnologii (
Беспроводные технологии
)
, #_programmirovanie_mikrokontrollerov (
Программирование микроконтроллеров
)
, #_razrabotka_dlja_interneta_veschej (
Разработка для интернета вещей
)
, #_proizvodstvo_i_razrabotka_elektroniki (
Производство и разработка электроники
)
, #_internet_veschej (
Интернет вещей
)
Профиль  ЛС 
Показать сообщения:     

Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы

Текущее время: 26-Ноя 02:15
Часовой пояс: UTC + 5