0

Сдвиговые регистры и stm32f4

Рано или поздно возникает проблема: не хватает портов ввода и вывода на микроконтроллере. Конечно, можно построить D-триггеры, и вязать к ним систему обвязки, но это уж как то старомодно и не круто. Можно поставить преобразователи I2C, и я хотел так сделать, например для 7 сегментников есть такая микросхема SAA1064, которая по I2C управляет 4-мя семисегментниками. Стоит она конечно дорого, но мне перепало несколько таких микросхем, и как будет время и стеклотекстолит: я, конечно, их задействую. А сейчас я нашёл себе микросхему попроще и подешевле. Это микросхема 74HC595, которая есть просто в огромном количестве на прилавках магазинов.
1Что у неё внутри.Поскольку мне достались микросхемы фирмы fairchild semiconductor: MM74HC595, то я буду опираться на её даташит.
Данная микросхема содержит 8 сдвиговых регистров с выводными переключателями. То есть содержимое этих 8 регистров данных может быть отображено в виде высокого и низкого уровня на выходах микросхемы. Так же есть бит перехода к след микросхеме (что позволяет собирать из них почти бесконечные цепочки). Предельная частота 30МГЦ. Есть почти во всех корпусах, что очень приятно услышать имея запросы в портативных устройствах(SO, TSSOP) и желание собирать устройства на макетной плате(PDIP). Распиновка у этой микросхемы не зависит от производителя:
2Немного об электрических характеристиках.

Низкий входной ток в 1мА, — позволил подключить её без буферных резисторов к любым микроконтроллерам.
Высокий выходной ток в 80мА позволяет подключать не только микросхемы, но и светодиоды, зуммеры, и прочие устройства, вплоть до небольших моторчиков.
Широкий диапазон рабочих напряжений(от 2 до 6 В). Меня этот факт просто поразил, так как сейчас планирую сделать автономное устройство с особо низким потреблением питания, с общей шиной питания 2В. Пока часть, в которой используется данная микросхема работает, и сильно упростило мне задачу.
Очень удобные фишки данной микросхемы: регистр вывода и отдельный регистр сброса данной микросхемы. Они легко находятся из логической схемы:
3 Отладка в эмуляторе.

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

4  Замечания: в протеусе схема не умеет работать в режиме 3В, только 5В и выше. Поэтому эмулируйте на 5-ти вольтах, а потом 1 к 1-му переносим на 3-х волтную логику. Резисторы я добавил просто для сброса, о нём не стоит забывать. Если подключаете к мк, то есть замечательную таблицу логических уровней:
5На вход нужно подавать импульсы, поэтому тактирование и не забывайте про сброс. Данная микросхема совместима с интерфейсом SPI, что значительно увеличивает максимальную частоту тактирования.
Из следующей фотографии из даташита следует, что нужна небольшая задержка после устрановки бита данных перед CLocK. Это отображено в программном коде.
6(там где решётка, неустановленно)

Отладка на практике.

Эмулятор эмулятором, а на натуре проверить хотелось, вот я и собрал простую плату:
7На ней особо ничего нет, но можно удобно впихнуть в макетку (к сожалению в магазине были только so-корпуса, и я не нашёл ни одного дипа). Как она легко вставляется в макетку модно посмотреть на первом изображении в этой статье.
Её можно легко заменить макеткой собранной по такой схеме:
8На неё 5 разъёмов: 3 канала + питание.
Далее её подключаем к стму при помощи моего разъёма на 4 линейных разъёма(3 порта + земля), это можно видеть на первой фотографии. Подключаем к РЕ7, РЕ 9, РЕ11. Также нам потребуется UART-USB. Его я подключил по тому же разъёму, что и в прошлой статье.
1. Настройка usart.
2. Настройка функция вывода информации.
3. Настройка прерываний по событию в UARTe
Чтобы немного сократить объём статьи я просто выложу полный код, он простой, и у вас не возникнет проблем при его разборе, так как я всё комментировал.

#include "stm32f4xx.h"
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_usart.h"

char uart_data;//принятые данные по USART
int i;//variables

//Функция отправляет байт в UART
void send_to_uart(uint8_t data)
{
 while(!(USART2->SR & USART_SR_TC));
 USART2->DR=data;
}

//Функция отправляет строку в UART, по сути пересылая по байту в send_to_uart
void send_str(char * string)
{
 uint8_t i=0;
 while(string[i])
 {
  send_to_uart(string[i]);
  i++;
 }
}

//Инициализируем USART2
void usart_init(void)
{
  GPIO_InitTypeDef GPIO_InitStructure; //Структура содержащая настройки порта
  USART_InitTypeDef USART_InitStructure; //Структура содержащая настройки USART

  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); //Включаем тактирование порта A
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); //Включаем тактирование порта USART2

  GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2); //Подключаем PA3 к TX USART2
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2); //Подключаем PA2 к RX USART2

  //Конфигурируем PA2 как альтернативную функцию -> TX UART.
 Подробнее об конфигурации можно почитать во втором уроке.
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOA, &GPIO_InitStructure);

  //Конфигурируем PA2 как альтернативную функцию -> RX UART.
 Подробнее об конфигурации можно почитать во втором уроке.
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
  GPIO_Init(GPIOA, &GPIO_InitStructure);

  USART_StructInit(&USART_InitStructure); //Инициализируем UART с дефолтными
 настройками: скорость 9600, 8 бит данных, 1 стоп бит

  USART_Init(USART2, &USART_InitStructure);
  USART_Cmd(USART2, ENABLE); //Включаем UART
}

int main(void)
{
        GPIO_InitTypeDef GPIO_InitStructure; //Структура содержащая настройки порта
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE); //Включаем тактирование порта E
        GPIO_InitStructure.GPIO_Pin = (GPIO_Pin_7| GPIO_Pin_9| GPIO_Pin_11);//Выбераем нужные 
         выводы
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;//задаём тактовую частоту порта
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //Включаем режим выхода
        GPIO_InitStructure.GPIO_PuPd= GPIO_PuPd_UP;   //подтяжка порта к + питания
        GPIO_Init(GPIOE, &GPIO_InitStructure); //вызов функции инициализации

        usart_init(); //Инициализируем UART
        //Настраиваем прерывания по приему
        __enable_irq(); //Глобальное включение прерывания
        NVIC_EnableIRQ(USART2_IRQn); //Включаем прерывания от UART
        NVIC_SetPriority(USART2_IRQn, 0); //Прерывание от UART, приоритет 0, самый высокий
        USART2->CR1 |= USART_CR1_RXNEIE; //Прерывание по приему

        GPIO_SetBits(GPIOE,GPIO_Pin_7| GPIO_Pin_9| GPIO_Pin_11);

    while(1)
    {

    }
}

void set_one()
{
        GPIO_SetBits(GPIOE,GPIO_Pin_11);
        i=0;
        while(i<100){i++;}
        GPIO_SetBits(GPIOE,GPIO_Pin_7);
}
void set_zero()
{
        GPIO_SetBits(GPIOE,GPIO_Pin_7);
}

void show()
{
        GPIO_SetBits(GPIOE,GPIO_Pin_9);
}
void USART2_IRQHandler (void)
{
                if (USART2->SR & USART_SR_RXNE) //Проверяем, прило ли чтонибудь в UART
                {
                USART2->DR = USART2->DR; //Echo по приему, символ отправленный в
                консоль вернется
                uart_data= USART2->DR;
                GPIO_ResetBits(GPIOE,GPIO_Pin_7| GPIO_Pin_9| GPIO_Pin_11);
                i=0;
                while(i<100){i++;}
                switch(uart_data)
                {
                case '1':set_one();break;
                case '2':set_zero();break;
                case '3':show();break;
                }
            i=0;
            while(i<100){i++;}
                }
}

В результате через UART отправляя соответствующие цифры, можно управлять выводом данной микросхемы.
Коды управления через терминал, отсылать без признака конца строки:
1. Вывести 1 в буфер (сдвинуть и добавить горящий светодиод)
2. Вывести 0 в буфер (сдвинуть и добавить не горящий светодиод)
3. Вывести на светодиоды.(да, выводить тоже можно по отдельной команде, и до момента пока вы туда не послали импульс там будет предыдущая картинка. Весьма полезная штука, если вы занимаетесь динамической индикацией.)
В итоге я записал вот это видео:

Практика на закрепление материала.

Но на этом я не остановился, и так как я видел классную статью на английском по этой штуке, но от NXP, то я решил реализовать программу, а которой там говорил автор, но на STM32f407vg, а не на ATMega8, как в примере. Там есть некоторые особенности, но они объяснены в коде.
В качестве практики на закрепление сделаем популярную и простую программу: бегущий огонёк. Смысл такой:
1. Устанавливает один бит на 1
2. Сдвигаем горящий светодиод, пока он не достигнет крайнего положения и не потухнет.
3. Переходим к пункту 1.
Используя возможности стм32, задача крайне простая. И я быстро написал вот такой код:


#include "stm32f4xx.h"
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_rcc.h"

int i,j;//variables for loop and move

void set_one()
{
        GPIO_SetBits(GPIOE,GPIO_Pin_11);
        i=0;
        while(i<100){i++;}
        GPIO_SetBits(GPIOE,GPIO_Pin_7);
        GPIO_ResetBits(GPIOE,GPIO_Pin_7| GPIO_Pin_9| GPIO_Pin_11);//готовим для 
следующей команды
}
void set_zero()
{
        GPIO_SetBits(GPIOE,GPIO_Pin_7);
        GPIO_ResetBits(GPIOE,GPIO_Pin_7| GPIO_Pin_9| GPIO_Pin_11);//готовим для 
следующей команды
}

void show()//отображает данные в сдвиговом регистре

{
        GPIO_SetBits(GPIOE,GPIO_Pin_9);
        GPIO_ResetBits(GPIOE,GPIO_Pin_7| GPIO_Pin_9| GPIO_Pin_11);//готовим для 
следующей команды
}

int main(void)
{
        GPIO_InitTypeDef GPIO_InitStructure; //Структура содержащая настройки порта
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE); //Включаем тактирование порта E
        GPIO_InitStructure.GPIO_Pin = (GPIO_Pin_7| GPIO_Pin_9| GPIO_Pin_11); //Выбераем 
нужные выводы
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;//задаём тактовую частоту порта
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //Включаем режим выхода
        GPIO_InitStructure.GPIO_PuPd= GPIO_PuPd_UP;   //подтяжка порта к + питания
        GPIO_Init(GPIOE, &GPIO_InitStructure); //вызов функции инициализации

        GPIO_ResetBits(GPIOE,GPIO_Pin_7| GPIO_Pin_9| GPIO_Pin_11);//готовим для команды

    while(1) //бесконечный цикл работы
    {
    j=0;
    set_one();//устанавливаем бит готорый будет гореть
    show();   //выводим данные на светодиоды
    while(j<8)//тут хитрость, мы экономим код, но лишный раз прогоняем цикл
        {
        i=0;
        while(i<100000){i++;}//задержка
        set_zero();//сдвигаем на 1 бит, и заполняем следующий пустым
        show();    //выводим данные на светодиоды
        j++;       //переводим счётчик к след положению
        }
    }
}

Работу данной программы можно посмотреть на следующем видео:

akoe.ru

admin

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *