Привет! Это снова я.
Поспрашивал я немного в этих ваших интернетах про предыдущую статью и, оказалось, нужно писать ещё... подробнее. Буду исправляться! И начну с самого начала.
Со схемы.
Сама плата выглядит примерно так:
Надписи, в отличие, например, от Ардуино Нано обозначают не аналоговые или цифровые порты, а аббревиатуру от аббревиатуры GPIO PORT X PIN X. Таким образом надпись "D5" обозначает пин 5 на порте 4. И в среде разработки MounRiver Studio при настройке порта нужно сначала включить тактирование всего порта, а уже потом настраивать пин, как нам нужно.
Приближаясь к теме статьи.
Самая большая сложность в отображении информации на семисегментных индикаторах в том, что под каждый из них нужно по семь выводов микроконтроллера. И чтобы сделать простенькие часы только под отображение времени нужно 28 выводов контроллера! Или нет? На самом деле все сложнее и интереснее! Есть такая "штука", как динамическая индикация. То есть мы можем отображать информацию на каждом из сегментов по очереди. Используя подключение индикаторов параллельно. И в момент отображения цифры на индикаторе включать только его, подавая питание на общий вывод или притягивая его к земле, в зависимости от конструкции. Это как бежать вдоль забора от собаки на другой стороне. И мы ее видим и она нас. Весело! Но стоит только остановиться, как из всей собаки виден только злой глаз. Это явление называется инерционность зрения.
Чтобы избежать остановок и достичь скорости переключения индикаторов, необходимой для активации нужного эффекта без визуальных подергиваний нужно в первую очередь избавить программу от функуии DelayMs();, которая просто останавливает всю работу контроллера на время.
Для того, чтобы избежать задержек обычно используются встроенные таймеры контроллера. Обратимся на сайт производителя за информацией на контроллер.
Как видно из структурной схемы микроконтроллера нам доступны два внутренних таймера:
1×16-bit general-purpose timer - GPTM*1 - TIM2
1×16-bit advanced-control timer - Advanced TM*1 -TIM1
С нашей задачей справится любой из таймеров. Выберем TIM1 и проинициализируем. Для этого создадим функцию TIM_init(); Располагаться она может как до, так и после функции инициализации портов в теле программы.
void TIM_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);// инициализация таймера
TIM_CounterModeConfig(TIM1, TIM_CounterMode_Up);// режим прямого счета
TIM_SetAutoreload(TIM1, 100);// настройка автоперезагрузки
TIM_PrescalerConfig(TIM1, 48000 - 1, TIM_PSCReloadMode_Immediate); //прескаллер
TIM_Cmd(TIM1, ENABLE); //разрешение работы таймера
}
Функция TIM_SetAutoreload(TIM_TypeDef *TIMx, uint16_t Autoreload) позволяет выбрать период в миллисекундах до перезагрузки выбранного таймера, а для того, чтобы миллисекунды длились как раз-таки миллисекунды нужно настроить прескаллер. Цифра 48000 Была взята из настройки "#define SYSCLK_FREQ_48MHz_HSE 48000000", которая находится в файле "system_ch32v00x.c" в той же папке, что и файл "main.c" и нужна для выбора источника тактирования микроконтроллера и частоты этого самого тактирования.
Далее включаем в функцию main() нашу инициализацию и можем пользоваться таймером вместо задержки выполнения всей программы. Существует несколько способов, как можно это сделать. Лучшим решением было бы использование прерывания, но пока что будем использовать условный оператор и сравнивать значение обнуляющегося каждый период счетчика таймера с любым возможным значением из этого счетчика и выполнять какое-то действие при каждом совпадении. Период мы можем поставить любой, а срабатывание условия должно выполняться, так что выберем за значение для сравнения - "0" - он есть в любом периоде.
Для получения значений счетчика MRS(MounRiver Studio) предлагает использовать функцию "uint16_t TIM_GetCounter(TIMx)" - возвращает целые числа формата uint16_t. Так и поступим.
Помигаем цифрой СЕМЬ на нашем индикаторе.
Для этого будем зажигать и гасить каждый период светодиоды A, B, C.
int main(void) //обновленный вид функции "main()"
{
u8 i = 0; // переменная для хранения состояния светодиодов
SystemCoreClockUpdate();
GPIO_INIT(); // инициализация портов
TIM_Init(); // инициализация таймера
while(1) // бесконечный цикл
{
if(TIM_GetCounter(TIM1)==0){ // если значение счетчика равно нулю
if (i == 1) i=0; else i = 1; // меняем значение переменной состояния таймера на противоположное
}
GPIO_WriteBit(GPIOD, LED1_A ,i); // зажигаем(гасим) выбранный светодиод
GPIO_WriteBit(GPIOD, LED1_B ,i);
GPIO_WriteBit(GPIOD, LED1_C ,i);
}
}
Без рассеивателей, конечно, на цифру не очень похоже, но всему свое время.
Наша реализация имеет много недостатков, в том числе диоды мигают не стабильно. Работу над ошибками проведем в следующий раз.
Спасибо за внимание! Буду рад критике и комментариям.