Подключение 74HC595 к STM32F103C8T6 по SPI
Приобрести на Aliexpress
Микросхема 74HC595 - это сдвиговый регистр
- последовательный 8-бит вход, параллельный 8-бит выход
- напряжение питания 2-6 В
- выходной ток до 25 мА (на ножку)
Эта микра дает возможность подключить и управлять восемью нагрузками используя только три ножки МК, её используют обычно для светодиодных индикаторов и реже для ЖК-дисплея. То бишь даже применяя восьмивыводный контроллер благодаря сдвиговому регистру можно добавить в устройство светодиодный индикатор.
Чутка теории из тех. документации:
Восьмиразрядный сдвиговый регистр принимает данные с вывода DS (14, MOSI) при переднем фронте (переход от лог.0 к лог.1) на выводе SHCP (11, SCK). Данные с входного сдвигового регистра переносятся в выходной регистр при импульсе на выводе STCP (12, PA6).
Также нужно не забыть на вывод OE (13) подать лог.0, чтобы вообще разрешить вывод данных и подтянуть вывод MR (10), чтобы не сбрасывались значения в сдвиговом регистре. Захват данных регистрами происходит при переднем фронте, а вывод при заднем фронте.
Подключение
Подключение микросхемы 74HC595 к микроконтроллеру показано ниже, ни в коем случае не оставляйте неиспользуемые ножки неподключенными (особенно это касается вывода сброса MR (10)), иначе работать не будет.
Куб
Врубаем SPI (Serial Peripheral Interface) режиме передачи только от ведущего устройства (Transmit Only Master):
Настройки трогать обычно не нужно, но при высокой тактовой частоте предделитель придется выставить на 4, вместо 2.
Теперь любой вывод (здесь PA6) настраиваем на выход, включаем подтяжку к минусу.
Вывод байта
Для передачи чего-либо через SPI средствами HAL нужно иметь массив, здесь он состоит из одного элемента:
uint8_t transmit_data[1];
Присваиваем значение нулевому элементу, которое и будет передано:
transmit_data[0] = 170;
Почему именно 170? Потому что в двоичной системе исчисления это выглядит так:
Где "1" СД будет светится, где "0" - нет. То есть светодиоды будут светится через один.
Числа в двоичном коде Keil не понимает (такой стандарт C51), поэтому написано в десятичной, можно также в шестнадцатеричной 0xAA.
Чтобы микросхема всё приняла сначала нужно установить низкий логический уровень на выводе STCP (11), благодаря подтяжке он уже установлен, передать 1 байт, подать импульс на вывод защёлки STCP (11) и тогда данные запишутся по переднему фронту и выведутся по заднему.
HAL_SPI_Transmit(&hspi1,(uint8_t*)transmit_data, 1, 100); // Передача данных по SPI
GPIOA->BSRR |= GPIO_BSRR_BS6; // Установка высокого уровня
GPIOA->BSRR |= GPIO_BSRR_BR6; // Установка низкого уровня
В итоге (показано на примере индикатора):
Бегущие огни светодиоды
Вспомогательная переменная:
uint8_t i = 0;
В главном цикле сдвигаем бит, передаем получившееся значение по SPI и создаем импульс на выводе STCP (11) (Обновление от 07.07.2024: код для бегущих огней был неправильный в примере и исходниках, теперь исправлен: https://cxemka.com/29-podklyuchenie-74hc595-k-stm3...:
transmit_data[0] = (1 << i);
if(i >= 7){
i = 0;
}else
{
i++;
}
HAL_SPI_Transmit(&hspi1,(uint8_t*)transmit_data, 1, 100);
GPIOA->BSRR |= GPIO_BSRR_BS6;
GPIOA->BSRR |= GPIO_BSRR_BR6;
HAL_Delay(100);
В итоге:
Вывод символов на индикатор (динамическая индикация)
Чтобы осуществить динамическую индикацию нужно дополнительно задействовать выводы для включения/выключения целых разрядов, то есть управлять подачей питания на общий провод (общий К/A).
В моем случае в середине каждого сегмента индикатора целых два СД (красный и зеленый), поэтому настроено на выход аж 4 вывода (2 для красн., 2 для зелен.).
В общем настраиваем выводы как двухтактный выход с подтяжкой к минусу.
SPI в режиме передачи от ведущего к ведомому (SPI1 -> Transmit Only Master).
Отладка через SWD (SYS -> Debug Serial Wire).
Тактирование одного из таймеров (TIM17 -> Activated).
Настраиваем таймер на частоту f > 40 Гц и включаем прерывание от него.
Связь между передаваемыми данными и выводимыми символами:
Для удобства создается массив с числами, при выводе которых будет отображение символов 0-9. Индикатор с общий анодом, то бишь придется инвертировать все значения, либо вызывать число из массива с инвертированием (знак ~ перед числом).
/* Массив чисел 0-9 для светодиодного индикатора */
uint8_t Number[10] = {123, 34, 241, 242, 170, 218, 219, 50, 251, 250};
main.c
/* USER CODE BEGIN Includes */
uint8_t Peredaem; // Переменная-число, которое и будет отображатся
/* USER CODE END Includes */
HAL_TIM_Base_Start_IT(&htim17); // Разрешаем прерывание от таймера
stm43f0xx_it.c
/* Буфер для передачи на индикатор */
uint8_t transmit_data[1];
/* Создаём массив чисел 0-9 */
uint8_t Number[10] = {123, 34, 241, 242, 170, 218, 219, 50, 251, 250};
/* Для перекючения */
uint8_t toogle = 0;
/* Переменная, объявленная в главном файле main.c*/
extern uint8_t Peredaem;
Непосредственно обновление происходит в обработчике прерывания от таймера:
void TIM17_IRQHandler(void)
{
/* USER CODE BEGIN TIM17_IRQn 0 */
if(toogle){
transmit_data[0] = ~Number[Peredaem%10]; // Число для младшего разряда
HAL_SPI_Transmit(&hspi1,(uint8_t*)transmit_data, 1, 100);
/* Создаем импульс на выводе STCP */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
/* Врубаем второй разряд и вырубаем первый */
GPIOB->BSRR |= GPIO_BSRR_BS_0;
GPIOB->BSRR |= GPIO_BSRR_BR_10;
}
else{
transmit_data[0] = ~Number[Peredaem/10]; // Число для старшего разряда
HAL_SPI_Transmit(&hspi1,(uint8_t*)transmit_data, 1, 100);
/* Создаем импульс на выводе STCP */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
/* Врубаем первый разряд и вырубаем второй */
GPIOB->BSRR |= GPIO_BSRR_BR_0;
GPIOB->BSRR |= GPIO_BSRR_BS_10;
}
/* Переключаем значение (0/255) */
toogle = ~toogle;
/* USER CODE END TIM17_IRQn 0 */
HAL_TIM_IRQHandler(&htim17);
/* USER CODE BEGIN TIM17_IRQn 1 */
/* USER CODE END TIM17_IRQn 1 */
}
Ну и в главном цикле:
/* USER CODE BEGIN WHILE */
while (1) {
while(Peredaem < 99){
Peredaem++;
HAL_Delay(100);
}
while(Peredaem > 0){
Peredaem--;
HAL_Delay(100);
}
/* USER CODE END WHILE */
В итоге:
Проект Keil и STM32CubeMX (бегущие огни): 74hc595_stm32_spi1.zip
Проект Keil и STM32CubeMX (динамическая индикация): 74hc595_stm32_led_indicator.zip












Хм, точно битые, исправлю.
https://cxemka.com/upload/art/74hc595_spi_stm32/74...
https://cxemka.com/upload/art/74hc595_spi_stm32/74...
Благодарю
Здравствуйте. Прочитал Вашу статью «Подключение 74HC595 к STM32F103C8T6 по SPI» у меня к Вам вопрос. Вы можете мне помочь с «Вывод символов на индикатор (Статическая индикация)» 2 регистра и 2 индикатора.?
Здравствуйте.
Тут должно быть все просто, последовательный выход регистра 1 Q7S подключается на вход DS регистра 2.
Данные шлются на вход DS регистра 1.
То есть, если используется тот же SPI, как в статье, тогда нужно будет отправить не один байт, а два (для двух индикаторов).
https://meandr.org/archives/5107
ссылки с проектами мертвые
починил
Спасибо!!! R9WAY
Доброго дня, в файле для работы со светодиодами не обнаружил куска кода transmit_data[0] = 170;
его отсутствие, ошибка, да?
Добрый день.
Его нет в проекте, т.к. это пример вывода одного байта, там сразу пример бегущих огней, где передача байта также происходит, но передаваемое значение постоянно меняется, чтобы уровень сигнала изменялся так, что светодиоды засвечивались по очереди.
А никого не смущает, что написанная программа «Бегущих огней» не соответствует анимации?
На анимации светодиоды засвечиваются последовательно: 0 -> 1 -> 2 -> 3… и так далее до седьмого.
А код написан для перебора двоичных чисел и светодиоды зажигаются так:
0000 0000
0000 0010
0000 0100
0000 0110
0000 1000
0000 1010
0000 1100
0000 1110
и с начала.
Да, вы правы, код был неправильный:
10
100
110
1000
1010
1100
1110
Уж не помню как точно было тогда, но исправил на новый код в статье и в исходниках:
10
100
1000
10000
100000
1000000
10000000
Теперь это именно те самые «Бегущие огни» (*светодиоды расположены просто в разных местах):