Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии


STM32 с нуля. Таймеры. Генерация ШИМ.



Posted on 19.01.2013

В прошлой статье (вот тут) мы рассмотрели в общих чертах таймеры в STM32 и написали простенькую программку. Теперь, как и обещал, поподробнее покопаем генерацию ШИМ при помощи все того же таймера TIM4.

Итак…

Честно говоря, писать то особо нечего) Думаю многие знают что такое ШИМ и с чем его едят, а если нет то об этом можно прочитать, например, в Википедии, так что нет, наверное, смысла отдельно описывать то, что уже многократно и хорошо описано =) Давайте писать пример для генерации ШИМ. Просто замутить такой сигнал не так интересно, так что давайте хоть немного усложним задачу. Будем генерировать ШИМ с разным заполнением в зависимости от состояния кнопки. Если кнопка нажата – генерируем сигнал с периодом 2.5 мс и заполнением 1.5 мс, а если кнопка не нажата – то период – 2.5 мс, заполнение – 0.5 мс. Ничего сложного )

Так что создаем новый проект и пишем код. Для начала набор includ’ов:

#include " stm32f10x.h" #include " stm32f10x_rcc.h" #include " stm32f10x_gpio.h" #include " stm32f10x_tim.h"

Добавили все файлы, необходимые нам для работы.
Объявим переменные:

#define TIMER_PRESCALER 720#define EXT_TIM_PULSE 150#define TIM_PULSE 50

TIMER_PRESCALER мы уже упоминали — это предделитель частоты.
EXT_TIM_PULSE – увеличенная длительность заполнения (то есть при нажатой кнопке), аналогично TIM_PULSE – обычная длительность (кнопка не нажата).
Продолжаем:

uint16_t previousState; GPIO_InitTypeDef port; TIM_TimeBaseInitTypeDef timer; TIM_OCInitTypeDef timerPWM; uint16_t buttonPreviousState;

Просто переменные, которые нам понадобятся в проекте. Пока все понятно )

И вот наконец-то кое-что поинтереснее, а именно наша разросшаяся функция initAll ().

void initAll(){ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); GPIO_StructInit(& port); port.GPIO_Mode = GPIO_Mode_AF_PP; port.GPIO_Pin = GPIO_Pin_6; port.GPIO_Speed = GPIO_Speed_2MHz; GPIO_Init(GPIOB, & port); port.GPIO_Mode = GPIO_Mode_IPD; port.GPIO_Pin = GPIO_Pin_3; port.GPIO_Speed = GPIO_Speed_2MHz; GPIO_Init(GPIOA, & port); TIM_TimeBaseStructInit(& timer); timer.TIM_Prescaler = TIMER_PRESCALER; timer.TIM_Period = 250; TIM_TimeBaseInit(TIM4, & timer); TIM_OCStructInit(& timerPWM); timerPWM.TIM_Pulse = 50; timerPWM.TIM_OCMode = TIM_OCMode_PWM1; timerPWM.TIM_OutputState = TIM_OutputState_Enable; TIM_OC1Init(TIM4, & timerPWM); }

Давайте прямо по строчкам смотреть, что же тут написано. Ну, вначале уже привычное включение тактирования необходимой периферии. Шестую ножку порта GPIOB настраиваем на работу в режиме альтернативной функции(! ). Лезем в даташит на контроллер и видим, что альтернативной функцией у этого вывода является первый канал таймера TIM4. То что надо )

Вывод PA3 настраиваем на вход – там будет наша воображаемая кнопка. Почему именно третья ножка? Да фиг его знает, просто так ) Итак, с инициализацией портов закончили, идем настраивать таймер. Поначалу все конфигурируем, как и в предыдущем проекте. А вот дальше что-то новенькое:

TIM_OCStructInit(& timerPWM); timerPWM.TIM_Pulse = 50; timerPWM.TIM_OCMode = TIM_OCMode_PWM1; timerPWM.TIM_OutputState = TIM_OutputState_Enable; TIM_OC1Init(TIM4, & timerPWM);

Для использования режима генерации ШИМ нам понадобилась структура TIM_OCInitTypeDef. ПолеTIM_Pulse – длительность заполнения, пусть будет сначала 50 тиков (0.5 мс). Далее задаем режим — TIM_OCMode_PWM1. Помимо PWM1 есть еще PWM2. Это всего лишь разные режимы ШИМ – с выравниванием по границе и по центру. В поле TIM_OutputState забиваем – Enable и инициализируем таймер. Готово!

Осталось немного )
Функция main() – как же без нее:

int main(){ __enable_irq (); initAll(); buttonPreviousState = 0; TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE); TIM_Cmd(TIM4, ENABLE); NVIC_EnableIRQ(TIM4_IRQn); while(1) { __NOP(); } }

Все, как и раньше – включаем прерывание по переполнению. Оно нам нужно, чтобы опрашивать кнопку именно в момент окончания периода ШИМ. Вот и сам код обработчика:

void TIM4_IRQHandler(){ uint16_t button = 0; button = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_3); TIM_ClearITPendingBit(TIM4, TIM_IT_Update); if ((button == 1) & & (buttonPreviousState == 0)) { TIM4-> CCR1 = EXT_TIM_PULSE; buttonPreviousState = 1; } if ((button == 0) & & (buttonPreviousState == 1)) { TIM4-> CCR1 = TIM_PULSE; buttonPreviousState = 0; }}

Опрашиваем кнопку, ничего нового, знакомая функция ) Возможно возникнет вопрос – зачем так извращаться «(button == 1) & & (buttonPreviousState == 0)»? А чтобы работа с регистром таймера шла не на каждое прерывание, а только когда состояние кнопки изменяется. Упомянутая работа с регистром заключается в прямой записи длительности заполнения в регистр таймера TIM_CCR.

Компилируем, идем в отладчик, запускаем программу и эмулируем нажатие кнопки на PA3. Если кто забыл, открываем окно General Purpose I/O A (GPIOA) и вручную выставляем бит на входе PA3, то есть говорим контроллеру, что кнопка типа нажата, давай действуй =) В окошке логического анализатора видим:

При изменении состояния кнопки меняется и скважность импульсов, как и задумывалось ) Ура, товарищи!

 

 

15) ЦАП.

 

Цифро-аналоговый преобразователь (ЦАП) — устройство для преобразования цифрового (обычно двоичного) кода в аналоговый сигнал (ток, напряжение или заряд). Цифро-аналоговые преобразователи являются интерфейсом между дискретным цифровым миром и аналоговыми сигналами.

Аналого-цифровой преобразователь (АЦП) производит обратную операцию.

Звуковой ЦАП обычно получает на вход цифровой сигнал в импульсно-кодовой модуляции (англ. PCM, pulse-code modulation). Задача преобразования различных сжатых форматов в PCM выполняется соответствующими кодеками.

 

16) АЦП.

 

Аналого-цифровой преобразователь[1][2][3] (АЦП, англ. Analog-to-digital converter, ADC) — устройство, преобразующее входной аналоговый сигнал в дискретный код (цифровой сигнал).

Обратное преобразование осуществляется при помощи цифро-аналогового преобразователя (ЦАП, DAC).

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

 

 

17) ОЗУ.

 

Операти́ вная па́ мять (англ. Random Access Memory, RAM, память с произвольным доступом; ОЗУ (оперативное запоминающее устройство); комп. жарг. память, оперативка) — энергозависимая часть системы компьютерной памяти, в которой во время работы компьютера хранится выполняемый машинный код (программы), а также входные, выходные и промежуточные данные, обрабатываемые процессором.

Обмен данными между процессором и оперативной памятью производится:

· непосредственно;

· через сверхбыструю память 0-го уровня — регистры в АЛУ, либо при наличии аппаратного кэша процессора — через кэш.

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

 

18) ПЗУ.

Постоя́ нное запомина́ ющее устро́ йство (ПЗУ) — энергонезависимая память, используется для хранения массива неизменяемых данных.

 

 

19) Инкрементальный энкодер.

Энкодер это всего лишь цифровой датчик угла поворота, не более того. Энкодеры бывают абсолютные — сразу выдающие двоичный код угла и инкрементальные, дающие лишь указание на направление и частоту вращения, а контроллер, посчитав импульсы и зная число импульсов на оборот, сам определит положение.

Импульсный (пошаговый) энкодеротносится к типу энкодеров, которые предназначены для указания направления движения и/или углового перемещения внешнего механизма. Пошаговый энкодер формирует импульсы, количество которых соответствует повороту вала на определенный угол. Этот тип энкодеров, в отличие от абсолютных, не формирует код положения вала, когда вал находится в покое.
Пошаговый энкодер связан со счетным устройством, это необходимо для подсчета импульсов и преобразования их в меру перемещения вала.

Пошаговый оптический энкодер состоит из следующих компонентов: источника света, диска с метками, фототранзисторной сборки и схемы обработки сигнала. Диск пошагового энкодера подразделен на точно позиционированные отметки. Количество отметок определяет количество импульсов за один оборот. К примеру, если диск поделен на 1000 меток, тогда за 250 импульсов вал должен повернуться на 90 градусов.

Диск с метками инкрементного энкодера

 

20) Матрица кнопок.

Матричная клавиатура

Начинающим 8 Сентябрь 2008 DI HALT 82 Comments

Допустим нам надо подавать команды нашему девайсу. Проще всего это делать посредством обычных кнопок, повешенных на порт. Но одно дело когда кнопок две три, и другое когда их штук двадцать. Не убивать же ради этого двадцать выводов контроллера. Решение проблемы есть — матрицирование. То есть кнопки группируются в ряды и столбцы, а полученная матрица последовательно опрашивается микроконтроллером, что позволяет резко снизить количество нужных выводов ценой усложнения алгоритма опроса.

Клавиатурная матрица.
Я ее нарисовал тебе на первой картинке. Как видишь, там есть строки и столбцы. Кружочками обозначены кнопки. Включены они так, что при нажатии кнопка замыкает строку на столбец.

Считывающий порт включается в режиме Pull-up входа, то есть вход с подтягивающими резисторами. Если контроллер это не поддерживает, то эти резисторы надо повесить снаружи.

Сканирующий порт работает в режиме выхода, он подключен к столбцам. Столбцы должны быть подтянуты резисторами к питанию. Впрочем, если используется полноценный Push-Pull то подтяжка не нужна — выход сам поднимет ногу на нужный уровень.

Работает следующим образом.

Матричная клавиатура
Расширение разрядности дешифратором
Используя счетчик

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

Теперь считываем сразу все значение из читающего порта. Если на столбце А не нажата ни одна кнопка, то в порту будут все единички. Но стоит нажать любую кнопку из столбца А, так она сразу же замкнет линию А, на этот вывод порта. В линии А у нас в данный момент 0, это обеспечивает ноль на сканирующем выводе контроллера. Поэтому и на соответствующем выводе порта будет 0
Так что, если будет нажата кнопка, например, 6, то на линии Р1 будет 0.

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

Можно определить одновременные нажатия многих кнопок — надо просто делать проверку не по байту, а по конкретному биту.

Увеличение разрядности
Но что делать если у нас кнопок не просто много, а очень много. Что даже матрицирование не спасает от огромного расхода линий порта. Тут приходится либо жертвовать несколько портов, либо вводить дополнительную логику. Например дешифратор с инверсным выходом.

Дешифратор, это такая микросхема, принимающая на вход двоичный код, а на выходе выдает единицу в выбранный разряд. Т.е. подали число «101» — получили «1» на выводе номер 5. Ну, а у инверсного дешифратора будет 0.

Можно пойти еще дальше и поставить микросхему счетчик, который дергать импульсом с порта, значение со счетчика прогонять через дешифратор. Таким образом, можно влепить сколько угодно выводов, хватило бы разрядности дешифратора. Главное учитывать на каком такте счетчика у нас будет какой столбец.

Если сканируется обычная клавиатура, нажимаемая человеком, то можно не заморачиваться на скорость опроса и сделать его в качестве побочного продукта, повесив на какое-нибудь левое прерывание. Достаточно чтобы клава опрашивалась хотя бы 10-20 раз в секунду. Этого уже достаточно, для комфортной работы.

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

Но микроконтроллер работает с такой скоростью, что успевает посчитать эти всплески как устойчивые состояния. Решить эту проблему можно аппаратно, с помощью RS триггера, так и программно — внеся небольшую задержку перед следующим опросом кнопки. Задержка подбирается такой, чтобы дребезг успел прекратиться к ее окончанию.

21)


Поделиться:



Популярное:

Последнее изменение этой страницы: 2017-03-11; Просмотров: 1824; Нарушение авторского права страницы


lektsia.com 2007 - 2024 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.036 с.)
Главная | Случайная страница | Обратная связь