Урок 12
HAL. Динамическая индикация
Продолжим работать с семисегментными индикаторами. Только работать мы будем уже сразу с несколькими индикаторами, чтобы нам получить на них числа не от 1 до 9, так как это не совсем интересно, а ещё и большие числа.
Только подключать мы их будем не к 8 ножкам порта каждый, а все имеющиеся индикаторы параллельно всего к восьми ножкам. В этом нам поможет динамическая индикация.
Динамическая индикация — это такой вид отображения информации с помощью светодиодных индикаторов, при котором показывается в определённый момент времени только одна цифра одного отдельно взятого индикатора, затем следующего, а предыдущий гасится, и так далее. Только показывается всё это с такой скоростью, что человеческий глаз (а точнее мозг) не успевает оценить, что цифры горят по очереди и ему кажется что цифры горят все вместе одновременно. Данный тип индикации позволяет сэкономить внушительное количество ножек портов.
Достигается это следующим образом. На все ножки всех индикаторов последовательно по очереди подаются уровни сначала одной цифры, предназначенной, например, для первого индикатора, потом также на все вместе, уровни цифры, предназначенной для второго индикатора и так далее. А иначе никак и не получится, ведь у них лапки все запараллелены. Только в то время, когда подаётся цифра для определённого индикатора, то на его катод, если он с общим катодом, подаётся низкий уровень, а на все остальные катоды — высокий, и уже следовательно остальные индикаторы светиться не будут. А если он с общим анодом, то подаётся на нужный анод высокое напряжение, а на все остальные — низкое и они также светиться не будут.
По динамической индикации у нас уже был урок для AVR, а кто занимается с STM, те уже более подготовленные товарищи, поэтому слишком подробно всё это рассказывать не имеет смысла.
Давайте посмотрим схему самого индикатора 4-разрядного 7-сегментного. У меня индикатор вот такого вот типа (нажмите на картинку для увеличения изображения)
У меня также индикатор с общим анодом, поэтому будем придерживаться вот этой схемы для написания кода.
Вот так вот всё у нас подключено к контроллеру (нажмите на картинку для увеличения изображения)
На схеме присутствуют несколько индикаторов, так ка я в программе схемо-рисования не нашел единого четырёхразрядного, но так даже понятно.
Общие аноды подключены через ключевые транзисторы в инверсном режиме. также применены токоограничивающие резисторы.
Вот так вот выглядит схема на практике (нажмите на картинку для увеличения изображения)
Также как и раньше проект создаем из LED_STAT, называем его LED_DYN.
Запускаем куб, включаем на выход ещё 4 порта для индикации PE3-PE6.
В настройках в Configuration вообще ничего не трогаем.
Генерируем проект.
Добавляем файл led.c
Собираем, прошиваем, смотрим.
У нас всё работает, но рабоает одновременно, так как мы пока общими анодами никак не управляем.
В главной функции main() изменим инициализацию лапок, чтобы индикаторы не светились
/* USER CODE BEGIN 2 */
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6
|GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10
|GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14,
GPIO_PIN_SET);
Ещё раз собираем, прошиваем, смотрим.
Закрываем проект, заходим в куб добавляем 6й таймер
Смотрим Clock Configuration – частота APB1 стоит 84. Оставляем её без изменений
Заходим в Configuration->TIM6
Выставляем параметр Prescaler 41999, Counter Period 2000. Должно быть равно 1 секунде
Включаем прерывания
Применяем изменения в таймере, генерируем проект и открываем его в Keil.
В файле main.c включаем старт таймера
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6
|GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10
|GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14,
GPIO_PIN_SET);
HAL_TIM_Base_Start_IT(&htim6);
В файле stm32f4xx_it.c находим функцию для прерывания TIM6_DAC_IRQHandler и пишем туда для проверки таймера
HAL_TIM_IRQHandler(&htim6);
/* USER CODE BEGIN TIM6_DAC_IRQn 1 */
HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6);
Всё это просто пока для эксперемента, чтобы проверить, работает ли вообще таймер.
Ещё раз собираем, прошиваем, смотрим.
У нас индикаторы мигают с периодом в 2 секунды, так как это функция переключения, и сначала индикаторы выключаются, через секунду включаются, а уж ещё через секунду выключаются опять. Поэтому мы видим, что циферки у нас мигают и показываются через одну. Я даже показывать это не буду, уж очень это некрасиво. Но вы, конечно, можете это увидеть в видеоверсии данного урока, которая прикреплена в самом низу данной страницы с уроком.
Закрываем проект, заходим в куб, меняем Counter Period на 500
Генерируем проект.
В файл stm32f4xx_it.c добавим переменную для счётчика разрядов, а заодно подключим туда файл led.h
#include «stm32f4xx_it.h»
/* USER CODE BEGIN 0 */
#include «led.h»
uint8_t n_count=0;
Убираем свой код из функции TIM6_DAC_IRQHandler и пишем туда следующий код:
/* USER CODE BEGIN TIM6_DAC_IRQn 1 */
if(n_count==0)
{
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6, GPIO_PIN_SET);
segchar(R1);
}
if(n_count==1)
{
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_4, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3|GPIO_PIN_5|GPIO_PIN_6, GPIO_PIN_SET);
}
if(n_count==2)
{
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_5, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_6, GPIO_PIN_SET);
}
if(n_count==3)
{
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_6, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5, GPIO_PIN_SET);
}
n_count++;
if (n_count>3) n_count=0;
Ещё раз собираем, прошиваем, смотрим
Мы видим, что цифры у нас теперь зажигаются по очереди.
Конечно, пока скорость таймера небольшая, мы можем это наблюдать. Вообще, так быть не должно, чтобы глаз видел процесс динамической индикации, но я специально увеличил только в 4 раза скорость. Сейчас нам, наоборот, интересно проследить данный процесс.
Теперь наша задача, передавать в каждый разряд специально предназначенную именно для него цифру.
В файл led.c добавим переменные для разрядов
//———————————————
uint8_t R1=0, R2=0, R3=0, R4=0;
//———————————————
Туда же добавляем новую функцию
//———————————————
void ledprint(uint16_t number)
{
R1 = number%10;
R2 = number%100/10;
R3 = number%1000/100;
R4 = number/1000;
}
Создадим для неё прототип.
В файл stm32f4xx_it.c проекстерналим переменные
uint8_t n_count=0;
extern uint8_t R1, R2, R3, R4;
/* USER CODE END 0 */
Добавим строчки в функции TIM6_DAC_IRQHandler
/* USER CODE BEGIN TIM6_DAC_IRQn 1 */
if(n_count==0)
{
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6, GPIO_PIN_SET);
segchar(R1);
}
if(n_count==1)
{
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_4, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3|GPIO_PIN_5|GPIO_PIN_6, GPIO_PIN_SET);
segchar(R2);
}
if(n_count==2)
{
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_5, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_6, GPIO_PIN_SET);
segchar(R3);
}
if(n_count==3)
{
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_6, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5, GPIO_PIN_SET);
segchar(R4);
}
n_count++;
if (n_count>3) n_count=0;
В файле main.c комментируем всё в бесконечном цикле.
Добавим туда вызов функции
HAL_TIM_Base_Start_IT(&htim6);
ledprint(1234);
Ещё раз собираем, прошиваем, смотрим
Мы видим, что число «1234» у нас отображается, но в связи с малой скоростью смены разрядов цифры показываются по очереди.
Закрываем проект, заходим в куб, меняем Counter Period на 20, это будет 1/100 секунды.
Генерируем проект, открываем его в Keil.
Уберем полностью весь код из бесконечного цикла, чтобы нам не получить артефактов в первом разряде или закомментируем его.
Собираем проект, прошиваем его, смотрим и видим что есть мерцание (здесь это не очень заметно, в видео тоже, заметно только натуральным глазом)
Опять заходим в куб, меняем Counter Period на 10.
Генерируем проект, собираем, прошиваем, смотрим и видим что мерцания пропали. Здесь нет смысла это показывать, тем более что на видео наоборот, в этом случае мерцает больше. чем при периоде 20, видимо, это, из-за несовпадения частоты таймера с частотой кадров камеры.
Теперь убираем строчку
ledprint(1234);
Меняем тип переменной i
/* USER CODE BEGIN 1 */
uint16_t i=0;
/* USER CODE END 1 */
Раскомментируем счётчик в бесконечном цикле и прибавим там предел и изменим функцию вызова вывода символа на функцию вызова вывода четырехзначного числа и убавим на порядок задержку.
for(i=0;i<10000;i++)
{
ledprint(i);
HAL_Delay(100);
}
Ещё раз собираем, прошиваем, смотрим
Наш четырёхразрядный счётчик отлично работает, благодаря применению динамической индикации.
Предыдущий урок Программирование МК STM32 Следующий урок
Отладочную плату и индикаторы можно приобрести здесь:
Семисегментный чертырехразрядный индикатор красный с общим анодом 10 шт
Смотреть ВИДЕОУРОК
Прекрасно работающий код спасибо большое. Хотелось спросить как его можно модифицировать чтобы можно было выводить float на экран
Где-то мы выводили значения с плавающей запятой на светодиодный индикатор и именно через драйвер MAX7219, может когда какой-то датчик прикручивали к нему, только точно не помню.
Narod Stream, Спасибо Вам за качественные уроки — продолжайте в этом духе)
1. А для чего нужны эти транзисторы на схеме? (понятно, что каком режиме они работаю, вопрос в том с какой целью?).
2. Можно ли без них обойтись?
Транзисторы нужны для управления подачей питания на общий анод каждого индикатора поочередно. Через общий анод (или катод в случае ОК индикаторов) течет ток, который может спалить вывод микросхемы. Для предотвращения этого используются транзисторы, которыми и управляет МС.
т.е. просто как ключи?
Да, в этой схеме транзисторы используются как ключи. Я кстате не использовал в схеме их. Резисторы использовал 2k.Индикатор потреблял 0.75mA-сегмент. А если 8*0.75mA=6mA на ножке(общий Анод1), а ножка STM32 можно нагружать 25mA.