...

суббота, 17 июля 2021 г.

Разработка панели индикации с помощью сдвиговых регистров IN74HC595AD

Часто при разработке радио-электронных устройств возникает необходимость выполнения климатических условий с повышенными требованиями, таких как предельно допустимые рабочие температуры -55…+55 ºC. И эти требования становятся проблемой для реализации цифровых панелей взаимодействия с пользователем. Рабочие температуры ЖК индидикаторов в пределах -20...+70 ºC , люминисцентных газоразрядных индикаторов -40..+70 ºC. Поэтому возникает необходимость организовывать панели взаимодействия с оператором-пользователем с помощью цифровых индикаторов, одноцветных и двухцветных светодиодов. Для использования таких органов индикации необходимы схемотехнические и программные решения. Есть разные способы управления системой индикации. В данной статье хочу привести свой опыт использования и организации схемно-программной структуры проекта.

Cхема системы индикации

На рисунке ниже приведена применяемая мной схема подключения цифровых индикаторов, одноцветных и двухцветных светодиодов для вывода информации пользователю на лицевую панель. Все узлы собраны на отечественной комплектации. В качестве цифровых индикаторов используются индикаторы 3ЛС324Б1, которые управляются с контроллера с помощью буферных сдвиговых регистров IN74HC595AD. Особенностью данной схемы является ее масштабируемость. Для управления всеми индикаторами достаточно 5 выводов контроллера (можно уменьшить до трех, если OE выход подключить к минусу, а MR подключить к плюсу), которые используются для организации последовательного интерфейса к микросхемам IN74HC595AD. Как работать с этим интерфейсом описано в даташите на данную микросхему, пример программной реализации алгоритма работы представлен в листинге 1.

Микросхемы имеют дополнительный выход Q7’, подключение которого ко входу DS следующей микросхемы дает возможность создания целой цепочки сдвиговых регистров для передачи данных от одной микросхемы к другой. Если кол-во переданных бит в первую микросхему превысит 8, то эта микросхема начнет выдавать принятый байт на вход следующей микросхемы через выход Q7’, при этом стробирующие сигналы для всех микросхем запараллелены. Схема, приведенная ниже содержит небольшое кол-во индикаторов, для примера работы. У меня есть проект, где кол-во индикаторов достигает 63 штук и это не предел, причем в этом проекте есть два разных варианта панели))) Перейдем к программной реализации работы данной схемы.

Программа управления системой индикации

Для удобной программной реализации работы с системой индикации используется С++. Иерархия классов системы индикации состоит из абстрактного базового класса Indicator, от которого наследуются классы Led (одноцветный светодиод), LedDual (двухцветный светодиод), Digit (цифровой индикатор). Абстрактный класс Indication, в котором реализован метод Show и который использует массив индикаторов для отображения и дочерние классы Indication_v1, Indication_v2 в которых создаются объекты индикаторы и реализуются методы Update и Test для разных вариантов панели индикации в одном проекте.

В листинге 1 представлена функция Show абстрактного класса Indication. Это основная функция, непосредственно в которой происходит работа со сдвиговыми регистрами и для организации удобной работы с которой строится иерархия классов. Цикл for с индексом j перебирает все объекты Indicator. Цикл for с индексом i занимается перебором всех доступных бит в каждом из объектов Indicator, так как определенному типу индикатору соответствует свое кол-во подключаемых ножек сдвигающих регистров.

Листинг1. Алгоритм работы с буферными сдвиговыми регистрами 74HC595RM13TR.

#define OE_H              PORT_WriteBit ( MDR_PORTA, OE, Bit_SET )
#define OE_L            PORT_WriteBit ( MDR_PORTA, OE, Bit_RESET )
#define MR_H            PORT_WriteBit ( MDR_PORTA, MR, Bit_SET )
#define MR_L            PORT_WriteBit ( MDR_PORTA, MR, Bit_RESET )
#define STCP_H        PORT_WriteBit ( MDR_PORTA, STCP, Bit_SET )
#define STCP_L        PORT_WriteBit ( MDR_PORTA, STCP, Bit_RESET )
#define SHCP_H        PORT_WriteBit ( MDR_PORTA, SHCP, Bit_SET )
#define SHCP_L        PORT_WriteBit ( MDR_PORTA, SHCP, Bit_RESET )
#define DS_H            PORT_WriteBit ( MDR_PORTA, DS, Bit_SET )
#define DS_L            PORT_WriteBit ( MDR_PORTA, DS, Bit_RESET )
#define RESET_74HC595 MR_L; STCP_H; STCP_L; MR_H; OE_L;
#define TOUT          10
#define PAUSE         Delay(TOUT)

void Indication::Show()
{
        // Перебор индикаторов
        for(uint8_t j = 0;j < count;j++)
        {
        // Перебор всех битов значения value индиктора и запись
        // каждого бита в сдвиговый регистр.
                for(uint8_t i = indicators[j]->Bits();i > 0;i--)
                {
                        SHCP_L;
                        PAUSE;
                        (indicators[j]->Value()&(1<<(i-1)))? DS_H:DS_L;
                        PAUSE;
                        SHCP_H;
                        PAUSE;
                }
        }
        // Стробирующий импульс на выдачу значений на параллельный выход
        STCP_H;
        PAUSE;
        STCP_L;
        PAUSE;
}

В листинге 2 представлен абстрактный базовый класс Indicator и дочерние классы Led, LedDual и Digit. Член value данного класса – это выводимое значение для индикации, bits – кол-во бит для индикации. Классы Led, LedDual, Digit наследуются от абстрактного базового класса Indicator. Для каждого из этих классов value и bits имеет свое значение. В Led::value задается состояние выключен/включен, а bits = 1. Для LedDual::value задает состояния выключен/включен зеленый/включен красный/мигающий зеленый/мигающий красный, bits = 2. Для Digit::value – это значение выводимое на индикаторе, а bits = 8. Всю конкретику работы мы реализуем в дочерних классах, а использовать объекты этих классов будем через абстрактный интерфейс!

Листинг 2. Класс Indicator.

class Indicator
{
protected:
        uint8_t value; // Выводимое значение
        uint8_t bits; // Кол-во бит в значении
public:
        Indicator(){}
        // Получить значение.
        uint8_t Value() { return value; }
        // Установить значение.
        virtual void SetValue(uint8_t val) = 0;
        // Получить кол-во бит.
        uint8_t Bits() { return bits; }
        // Перегрузка оператора равно.
        virtual void operator=(uint8_t val) = 0;
};

class Led : public Indicator
{
public:
                Led() { 
                bits = 1; 
      value = 0;
    }
    void operator=(uint8_t val) { value = val;}
    void SetValue(uint8_t val) { value = val;}
    void Off() { value = 0;}
    void On() { value = 1;}
};

class LedDual : public Indicator
{  
public:
        LedDual() {
                bits = 2; 
                value = 0;
  }
  void operator=(uint8_t val) { SetValue(val); }
  // Set val as
        // 0 - off
        // 1 - on green
        // 2 - on red
        // 3 - blink green
        // 4 - blink red
        void SetValue(uint8_t val) { 
                if      ( val == 3 ) value = (value & 1)^ 1;
                else if ( val == 4 ) value = (value & 2)^ 2;
                else if ( val == 1 ) value = 1;
                else if ( val == 2 ) value = 2;
                else value = 0;
}
void Off() { value = 0;}
void Green() { value = 1;}
void Red() { value = 2;}
void BlinkGreen() { value = 3;}
void BlinkRed() { value = 4;}
};

class Digit : public Indicator
{
        static const uint8_t segCode[];
public:
        Digit() {
        bits = 8; 
        value = 0;
    }
  void operator=(uint8_t val) { value = segCode[val];}
        void SetValue(uint8_t val) { value = segCode[val];}
};

#define A_ (uint8_t)(1<<1)
#define B_ (uint8_t)(1<<4)
#define C_ (uint8_t)(1<<5)
#define D_ (uint8_t)(1<<0)
#define E_ (uint8_t)(1<<3)
#define F_ (uint8_t)(1<<6)
#define G_ (uint8_t)(1<<7)
#define H_ (uint8_t)(1<<2)

const uint8_t Digit::segCode[] = {
/* без точки */
/*00*/ /*0 */(uint8_t)(~(A_|B_|C_|D_|E_|F_)),
/*01*/ /*1 */(uint8_t)(~(B_|C_)),
/*02*/ /*2 */(uint8_t)(~(A_|B_|D_|E_|G_)),
/*03*/ /*3 */(uint8_t)(~(A_|B_|C_|D_|G_)),
/*04*/ /*4 */(uint8_t)(~(B_|C_|F_|G_)),
/*05*/ /*5 */(uint8_t)(~(A_|C_|D_|F_|G_)),
/*06*/ /*6 */(uint8_t)(~(A_|C_|D_|E_|F_|G_)),
/*07*/ /*7 */(uint8_t)(~(A_|B_|C_)),
/*08*/ /*8 */(uint8_t)(~(A_|B_|C_|D_|E_|F_|G_)),
/*09*/ /*9 */(uint8_t)(~(A_|B_|C_|D_|F_|G_)),
/* с точкой */
/*10*/ /*0.*/(uint8_t)(~(A_|B_|C_|D_|E_|F_|H_)),
/*11*/ /*1.*/(uint8_t)(~(B_|C_|H_)),
/*12*/ /*2.*/(uint8_t)(~(A_|B_|D_|E_|G_|H_)),
/*13*/ /*3.*/(uint8_t)(~(A_|B_|C_|D_|G_|H_)),
/*14*/ /*4.*/(uint8_t)(~(B_|C_|F_|G_|H_)),
/*15*/ /*5.*/(uint8_t)(~(A_|C_|D_|F_|G_|H_)),
/*16*/ /*6.*/(uint8_t)(~(A_|C_|D_|E_|F_|G_|H_)),
/*17*/ /*7.*/(uint8_t)(~(A_|B_|C_|H_)),
/*18*/ /*8.*/(uint8_t)(~(A_|B_|C_|D_|E_|F_|G_|H_)),
/*19*/ /*9.*/(uint8_t)(~(A_|B_|C_|D_|F_|G_|H_)),
/*20*/ /*_ */(uint8_t)(~(D_)),
/*21*/ /*P */(uint8_t)(~(A_|B_|E_|F_|G_)),
/*22*/ /*t */(uint8_t)(~(E_|D_|F_|G_)),
/*23*/ /*° */(uint8_t)(~(A_|B_|F_|G_)),
/*24*/ /*П */(uint8_t)(~(A_|B_|C_|E_|F_)),
/*25*/ /*с */(uint8_t)(~(D_|E_|G_)),
/*26*/ /*S */(uint8_t)(~(A_|C_|D_|F_|G_)),
/*27*/ /*С */(uint8_t)(~(A_|D_|F_|E_)),
/*28*/ /*E */(uint8_t)(~(A_|E_|D_|F_|G_)),
/*29*/ /*U */(uint8_t)(~(B_|C_|D_|E_|F_)),
/*30*/ /*  */(uint8_t)(0xFF),
/*31*/ /*- */(uint8_t)(~(G_)),
/*32*/ /*H */(uint8_t)(~(B_|C_|E_|F_|G_))
};

Класс Indication является абстрактным и используется для наследования от него классов с различными вариантами наборов индикаторов и реализаций функций Update и Test. В функцию Update передаются данные, на основании которых выполняется обновление индицируемых значений.

Листинг 3. Абстрактный класс Indication.

#define SHCP      PORT_Pin_0
#define DS              PORT_Pin_1
#define STCP    PORT_Pin_2
#define MR              PORT_Pin_3
#define OE              PORT_Pin_4

class Indication
{
        int count; // кол-во индикаторов
        Indicator** indicators; // указатель на массив указателей
  // объектов Indicator
public:
        Indication();
        // Обновить состояние членов класса.
        virtual void Update(const state_t & state){}
        // Засветить все светодиоды, для визуальной проверки исправности.
        virtual void Test(){}
        // Непосредственный вывод значений в сдвиговые регистры.
  void Show();
        // Сброс-очистка регистров.
  void Reset() { RESET_74HC595;}
  // Установить кол-во индикаторов
  void SetCount(int n) { count = n; }
  // Установить указатель массива указателей
  void SetIndicators(Indicator** ind) { indicators = ind;}
        void HGgroup(uint32_t v, Digit & d1, Digit & d2, Digit & d3, uint8_t isDigit)
        {       
                d1 = v / 100;
                d2 = (v % 100)/10 + (isDigit ? 10 : 0);
                d3 = v % 10;
        }
};

Листинг 4. Дочерний класс Indication_v1.

class Indication_v1 : public Indication
{
  Led HL1;
  Led HL2;
  LedDual HL3;
  LedDual HL4;
  LedDual HL5;
        Digit HG3;
        Digit HG2;
        Digit HG1;
        enum{n = 8};
        Indicator* indicators[n];

public:
        Indication_v1();
        void Update(const state_t & arg);
        void Test();
};

Indication_v1::Indication_v1()
    :Indication()
{
        indicators[0] = &HL5;
        indicators[1] = &HL4;
        indicators[2] = &HL3;
        indicators[3] = &HL2;
        indicators[4] = &HL1;
        indicators[5] = &HG3;
        indicators[6] = &HG2;
        indicators[7] = &HG1;       
Indication::SetCount(n);
        Indication::SetIndicators(indicators);
}

void Indication_v1::Update(const state_t & arg)
{
                        int voltage = 285;
        HGgroup(voltage, HG1, HG2, HG3, 1);
        HL1.On();
        HL2.On();
        HL3.Red();
        HL4.Green();
        HL5.BlinkGreen();}

void Indication_v1::Test()
{
// Зажигаем на некоторое время все светодиоды.
// Для проверки не работающих.
// Зажигаем красные
                        HL3.Red();
        HL4.Red();
                HL5.Red();
        HG1 = 18;
        HG2 = 18;
        HG3 = 18;
        Show();
        Delay (0x1FFFFFF);
// Зажигаем зеленые
        HL3.Green();
        HL4.Green();
        HL5.Green();
        HL1.On();
        HL2.On();
                Show();
        Delay (0x1FFFFFF);
}

Для использования класса создаем объект класса в следующем виде:

#if ( VERS == 0 )
Indication_v1 indication;
#elif ( VERS == 1)      
Indication_v2 indication;
#endif

indication.Reset();
while(1)
{
        indication.Update(arg);
        indication.Show();
  sleep()
}

Данная структура проекта позволяет управлять индикаторами, используя их позиционные имена с помощью простых операторов (HL1 = 1 или HL1.Red()), т.е. ваша программа будет визуально соответствовать схеме и разрабатываться используя позиционные обозначения, представленные на схеме. Что очень удобно. Изменяемая часть, такая как версия панели инкапсулирована в отдельный класс, что дает возможность создавать новые классы панелей Indication_v2, Indication_v3, не портя алгоритмов уже разработанных панелей.

Adblock test (Why?)

1 комментарий:

  1. Thanks for sharing such a valuable information and knowledge. It is very useful and informative.
    As the world market is more globalized and united to a single market, Atomybrand is positioned to gain momentum and growth.Since its foundation in 2009, Atomy began establishing overseas branch offices, starting with Atomy USA in 2010, followed by Japan, Canada, Taiwan, Singapore, Cambodia, the Philippines, Malaysia, Mexico, and Thailand. Atomy’s policy of high-quality goods for the masses, along with its relentless efforts in global markets, worked together to achieve an amazing feat: winning not one but numerous Export Tower Awards ($5 million in 2011, $10 million in 2013, $20 million in 2015, $30 million in 2016, and now $50 million in 2017). Atomy is planning to open branch offices in Australia, Vietnam, and Indonesia during 2018, and develop the Chinese market – the world’s largest direct sales market – in 2019. As ever, Atomy is committed to devoting efforts to its vision of becoming a global distribution hub.Built on a foundation of principles, Atomy will lead the industry to reshape recognition and reputation.Atomy follows 1. Principle Centered Culture 2. Culture of Accompanied Growth 3. Sharing Culture. Our objective is beyond consumer satisfaction; rather it is centred for success.
    A corporation that cherishes the spirit! We believe that valuing individuals is our priority. Be our partner in faith and believe in your own success through Atomy. We will always serve you with the utmost humble heart. Enjoy a beautiful life with .Atomy and make your dreams come true.

    ОтветитьУдалить