Продолжаем изучение библиотеки LL.
И сегодня мы к нашему контроллеру STM32F103 попробуем подключить четырехразрядный семисегментный индикатор.
Причём именно сразу четырехразрядный, так как, думаю, работать со статической индикацией и с одноразрядным индикатором смысла нет, так как это используется очень редко и как правило актуально только на этапе изучения индикаторов. Но мы с вами их уже давно знаем и работали с ними далеко не один раз. В частности по STM в этом плане был урок 11.
Поэтому приступим сразу к динамической индикации. О динамической индикации мы также знаем не понаслышке и с ней мы работали уже в уроке 12, только использовали мы тогда немного другой контроллер и библиотеку HAL. Но это вовсе не беда, мы всё сегодня адаптируем под нашу LL.
На всякий случай повторю, что динамическая индикация — это такой вид общения с многоразрядными светодиодными индикаторами, при котором показывается в определённый момент времени только одна цифра одного отдельно взятого индикатора, затем следующего, а предыдущий гасится, и так далее. Только показывается всё это с такой скоростью, что человеческий глаз (а точнее мозг) не успевает оценить, что цифры горят по очереди и ему кажется что цифры горят все вместе одновременно. Данный тип индикации позволяет сэкономить внушительное количество ножек портов.
Достигается это следующим образом. На все ножки всех индикаторов последовательно по очереди подаются уровни сначала одной цифры, предназначенной, например, для первого индикатора, потом также на все вместе, уровни цифры, предназначенной для второго индикатора и так далее. А иначе никак и не получится, ведь у них лапки все запараллелены. Только в то время, когда подаётся цифра для определённого индикатора, то на его катод, если он с общим катодом, подаётся низкий уровень, а на все остальные катоды — высокий, и уже следовательно остальные индикаторы светиться не будут. А если он с общим анодом, то подаётся на нужный анод высокое напряжение, а на все остальные — низкое и они также светиться не будут.
Давайте посмотрим схему самого индикатора 4-разрядного 7-сегментного. У меня индикатор вот такого вот типа
У меня также индикатор с общим анодом, поэтому будем придерживаться вот этой схемы для написания кода.
Подключим наш индикатор к контроллеру STM32F103 следующим образом
Контроллер на схеме немного другой, так как в библиотеке такого же не нашлось, но ножек столько же и распиновка та же. Также вместо четрырёхразрядного я подключил четыре одноразрядных индикатора, тем самым показав наглядность соединения разрядов. Также по данной схеме вы можете собрать индикатор из четырёх одноразрядных индикаторов. Подключил я ножки контроллера, только используемые для индикаторов, служебные никакие не подключал, они у нас и так отлично разведены на нашей плате. Питать транзисторы и индикатор мы будем прямо от ST-Link, поэтому никаких дополнительных источников подводить не придётся, несмотря даже на то, что на плате выпаян стабилизатор.
А вот так теперь наша схема будет выглядеть в железе на практике
Ну, теперь, в принципе, можно переходить спокойно уже и к проекту, так как ничего нового нам по библиотеке LL изучать не придётся, всё уже изучено на первых двух занятиях.
Проект создадим из проекта прошлого занятия с именем LL_TIM2 и имя ему мы присвоим LL_LED_DYN.
Откроем проект в Cube MX и сначала изменим назначение ножек портов, то есть ножку, которая была у нас на вход, мы отключим, а ножки на выход мы настроим те, которые у нас используются в схеме. PA4:PA7 — разряды, PB8-PB15, соответственно — сегменты
В настройках таймера TIM2 изменим значение предделителя и периода
Сгенерируем проект для Keil и откроем его там. Настроим программатор на автоперезагрузку и уровень оптимизации установим в 0.
Возьмём из проекта урока 12 с именем LED_DYN файлы led.c и led.h и скопируем их в папки нашего проекта соответственно в Src и Inc.
Подключим файл led.c к дереву проекта.
Также подключим данную библиотеку в файле main.c
1 2 |
/* USER CODE BEGIN Includes */ #include "led.h" |
В файле led.h удалим подключение внешних хедеров
#include "stm32f4xx_hal.h"
#include "main.h"
Вместо них подключим другие
1 2 |
#include "stm32f1xx.h" #include "stm32f1xx_ll_gpio.h" |
К следующим дефайнам в данном файле добавим префикс LL_, также немного изменится и нумерация ножек
1 2 3 4 5 6 7 8 |
#define SA LL_GPIO_PIN_8 #define SB LL_GPIO_PIN_9 #define SC LL_GPIO_PIN_10 #define SD LL_GPIO_PIN_11 #define SE LL_GPIO_PIN_12 #define SF LL_GPIO_PIN_13 #define SG LL_GPIO_PIN_14 #define SH LL_GPIO_PIN_15 |
В макросах далее для включения и отключения сегментов также внесём некоторые исправления в свете требований библиотеки LL
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#define SA_SET LL_GPIO_ResetOutputPin(GPIOB, SA); #define SA_RESET LL_GPIO_SetOutputPin(GPIOB, SA); #define SB_SET LL_GPIO_ResetOutputPin(GPIOB, SB); #define SB_RESET LL_GPIO_SetOutputPin(GPIOB, SB); #define SC_SET LL_GPIO_ResetOutputPin(GPIOB, SC); #define SC_RESET LL_GPIO_SetOutputPin(GPIOB, SC); #define SD_SET LL_GPIO_ResetOutputPin(GPIOB, SD); #define SD_RESET LL_GPIO_SetOutputPin(GPIOB, SD); #define SE_SET LL_GPIO_ResetOutputPin(GPIOB, SE); #define SE_RESET LL_GPIO_SetOutputPin(GPIOB, SE); #define SF_SET LL_GPIO_ResetOutputPin(GPIOB, SF); #define SF_RESET LL_GPIO_SetOutputPin(GPIOB, SF); #define SG_SET LL_GPIO_ResetOutputPin(GPIOB, SG); #define SG_RESET LL_GPIO_SetOutputPin(GPIOB, SG); #define SH_SET LL_GPIO_ResetOutputPin(GPIOB, SH); #define SH_RESET LL_GPIO_SetOutputPin(GPIOB, SH); |
В файле led.c добавим глобальную переменную, которая будет у нас служить для отключения разрядов слева для цифр, в которых будет менее 4 знаков
1 2 |
uint8_t R1=0,R2=0,R3=0,R4=0; uint16_t num_gl=0; |
Передадим данной переменной входящий аргумент в функции ledprint
1 2 3 |
void ledprint(uint16_t number) { num_gl = number; |
В файле main.c удалим все макросы, останется только глобальная переменная
1 2 3 |
/* USER CODE BEGIN PV */ uint8_t tim2_count; /* USER CODE END PV */ |
Подключим также здесь некоторые глобальные переменные
1 2 3 |
uint8_t tim2_count; extern uint8_t R1,R2,R3,R4; extern uint16_t num_gl; |
Также выше добавим некоторые макросы, которые нам затем пригодятся для удобства
1 2 3 4 5 6 7 8 |
/* USER CODE BEGIN PV */ #define DIG_PORT GPIOA #define SEG_PORT GPIOB #define PIN_R1 LL_GPIO_PIN_4 #define PIN_R2 LL_GPIO_PIN_5 #define PIN_R3 LL_GPIO_PIN_6 #define PIN_R4 LL_GPIO_PIN_7 #define ALL_SEG_OFF() LL_GPIO_SetOutputPin(SEG_PORT,SH|SG|SF|SE|SD|SC|SB|SA); |
В функции main() удалим вот это
LED1_OFF();LED2_OFF();LED3_OFF();LED4_OFF();LED5_OFF();
LED6_OFF();LED7_OFF();LED8_OFF();LED9_OFF();LED10_OFF();
И попробуем здесь сначала отобразить какое-нибудь число
1 2 |
tim2_count = 0; ledprint(1234); |
В обработчике прерывания таймера удалим пока оператор вариантов с его телом.
Если мы соберём код и прошьём контроллер, то, конечно же, никакого числа мы не увидим, потому что нам нужно будет ещё написать определённый код в данной функции-обработчике.
Добавим данный код
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
LL_TIM_ClearFlag_UPDATE(TIM2); switch(tim2_count) { case 0: LL_GPIO_SetOutputPin(DIG_PORT,PIN_R1); LL_GPIO_ResetOutputPin(DIG_PORT,PIN_R2|PIN_R3|PIN_R4); segchar(R1); break; case 1: LL_GPIO_SetOutputPin(DIG_PORT,PIN_R2); LL_GPIO_ResetOutputPin(DIG_PORT,PIN_R1|PIN_R3|PIN_R4); segchar(R2); break; case 2: LL_GPIO_SetOutputPin(DIG_PORT,PIN_R3); LL_GPIO_ResetOutputPin(DIG_PORT,PIN_R1|PIN_R2|PIN_R4); segchar(R3); break; case 3: LL_GPIO_SetOutputPin(DIG_PORT,PIN_R4); LL_GPIO_ResetOutputPin(DIG_PORT,PIN_R1|PIN_R2|PIN_R3); segchar(R4); break; } |
Также ниже изменим число, до которого будет считать таймер, так как у нас всего 4 сегмента, а не 10
if(tim2_count>3) tim2_count=0;
Теперь, если мы соберём код и прошьём контроллер, то у нас будет должно отобразиться наше число
Закомментируем пока вызов функции отображения числа в main()
//ledprint(1234);
Добавим локальную переменную
1 2 |
/* USER CODE BEGIN 1 */ uint16_t i; |
И пусть наш счётчик считает от 0 до 9999. Для этого добавим вот этот код в бесконечный цикл
1 2 3 4 5 6 |
/* USER CODE BEGIN 3 */ for(i=0;i<10000;i++) { ledprint(i); LL_mDelay(100); } |
Соберём код, прошьём контроллер и посмотрим результат
Всё отлично считается, только неплохо бы при отображения однозначных, двухзначных и трёхзначных чисел убрать лидирующие нули.
Для этого в обработчике прерывания от таймера в 1 кейсе добавим вот такой код
1 2 |
segchar(R2); if(num_gl<10) ALL_SEG_OFF(); |
Во втором — вот такой
1 2 |
segchar(R3); if(num_gl<100) ALL_SEG_OFF(); |
В третьем — такой
1 2 |
segchar(R4); if(num_gl<1000) ALL_SEG_OFF(); |
Прошьём теперь контроллер, собрав предварительно код.
Результат теперь будет вот такой
Теперь другое дело.
Таким образом, в данном уроке мы научились работать с динамической индикацией, используя возможности библиотеки LL.
Всем спасибо за внимание!
Предыдущий урок Программирование МК STM32 Следующий урок
Отладочную плату STM32F103C8T6 можно приобрести здесь STM32F103C8T6
Программатор недорогой можно купить здесь ST-Link V2
Семисегментный чертырехразрядный индикатор красный (с общим анодом или катодом на выбор) 10 шт
Смотреть ВИДЕОУРОК (нажмите на картинку)
Добавить комментарий