Простой счётчик на STM32

Теперь когда мы более-менее разобрались как рулить портами микроконтроллера можно попробовать сделать что-то практическое и даже немного полезное, дабы закрепить знания. Делать мы сегодня будем простейший счётчик, а считать он будет количество нажатий кнопки. Ну а кнопку в свою очередь можно прицепить куда угодно, например к двери, чтоб считать количество открываний 🙂 Это первое что пришло мне в голову, вероятно что вы придумаете ему куда более лучшее применение. В качестве устройства индикации мы будем использовать 4-х разрядный семисегментный индикатор. Он представляет собой вот такую железяку:

Может показаться немного странно, что выводов меньше чем должно быть. Сегментов у нас семь + точка итого 8 на разряд, а разрядов 4. Итого получаем 8 * 4 = 32 + как минимум один общий провод итого 33! Но на деле всего лишь 12 выводов. Такое уменьшение количества выводов достигается за счёт соединения вместе всех одинаковых сегментов. Таким образом наружу индикатора выходят 8 выводов (для сегментов) и 4 общих вывода (по одному на разряд). Возникает вопрос: А как вообще вывести какое-либо число на индикатор ? Ведь все сегменты соединены воедино и вывести две разные цифры в разные разряды в один момент времени не получится. Для решения этой проблемы придумали динамическую индикацию. Принцип её работы очень хорошо отражает следующая картинка: 

Из неё видно, что в любой момент времени на индикаторе загорается только один разряд. Разряды загораются по очереди, и очень быстро. Из-за инерционности человеческого зрения, нам кажется что все разряды горят одновременно. Прежде чем писать код, сначала разберёмся какие индикаторы бывают и как их подключить к контроллеру. Индикаторы бывают с общим катодом и общим анодом. Какие покупать это совершенно не принципиально, юзать можно любой, но придётся изменять код программы и немного подправить схему. Для того чтобы зажечь цифру на индикаторе с общим катодом нужно подать плюс питания на нужные сегменты, а общий вывод нужного разряда посадить на землю. Для индикатора с общим анодом всё наоборот: Сегменты соединяем с землёй, а на общий вывод разряда подаём напряжение питания. Теперь о подключении всего этого дела к контроллеру. Кроме самого индикатора нам еще потребуются 8 резисторов, или 7 если точка нам не нужна. Поскольку мы ваяем счётчик нажатий кнопки, то считать он будет целые числа, а следовательно точка нам ни к чему. Каждый сегмент индикатора это один обычный светодиод. А значит для того чтоб он не сгорел, нужно ограничить ток который через него проходит. Для этого мы будем использовать семь резисторов на на 220 ом каждый. Таким образом ток через каждую ножку подключенную к сегментам не превысит 15 мА что вполне допустимо. Но есть одна проблема с выводами контроллера которые подключаются к общим выводам индикатора: Если одновременно загорятся все 7 сегментов, то ток который протекает через общий вывод составит примерно 15*7=105 мА! От такой нагрузки контроллер может вспотеть и испустить волшебный дымок на котором работает вся электроника. Когда дымок покидает устройство — оно ломается 🙂 Для того чтоб уменьшить нагрузку на общие выводы, в схеме применяются ключевые транзисторы. Когда на базу транзистора поступает лог 1 с контроллера, он открывается и соединяет общий вывод индикатора с землёй, тем самым принимая всю нагрузку на себя. Схема всего этого выглядит так: 

Транзисторы можно применить вообще любые n-p-n типа. У меня нашлись BC547 вот их то я и использовал. Четыре резистора по 1К обязательны, они ограничивают ток базы. Если таких нет, то можно заюзать другие (не слишком большого сопротивления) лишь бы транзистор полностью открывался. Теперь о кнопке. у нас она будет подключена к ноге PA0, это удобно так как эта кнопка уже есть на STM32vl Discovery и припаивать её мне не придётся. На этом описание аппаратной части можно считать законченным. Прежде чем писать софт еще пара слов об алгоритме работы:

1) Настроить порты ввода/вывода: 
  1.1) Включить тактирование портов
  1.2) Настроить 11 пинов порта ХХ на выход
  1.3) Настроить пин PA0 на вход
2) Проверить состояние кнопки. 
  2.1) Если нажата то увеличить счётчик на 1.
  2.2) Если счётчик досчитал до 10000 то обнулить его
3) Разложить число в переменной счётчике на отдельные цифры
4) Поочередно вывести каждую цифру на соответствующий разряд индикатора
5) Перейти к шагу 2

Теперь, когда у нас есть хотябы примерный алгоритм можно приступать к написанию кода. 

#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
// Порт к которому подключен индикатор
#define IND_PORT GPIOB
// Общие выводы индикатора
#define D0 GPIO_Pin_7
#define D1 GPIO_Pin_8
#define D2 GPIO_Pin_9
#define D3 GPIO_Pin_10
// К какой ноге какой сегмент подключен
#define SEG_A GPIO_Pin_0
#define SEG_B GPIO_Pin_1
#define SEG_C GPIO_Pin_2
#define SEG_D GPIO_Pin_3
#define SEG_E GPIO_Pin_4
#define SEG_F GPIO_Pin_5
#define SEG_G GPIO_Pin_6
//Собираем цифры из сегментов
#define DIG0 ( SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F )
#define DIG1 ( SEG_B | SEG_C )
#define DIG2 ( SEG_A | SEG_B | SEG_G | SEG_E | SEG_D )
#define DIG3 ( SEG_A | SEG_B | SEG_G | SEG_C | SEG_D )
#define DIG4 ( SEG_F | SEG_G | SEG_B | SEG_C)
#define DIG5 ( SEG_A | SEG_F | SEG_G | SEG_C | SEG_D )
#define DIG6 ( SEG_A | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G )
#define DIG7 ( SEG_A | SEG_B | SEG_C )
#define DIG8 ( SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G)
#define DIG9 ( SEG_A | SEG_B | SEG_C | SEG_D | SEG_F | SEG_G)
#define ALL_PINS (DIG8 | D0 | D1 | D2 | D3 )
 
//Функция выводит в порт нужную цифру
void digit_to_port (uint8_t digit) {
  uint8_t digitsp[]={DIG0,DIG1,DIG2,DIG3,DIG4,DIG5,DIG6,DIG7,DIG8,DIG9};
  IND_PORT->ODR &= ~DIG8; //Выключаем все сегменты
  IND_PORT->ODR |= digitsp[digit]; //Зажигаем нужные
}
 
//Функция формирующая небольшую задержку
void Delay(void) {
  volatile uint32_t i;
  for (i=0; i != 0x1000; i++);
}
 
int main(void) {
  uint16_t counter=0; //Счётчик нажатий
  uint16_t tmp; // Содержит копию counter
                      // (из него по очереди исключаются последние цифры)
  uint8_t digit; // В эту переменную поочередно записываются цифры
                     // из которых состоит число counter
  uint8_t button_flag=0;
  //Выключаем JTAG (он занимает ноги нужные нам)
  RCC_APB2PeriphClockCmd( RCC_APB2Periph_AFIO , ENABLE);
  GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
  //Настраиваем на выход все ноги подключенные к индиакатору
  RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB , ENABLE);
  GPIO_InitTypeDef PORT; //Структура содержащая настройки порта
  PORT.GPIO_Pin = ALL_PINS; //Указываем какие ноги нужно настроить
  PORT.GPIO_Mode = GPIO_Mode_Out_PP; // Настраиваем как выход Push-pull
  PORT.GPIO_Speed = GPIO_Speed_2MHz; // Частота 2 МГц
  GPIO_Init(IND_PORT, &PORT); //Вызываем функцию настройки порта
  //Настраиваем ногу (PA0) с кнопкой на вход
  RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA , ENABLE);
  PORT.GPIO_Pin = GPIO_Pin_0;
  PORT.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_Init(GPIOA, &PORT);
  while(1) {
    if ((GPIOA->IDR & 0x01)==0x01) { //Кнопка нажата ?
      if (!button_flag) counter++;
      if (counter==10000) { //Досчитали до предела ? 
        counter=0; //обнуляем счётчик
      }
      button_flag=1;
      } else {
      button_flag=0;
    }
    tmp=counter;
    //Вытаскиваем первую справа цифру из числа counter
    digit = tmp % 10;
    tmp = tmp / 10;
    //Выключаем все разряды
    IND_PORT->ODR &= ~(D0|D1|D2|D3);
    //Включаем нулевой разряд индикатора
    IND_PORT->ODR |= D0;
    //Выводим цифру в нулевой разряд
    digit_to_port(digit);
    //Небольшая задержка. Пусть цифра погорит некоторое время
    Delay();
    //Вытаскиваем вторую справа цифру из числа counter
    digit = tmp % 10;
    tmp = tmp / 10;
    //Выключаем все разряды
    IND_PORT->ODR &= ~(D0|D1|D2|D3);
    //Включаем первый разряд индикатора
    IND_PORT->ODR |= D1;
    //Выводим цифру в первый разряд
    digit_to_port(digit);
    //Небольшая задержка. Пусть цифра погорит некоторое время
    Delay();
    //Вытаскиваем третью справа цифру из числа counter
    digit = tmp % 10;
    tmp = tmp / 10;
    //Выключаем все разряды
    IND_PORT->ODR &= ~(D0|D1|D2|D3);
    //Включаем второй разряд индикатора
    IND_PORT->ODR |= D2;
    //Выводим цифру в второй разряд
    digit_to_port(digit);
    //Небольшая задержка. Пусть цифра погорит некоторое время
    Delay();
    //Тут мы цифр не вытаскиваем. В переменной counter уже самая старшая цифра
    //Выключаем все разряды
    IND_PORT->ODR &= ~(D0|D1|D2|D3);
    //Включаем третий разряд индикатора
    IND_PORT->ODR |= D3;
    //Выводим цифру в третий разряд
    digit_to_port(tmp);
    //Небольшая задержка. Пусть цифра погорит некоторое время
    Delay();
  }
}

В этом коде я работаю с портами не напрямую через регистры, как в предыдущем примере, а использую специализированную библиотеку, а это значит что при создании нового проекта в CooCox’e нужно не забыть поставить галку GPIO. С использованием этой библиотеки код становится более читаемым, не нужно помнить о том для чего нужен каждый бит. Многое в этом коде откомментировано, думаю что особых проблем с пониманием написанного не возникнет. Ну а если вопросы появятся то можно задать их чуть ниже. Компилируем, прошиваем, запускаем и получаем в результате что-то типа такого: 

Работает! Жмем кнопку — значение счётчика увеличивается. Теперь можно сделать для всего этого отдельный корпус и найти счётчику достойное применение 🙂 

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