середу, 2 листопада 2016 р.

Мій проект: Лічильник подій 4x4 (поточні, попередні, різниця)


Передмова

Цей допис роблю для себе, як документацію на пристрій. Пристрій робився на замовлення в одному екземплярі. Як кому згодиться то дуже добре.

Призначення

Зовнішній вигляд готового пристрою
Лічильник подій 4х4 фіксує і показує на екрані поточні показники лічильників, різницю між поточними показами лічильників і попереднім зняттям показів лічильників. Показання лічильників виводяться на чотироьохрозрядний семисегментний індикатор по черзі.
Слугує заміною застарілим електромеханічним лічильникам, які вже використали свій ресурс і працюють з великою похибкою.

Як користуватись

Після зняття показів всіх лічильників, натиснути кнопку для фіксації, що значення лічильників занесено до журналу і розрахунок різниці ведеться вже з поточних показів лічильників.

Схеми блоків

Блок фіксації події, гальванічної розв'язки та антибрязкіт.
Схема блоків оптичної розв'язки і антибрязкіт
Мікроконтролерний блок STM32F103C8T6.
Схема мікроконтролерного блоку
Блок індикації і кнопки.
Схема блока індикації і кнопки
Блок живлення.
Схема блоку живлення
Архів зі схемами. Живлення пристрою від акумуляторів 24 Вольта великої ємності в режимі "буфер". Зарядний пристрій до акумуляторів живиться від гарантованої мережі 220В. Додаткового резервного живлення не потребує.

Програмне забезпечення

Початкова ініціалізація периферії згенерована програмою CubeMX. Програма написана мовою C в засобі розробки CooCox CoIDE 1.7. Архів з проектом.

  • mxconstants.h - константи, структури
  • main.c - головна програма
  • stm32f1xx_it.c - обробник переривань
Дещо з коду під спойлером.



Прототипи функцій.
/* USER CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*/
static void DigitToPort (uint8_t digit); // виставляє в відповідні розряди індикатора числа-розряди.
static void ShowScreen(uint8_t* number, uint8_t number_of_digits, const uint16_t* pDigitPin); // Вивід числа на екран. Аргументи: масив з чисел-розрядів після функції SplitToDigit, та кількість розрядів.
static void SplitToDigit(uint32_t counter, uint8_t* digit, uint8_t number_of_digits); // число з лічильника розбиває на окремі розряди. вхідні аргументи: значення лічильника, масив для розрядів, кількість розрядів.
/* USER CODE END PFP */

Деякі змінні.
/* USER CODE BEGIN 2 */
uint8_t digit[NUM_DIGIT] = {}; // Масив для символів в кожен розряд індикатора окремо
static const uint16_t digitPin[] = {D0_Pin,D1_Pin,D2_Pin,D3_Pin};
/* USER CODE END 2 */

Функції для роботи 7-сегментного дисплею
//Функція відображення інформації на дисплей
static void ShowScreen(uint8_t* number, uint8_t number_of_digits, const uint16_t* pDigitPin)
{
 for(uint8_t i = 0; i < number_of_digits; i++)
 {
  IND_PORT -> BRR = (D0_Pin|D1_Pin|D2_Pin|D3_Pin);//Вимикаємо всі розряди
  IND_PORT -> BSRR = pDigitPin[i];//Вмикаємо потрібний розряд індикатора
  DigitToPort(*(number+i));//Виводимо цифру у потрібний розряд
  HAL_Delay(1);//Невеличка затримка. Хай цифра світиться якийсь час
 }
}


// число з лічильника розбиває на окремі розряди. вхідні аргументи: значення лічильника, масив для розрядів, кількість розрядів.
static void SplitToDigit(uint32_t counter, uint8_t* digit, uint8_t number_of_digits)
{
 uint32_t tmp = counter;

 for (uint8_t i = 0; i < number_of_digits; i++)
 {
  *(digit+i) = tmp % 10;
  tmp = tmp / 10;
 }

}


//Функція виставляє в порт потрібну цифру
static void DigitToPort (uint8_t digit)
{
 static const uint8_t digitsp[]={DIG0,DIG1,DIG2,DIG3,DIG4,DIG5,DIG6,DIG7,DIG8,DIG9,SEG_G_Pin,LITA,~DIG8,LITP,LITH,LITPE}; //оголошуємо масив з можливими варіантами символами на індикатор
 IND_PORT -> ODR &= ~DIG8; //Вимикаємо всі сегменти
 IND_PORT -> ODR |= digitsp[digit]; //Запалюємо потрібні
}

Виклик функції для виводу інформації на дисплей. Або в безкінечному циклі, або по перериванням.
ShowScreen(digit,sizeof(digit)/sizeof(digit[0]),digitPin);




Оголошуємо масиви. Створюємо структуру і оголошуємо масив структур.
bool flag_button[NUM_COUNTER] = {}; // Масив з прапорцями стану отримання імпульсу на лічильник

typedef struct
{
 uint32_t current;
 uint32_t previous;
}CounterTypeDef;

CounterTypeDef AI_Counter[NUM_COUNTER] = {}; // Масив з кількістью структур для лічильників

static const uint16_t ButtonPin[] = {AI_COUNTER1_Pin,AI_COUNTER2_Pin,AI_COUNTER3_Pin,AI_COUNTER4_Pin,
                            AI_COUNTER5_Pin,AI_COUNTER6_Pin,AI_COUNTER7_Pin,AI_COUNTER8_Pin,
                            AI_COUNTER9_Pin,AI_COUNTER10_Pin,AI_COUNTER11_Pin,AI_COUNTER12_Pin,
                            AI_COUNTER13_Pin,AI_COUNTER14_Pin,AI_COUNTER15_Pin,AI_COUNTER16_Pin};

static GPIO_TypeDef* ButtonPort[] = {AI_COUNTER1_GPIO_Port,AI_COUNTER2_GPIO_Port,AI_COUNTER3_GPIO_Port,AI_COUNTER4_GPIO_Port,
                            AI_COUNTER5_GPIO_Port,AI_COUNTER6_GPIO_Port,AI_COUNTER7_GPIO_Port,AI_COUNTER8_GPIO_Port,
                            AI_COUNTER9_GPIO_Port,AI_COUNTER10_GPIO_Port,AI_COUNTER11_GPIO_Port,AI_COUNTER12_GPIO_Port,
                            AI_COUNTER13_GPIO_Port,AI_COUNTER14_GPIO_Port,AI_COUNTER15_GPIO_Port,AI_COUNTER16_GPIO_Port};

В циклі опитуємо входи на подію. Якщо прапорець "flag_bounce" в true.
if (flag_bounce)
 {
  flag_bounce = false;
    for (uint8_t i = 0; i < NUM_COUNTER; i++)
     {
       if (!HAL_GPIO_ReadPin(ButtonPort[i],ButtonPin[i]) && !flag_button[i])
     {
           flag_button[i] = true;
           ++AI_Counter[i].current;
     }
     else if(HAL_GPIO_ReadPin(ButtonPort[i],ButtonPin[i]) && flag_button[i])
          {
           flag_button[i] = false;
          }
     }
   }

Увімкнено два таймери. Таймер TIM2 в перериванні скидає прапорець "flag_lock_screen" для зміни інформації на екрані що 3 секунди. Таймер TIM4 в перериванні встановлює прапорець "flag_bounce" для дозволу опитування входів на подію через кожні 25 мілісекунд (програмний антибрязкіт).

Немає коментарів:

Дописати коментар