Урок 25
HAL. SPI. LED Динамическая индикация
В предыдущем уроке мы изучили возможность управления посредством шины SPI семисегментным светодиодным индикатором.
А на данном занятии мы попробуем управлять уже четырьмя такими индикаторами, и использовать мы будем уже динамическую индикацию.
Чем отличается динамическая индикация от обычной статической, вы можете узнать из урока 12.
Основное отличие в том, что индикаторы или разряды светятся не сразу все, а по очереди.
Прежде чем мы перейдём непосредственно к проекту, давайте посмотрим схему подключения (нажмите на картинку для увеличения изображения)
Во-первых, мы видим, что сдвиговых регистра у нас теперь два, так как восьми портов нам уже не хватит. Один из регистров будет управлять сегментами, а второй — разрядами.
Также мы применим каскадный или кольцевой метод подсоединения по SPI. Поэтому запараллелим мы только Chip Select и ножки синхронизации. А с ножки MISO контроллера сигнал пойдёт сначала на цифровой вход нижнего регистра, а уже дальше с цифрового выхода нижнего регистра сигнал пойдёт на цифровой вход верхнего. MOSI мы также не используем, так как отчёт нам не нужен, мы всё увидем по свечению индикаторов. Сегменты всех четырёх индикаторов у нас подключены параллельно к параллельным выходам верхнего регистра, а общие аноды индикаторов подключены через ключевые транзисторы к четырём младшим параллельным выходам нижнего регистра.
Вообщем, сначала в нижний регистр мы посылаем байт для верхнего регистра, а затем для нижнего, а тот байт, который там уже был, переместится в верхний регистр, тем самым байты все окажутся на своих местах. А затем, сгенерировав импульс на ножке SS, мы переместим все байты уже в регистры, предназначенные для параллельных выходов, и тем самым на индикаторы поступит нужные сигналы.
Также схема имеет несколько упрощённый вариант и не ней не указаны резисторы ограничения тока базы на 2,2 килоома. Так что не забудьте поставить данные резисторы между базами транзисторами и четырьмя выходами регистра.
А вот так наша схема выглядит на практике
Проект создаём из предыдущего проекта SPI_595_LED, называем его SPI_595_DYN_LED.
Запускаем Cube. Добавим таймер 6.
Конфигурируем его пока вот так – раз в секунду
Прерывания также нужно включить.
Генерируем и открываем проект в Keil.
Переходим в проект. Подключаем файл led.c и настраиваем программатор на авторезет.
Соберём проект.
В функции main() запустим таймер
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim6);
В бесконечном цикле пока всё закомментируем
while (1)
{
// for(i=0;i<=9;i++)
// {
// segchar(i);
// HAL_Delay(1000);
// }
Немного поправим стартовый код, т.к. микросхемы у нас две
cs_set();
aTxBuffer[0]=0xFF;//погасим все сегменты
HAL_SPI_Transmit (&hspi3, (uint8_t*)aTxBuffer, 1, 5000);
cs_strob();
aTxBuffer[0]=0x0F;//погасим все разряды
HAL_SPI_Transmit (&hspi3, (uint8_t*)aTxBuffer, 1, 5000);
cs_strob();
HAL_Delay(200);
/* USER CODE END 2 */
Добавим функцию перехвата прерывания от таймера в главный модуль программы
/* USER CODE BEGIN 4 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim6)
{
}
Добавим счётчик разрядов
uint8_t aTxBuffer[1]={0};
uint8_t n_count=0;
/* USER CODE END PV */
В led.c добавим переменные для разрядов
uint8_t portseg=0;
uint8_t R1=0,R2=0,R3=0,R4=0;
Проекстерналим их в main.c
uint8_t aTxBuffer[1]={0};
extern uint8_t R1,R2,R3,R4;
uint8_t n_count=0;
Функцию ledprint для файла led.c возьмем из одноименного файла проекта LED_DYN из урока 12.
//==============================
void ledprint(uint16_t number)
{
R1 = number%10;
R2 = number%100/10;
R3 = number%1000/100;
R4 = number/1000;
}
Также не забываем про прототип в led.h
void segchar (uint8_t seg);
void ledprint(uint16_t number);
В тело функции HAL_TIM_PeriodElapsedCallback в main.c скопируем также код из проекта LED_DYN, но из функции TIM6_DAC_IRQHandler файла stm32f4xx_it.c
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;
Изменим немного этот код в свете требований микросхемы и шины SPI
if(n_count==0)
{
segchar(R1);
aTxBuffer[0]=0x0E;
HAL_SPI_Transmit (&hspi3, (uint8_t*)aTxBuffer, 1, 5000);
cs_strob();
}
if(n_count==1)
{
segchar(R2);
aTxBuffer[0]=0x0D;
HAL_SPI_Transmit (&hspi3, (uint8_t*)aTxBuffer, 1, 5000);
cs_strob();
}
if(n_count==2)
{
segchar(R3);
aTxBuffer[0]=0x0B;
HAL_SPI_Transmit (&hspi3, (uint8_t*)aTxBuffer, 1, 5000);
cs_strob();
}
if(n_count==3)
{
segchar(R4);
aTxBuffer[0]=0x07;
HAL_SPI_Transmit (&hspi3, (uint8_t*)aTxBuffer, 1, 5000);
cs_strob();
}
n_count++;
if(n_count>3) n_count=0;
Соберём проект, прошьём его и посмотрим на переключающиеся нули
Теперь попробуем вывести какое-нибудь число
HAL_Delay(200);
ledprint(1234);
/* USER CODE END 2 */
Попробуем увеличить скорость в Cube, для этого период уменьшим Counter Period с 4000 до 1000
Прошьем и посмотрим результат, стало побыстрее, но это ещё не совсем то.
Теперь изменим на 200
Прошьем и посмотрим результат, опять недостаточно. Но ничего, потом ещё прибавим.
Теперь попробуем посчитать
Для этого изменим тип переменной для счёта
/* USER CODE BEGIN 1 */
uint16_t i=0;
/* USER CODE END 1 */
Также раскомментируем код в бесконечном цикле и внесём некоторые изменения, применив другую функцию и увеличив количество чисел, а также убавив задержку
for(i=0;i<=9999;i++)
{
ledprint(i);
HAL_Delay(100);
}
Вот это, соответственно, можно удалить
HAL_Delay(200);
ledprint(1234);
Опять убавим период в 10 раз в Cube, установив его в 20.
Посмотрим результат, также видим мерцание.
Если мы уберём ещё в 4 раза, то получим тот же результат, что и в проекте LED_DYN
Поэтому установим теперь Counter Period 5
Посмотрим результат, вроде нормально
На следующем занятии мы попробуем уже подключить специализированную микросхему, предназначенную для цифровых многоразрядных светодиодных индикаторов, причём также по интерфейсу SPI.
Предыдущий урок Программирование МК STM32 Следующий урок
Техническая документация на микросхему 747HC595
Отладочную плату и сдвиговые регистры и индикаторы можно приобрести здесь:
Сдвиговые регистры 74HC595N 10 шт
Семисегментный чертырехразрядный индикатор красный с общим анодом 10 шт
Смотреть ВИДЕОУРОК (нажмите на картинку)
Спасибо за уроки. Немного путано по урокам прыгать в поисках замысла, но все равно — это лучший начальный курс, который я видел.
По теме —
1. Раз уж действия с чипом 595 выполнены с помощью дефайнов, можно и передачу определить там же
#define cs_transmit() HAL_SPI_Transmit (&hspi1, (uint8_t*)aTxBuffer, 1, 5000)
2. Я работаю с маленькой платой F103C8T6, и код приходится сильно переделывать. В процессе сильно путался с инклюдами. Если честно — очень непростая тема наверное даже для опытных программистов, где и что должно быть подключено и описано.
3. Тема этого урока очень актуальна — нигде нет нормальной готовой библиотеки для работы с такими модулями четырехразрядных семисегментных индикаторов на данных микросхемах (ссылка очищена).
А они — практически то, что в Вашем уроке. Было бы здорово довести дело до готового решения. Но, вероятно, это домашнее задание?
Да можно и как домашнее задание, так как у меня таких нет, их вполне заменяют модули на микросхемах MAX7219. Принцип я вроде объяснил как мог. А передачу можно и определить, можно и функцию свою прописать, это как кому нравится.
Еще пара нюансов.
1. Я работаю в System Workbench. При компиляции была ошибка на тип uint_8t . Решвением было подключение библиотеки #include «stdint.h»
2. В объявлении функций void _Error_Handler(char*, int); была ошибка (несоответствие) — было так — Error_Handler(char, int);