Плата STM32 Discovery несколько лет назад
произвела настоящий «взрыв» интереса среди любителей микроконтроллерной
техники. За прошедшее время появились как новые процессоры, так и новые
варианты отладочных плат Discovery.
Тем не менее, первую плату еще можно использовать в качестве основы
разнообразных устройств. Одним из таких могут стать часы.
Реализация часовых алгоритмов на основе микроконтроллеров STM32 не создает особых
проблем. Встроенный модуль RTC
со всей необходимой обвязкой уже присутствует в составе процессора. Достаточно
только его настроить и можно работать. В применяемом контроллере STM32F100 модуль RTC несколько отличается от последующих
версий. В частности его счетный регистр не разделен на отдельные части, а выполнен
в виде двух 16-разрядных последовательно соединенных счетчиков. Это означает
дополнительную потребность в преобразовании времени и даты, а также
невозможность жесткой привязки к календарю. Первоначально такое положение дел
кажется сложным, но как и многих других элементов STM32 все оказывается не таким уж и
страшным.
Схема часов |
Принципиальная схема часов не отличается от таковой в других
проектах. Индикатор, подключенный для реализации динамического режима, две
кнопки настройки и несколько служебных светодиодов составляют практически всю
схему. Все остальные элементы уже разведены и установлены на STM32VL Discovery. Все что требуется – запрограммировать алгоритм работы.
Условно этот алгоритм можно разбить на несколько частей: динамическая
индикация, настройка модуля RTC,
опрос часов, настройка времени. Для программирования выбран компилятор MikroC.
Динамическая индикация реализуется стандартно, через
прерывание. Особых проблем при ее реализации не возникло. Гораздо больше
времени ушло на запуск RTС.
Множество регистров, зачастую расположенных в разных блоках, запутывают
программиста. Тем не менее, результат получился простой и работоспособный.
Определение времени также не вызвало больших затруднений, так как реализуется
банальным делением. Например:
tmp = (RTC_CNTH << 16) + RTC_CNTL;
//Получение значения счетчиков
hour = ((tmp % (365 * 24 * 3600)) % (24 *
3600)) / 3600; //Часы
minutes = (((tmp % (365 * 24 * 3600)) % (24 *
3600)) % 3600)/ 60; //Минуты
seconds = (((tmp %
(365 * 24 * 3600)) % (24 * 3600)) % 3600)% 60; //Секунды
Если для 8-ми разрядных микроконтроллеров приведенный код
тяжел, то в 32-разрядных он не составляет особых проблем, так как деление
реализовано аппаратно.
Наибольшее время пришлось потратить на реализацию алгоритма
установки времени. Первоначальное решение сделать его через прерывания на
кнопках показалось громоздким и плохо работающим, поэтому было решено вернуться
к чему-то подобному, что легко прописывалось в 8-ми разрядных системах.
Установка времени выполнена простым сложением счетчика RTC и числа 60 для увеличения минут, или
3600 для часов. Такая операция очень проста. Единственный ее недостаток
заключается в изменении значения часов, если минуты становятся больше 59.
Схема первоначально была собрана на макетной плате и
показала хорошую работоспособность. Сейчас есть желание сделать шилд под STM32VL Discovery, на который
установить термометр, датчик давления, реле и звонок.
Программа часов
#define
Dig1_On GPIOB_ODR.B1
#define
Dig2_On GPIOB_ODR.B0
#define
Dig3_On GPIOB_ODR.B4
#define
Dig4_On GPIOB_ODR.B5
#define
mig_max 300
unsigned
long tmp;
int
Dig_show; //Текущая цифра
int
Dig1,Dig2,Dig3,Dig4; //Цифры для индикации
int hour,
minutes, seconds; //Время
int
mig,rezim,sec;
int
btn0,cnt_btn0,btn1,btn1s,cnt_btn1; //Кнопки
void
Read_RTC(){
tmp = (RTC_CNTH << 16) + RTC_CNTL;
hour = ((tmp % (365 * 24 * 3600)) % (24 *
3600)) / 3600;
minutes = (((tmp % (365 * 24 * 3600)) % (24 *
3600)) % 3600)/ 60;
seconds = (((tmp % (365 * 24 * 3600)) % (24 *
3600)) % 3600)% 60;
}
void
Set_DIG12(){
Dig1=hour/10;
Dig2=hour%10;
}
void
Set_DIG34(){
Dig3=minutes/10;
Dig4=minutes%10;
}
//Преобразование цифры в код индикатора
int Mask(int Dig){
dig=dig & 0x000f;
switch(Dig){
case 0:
{return 0xED00; break;}
case 1:
{return 0x2100; break;}
case 2:
{return 0x7C00; break;}
case 3:
{return 0x7900; break;}
case 4:
{return 0xB100; break;}
case 5:
{return 0xD900; break;}
case 6:
{return 0xDD00; break;}
case 7:
{return 0x6100; break;}
case 8:
{return 0xFD00; break;}
case 9:
{return 0xF900; break;}
case 10: {return 0x0000; break;}
}
}
//Прерывание по таймеру2. Переключение цифр на индикаторе
void
Timer2_interrupt() iv IVT_INT_TIM2 {
TIM2_SR.UIF = 0;
if (RTC_CRL.secf) {sec=~sec; RTC_CRL.secf=0;}
//Секундной мигание
switch(Dig_show){
case 1: {Dig4_On=0; GPIOB_ODR =
mask(Dig1); Dig1_On=1; break;}
case 2: {Dig1_On=0; if (sec) GPIOB_ODR = mask(Dig2)+0x0200;
else GPIOB_ODR =
mask(Dig2);
Dig2_On=1; break;}
case 3: {Dig2_On=0; GPIOB_ODR = mask(Dig3);
Dig3_On=1; break;}
case 4: {Dig3_On=0; GPIOB_ODR =
mask(Dig4); Dig4_On=1; break;}
}
Dig_show++;
if(Dig_show>4) Dig_show=1;
mig++;
if
(mig>300)mig=0;
}
//Прерывание по таймеру 3. Выход из режима установки
void Timer3_interrupt() iv IVT_INT_TIM3 {
TIM3_SR.UIF = 0;
Rezim=0;
TIM3_CR1.CEN = 0;
}
void
Set_param(){
TIM3_CNT=0;
btn0=1;
btn1=1;
btn1s=0;
PWR_CR.DBP = 1;
while (rezim>0){
TIM3_CR1.CEN = 1;
//Переключение
режимов
cnt_btn0=0;
if (btn0==0){
do{cnt_btn0++;}while(GPIOC_IDR.B0&&(cnt_btn0<10000));
if(cnt_btn0==10000){rezim++; btn0=1;
TIM3_CNT=0;}
}
cnt_btn0=0;
if (btn0==1){
do{cnt_btn0++;}while(!GPIOC_IDR.B0&&(cnt_btn0<10000));
if(cnt_btn0==10000){btn0=0;}
}
if (rezim>2&&!btn0)rezim=0;
//Опрос кнопки установки
cnt_btn1=0;
if (btn1==0){
do{cnt_btn1++;}while(GPIOC_IDR.B1&&(cnt_btn1<10000));
if(cnt_btn1==10000){btn1=1; btn1s=1;
TIM3_CNT=0;}
}
cnt_btn1=0;
if (btn1==1){
do{cnt_btn1++;}while(!GPIOC_IDR.B1&&(cnt_btn1<10000));
if(cnt_btn1==10000){btn1=0;}
}
if (Rezim==1){
Read_RTC();
Set_DIG12();
if (mig>150){
Set_DIG34();}else {
Dig3=10;
Dig4=10;
if (btn1s){
RTC_CRL.CNF=1;
tmp=RTC_CNTL+60;
RTC_CNTL=tmp;
btn1s=0;
RTC_CRL.CNF=0;
}
}
}
if (Rezim==2){
Read_RTC();
Set_DIG34();
if (mig>150){
Set_DIG12();}else {
Dig1=10;
Dig2=10;
if (btn1s){
GPIOC_ODR.B11=~GPIOC_ODR.B11;
RTC_CRL.CNF=1;
tmp=(RTC_CNTH <<
16)+RTC_CNTL+3600;
RTC_CNTL=tmp&0x0000ffff;
RTC_CNTH=tmp >> 16;
RTC_CRL.CNF=0;
btn1s=0;
}
}
}
}
}
void
Set_RTC(){
int s;
/* Enable
the PWR clock */
RCC_APB1ENR.PWREN = 1;
RCC_APB1ENR.BKPEN = 1;
/* Allow
access to RTC */
PWR_CR.DBP = 1;
RCC_BDCR.RTCSEL0 = 1;
RCC_BDCR.RTCSEL1 = 0;
RCC_BDCR.RTCEN = 1;
RCC_BDCR.LSeoN =
1; //Включение внутреннего часового
генератора
/* Wait till LSE is ready */
s=1;
do{ delay_us(10); s++;
} while((s<50)||(!RCC_BDCR.LSERDY));
RTC_CRL.CNF=1;
RTC_PRLH=0;
RTC_PRLL=0x7FFF;
RTC_CRL.CNF=0;
RTC_CRL.RSF=1;
PWR_CR.DBP = 0;
}
void main()
{
GPIO_Alternate_Function_Enable(&_GPIO_MODULE_SWJ_JTAGDISABLE); //Включение RB4 как GPIO
GPIO_Config(&GPIOB_BASE,
_GPIO_PINMASK_4,
_GPIO_CFG_MODE_OUTPUT
|_GPIO_CFG_SPEED_MAX | _GPIO_CFG_OTYPE_PP);
GPIO_Digital_Output(&GPIOB_BASE,
_GPIO_PINMASK_0|_GPIO_PINMASK_1|_GPIO_PINMASK_4|_GPIO_PINMASK_5|_GPIO_PINMASK_8|_GPIO_PINMASK_9|_GPIO_PINMASK_10
|_GPIO_PINMASK_11|_GPIO_PINMASK_12|_GPIO_PINMASK_13|_GPIO_PINMASK_14|_GPIO_PINMASK_15);
GPIO_Digital_Output(&GPIOC_BASE,
_GPIO_PINMASK_8|_GPIO_PINMASK_9|_GPIO_PINMASK_10|_GPIO_PINMASK_11|_GPIO_PINMASK_12);
GPIO_Digital_Input(&GPIOC_BASE,
_GPIO_PINMASK_0|_GPIO_PINMASK_1);
Dig_show=1;
GPIOB_ODR=0;
GPIOC_ODR.B8=1;
EnableInterrupts();
//Настройка таймера
переключения цифр
RCC_APB1ENR.TIM2EN =
1;
delay_ms(100);
TIM2_CR1.CEN = 0; // Disable timer
TIM2_CNT = 1;
TIM2_PSC = 0; // Set timer prescaler.
TIM2_ARR = 10000;
TIM2_DIER.UIE = 1; // Update interrupt enable
TIM2_CR1.CEN = 1; // Enable timer
NVIC_IntEnable(IVT_INT_TIM2); // Enable timer
interrupt
//Настройка таймера выхода из режима
установки
RCC_APB1ENR.TIM3EN = 1;
delay_ms(100);
TIM3_CR1.CEN = 0; // Disable timer
TIM3_CNT = 1;
TIM3_PSC = 1000; // Set timer prescaler.
TIM3_ARR = 50000;
TIM3_DIER.UIE = 1; // Update interrupt enable
NVIC_IntEnable(IVT_INT_TIM3); // Enable timer
interrupt
Dig1=3;
Dig2=1;
Dig3=7;
Dig4=0;
rezim=0;
Set_RTC();
while(1){
//Опрос кнопки
настройки
cnt_btn0=0;
do{cnt_btn0++;}while(GPIOC_IDR.B0&&(cnt_btn0<10000));
if(cnt_btn0==10000){rezim=1; Set_Param();} //Переход в режим настройки
Delay_ms(200);
//Опрос часов
Read_RTC();
Set_DIG12();
Set_DIG34();
}
}
Комментариев нет:
Отправить комментарий