Сб авг 17, 2013 18:27:55
Пт янв 03, 2014 21:51:16
Пт мар 21, 2014 11:26:47
/*
* METER-CLOCK AND WATCH
* Часы с будильником и индикацией при помощи стрелочных миллиамперметров
*
*/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/eeprom.h>
#include "avr_helper.h"
// число "тиков" 0-го таймера за 1 секунду
#define TICKS_PER_SEC 125
// шкала времени - 12-часовая, т.к. 24 деления на индикаторе - слишком много
#define HOURS_SCALE 12
// значение максимума для 10-битного ШИМА часов
#define PWM_MAX_H 1023UL
// значение максимума для 10-битного ШИМА минут
#define PWM_MAX_M 1023UL
// шаг ШИМа в 1 часе
#define PWM_HOUR_STEP (PWM_MAX_H / HOURS_SCALE)
// шаг ШИМа в 1 минуте
#define PWM_MIN_STEP (PWM_MAX_M / 60)
// вывод, к которому подключени вспомогательный индикатор
#define DIV_PIN _BV(PB2)
// вывод, к которому подключен сигнал будильника
#define ALARM_OUT _BV(PB7)
// вывод, к которому подключен переключатель КОРРЕКЦИИ ЧАСОВ
#define SET_HOUR _BV(PD6)
// вывод, к которому подключен переключатель КОРРЕКЦИИ МИНУТ
#define SET_MIN _BV(PD5)
// вывод, к которому подключен переключатель режима индикации ЧАСЫ-БУДИЛЬНИК
#define ALARM_MODE _BV(PD0)
// коэффициент ШИМа вспомогательного индикатора ДО ПОЛУДНЯ
#define DIVIDER_AM 20
// коэффициент ШИМа вспомогательного индикатора ПОСЛЕ ПОЛУДНЯ
#define DIVIDER_PM 100
// коэффициент ШИМа вспомогательного индикатора ЦЕНТР
#define DIVIDER_CENTER 62
// для "плавной" индикации надо задать следующий макрос равным НЕ НУЛЮ
#define SOFT_TIME 0
// проверка правильности коэффициентов ШИМа вспомогательного индикатора
#if (DIVIDER_AM > TICKS_PER_SEC) | (DIVIDER_PM > TICKS_PER_SEC) | (DIVIDER_CENTER > TICKS_PER_SEC)
#error DIVIDER_xxx must equal or lower than TICKS_PER_SEC
#endif
// макрос проверки включения режима индикации будильника
#define is_alarm_mode() ((PIND & ALARM_MODE) == 0)
// макрос проверки режима КОРРЕКЦИИ
#define is_set_mode() (((PIND & (SET_HOUR | SET_MIN)) ^ (SET_HOUR | SET_MIN)) != 0)
// макрос проверки режима КОРРЕКЦИИ ЧАСОВ
#define is_set_hour_mode() ((PIND & (SET_HOUR | SET_MIN)) == SET_MIN)
// макрос, контролирующий "плавную" индикацию времени
#define soft_time() (SOFT_TIME && !is_alarm_mode() && !is_set_mode())
// флажок "прошла 1 секунда"
volatile uint8_t one_sec_flag;
// текущее время
volatile uint8_t hour = 10;
volatile uint8_t min = 36;
volatile uint8_t sec = 0;
// вспомогательный флажок, инвертирующися каждую секунду
volatile uint8_t toggle = 0;
// ячеки в EEPROM для хранения уставки будильника
EEMEM uint8_t dummy = 0; // это индикатор "первого" запуска программы
EEMEM uint8_t e_hour, e_min;
// вывод времени на индикаторы
static void show_time(uint8_t h, uint8_t m, uint8_t s){
static uint8_t sec_ocr = 0;
uint16_t ocr;
// индикация на вспомогательном индикаторе
if(toggle){
sec_ocr = DIVIDER_CENTER;
} else {
sec_ocr = h < HOURS_SCALE ? DIVIDER_AM : DIVIDER_PM;
}
OCR0B = sec_ocr;
toggle = !toggle;
// вычисление значения коэффициента ШИМ для индикатора часов
if(h > HOURS_SCALE) h -= HOURS_SCALE;
ocr = PWM_HOUR_STEP * h;
if(soft_time() && !(h == HOURS_SCALE)) ocr += PWM_HOUR_STEP * m / 60;
OCR1A = ocr;
// вычисление значения коэффициента ШИМ для индикатора минут
ocr = PWM_MIN_STEP * m;
if(soft_time()) ocr += PWM_MIN_STEP * s / 60;
OCR1B = ocr;
}
// обработчик прерывания по совпадению канала B 0-го таймера
ISR(TIMER0_COMPB_vect){
// выключение сигнала на вспомогательном индикаторе
PORTB &= ~DIV_PIN;
}
// Обработчик прерывания по совпадению 0-го таймера
// Вызывается TICKS_PER_SEC раз в секунду
ISR(TIMER0_COMPA_vect){
static uint8_t counter = TICKS_PER_SEC;
// считаем время
if(!--counter){
counter = TICKS_PER_SEC;
if(++sec >= 60){
sec = 0;
if(++min >= 60){
min = 0;
sec = 28; // корректировка - каждый час +28.8 секунд
if(++hour >= (HOURS_SCALE*2)){
hour = 0;
}
}
}
// устанавливаем флажок каждую секунду
one_sec_flag = 1;
}
// включаем сигнал на вспомогательном индикаторе
PORTB |= DIV_PIN;
}
MAIN(){
// уставка будильника
uint8_t alarm_hour, alarm_min;
if(eeprom_read_byte(&dummy) != 0){
// первый запуск - EEPROM чистая
correct_alarm:
eeprom_update_byte(&e_hour, 7);
eeprom_update_byte(&e_min, 0);
eeprom_update_byte(&dummy, 0);
}
// считывание уставки будильника
alarm_hour = eeprom_read_byte(&e_hour);
alarm_min = eeprom_read_byte(&e_min);
// корректировка уставки для страховки
if(alarm_hour >= 24){
alarm_hour = 7;
goto correct_alarm;
}
if(alarm_min >= 60){
alarm_min = 0;
goto correct_alarm;
}
// таймер 0 - счет времени: делитель 256 и режим CTC с коэффициентом 125 - итого деление 32000
// а так же управление индикатором-разделителем в режиме программного ШИМ
OCR0A = TICKS_PER_SEC;
TCCR0A = _BV(WGM01); // CTC
TCCR0B = TIMER_CLK_DIV_256;
// таймер 1 обеспечивает просто 10-битный FastPWM для управления стрелками часов и минут
TCCR1A = _BV(WGM10) | _BV(WGM11) | TIMER_OC_CLEAR(1A) | TIMER_OC_CLEAR(1B);
TCCR1B = TIMER_CLK_DIV_1;
// прерывания разрешены от 0-го таймера по совпадению каналов А и B
TIMSK = _BV(OCIE0A) | _BV(OCIE0B);
// порты переключателей
DDRD = 0;
PORTD = 255;
sei();
while(1){
if(ACSR & _BV(ACO)){
// если компаратор сработал - питание от батарейки - выключить индикацию
DDRB &= ~(_BV(PB4) | _BV(PB3) | DIV_PIN);
} else {
// иначе обычная индикация
DDRB |= (_BV(PB4) | _BV(PB3) | DIV_PIN);
}
while((PIND & (_BV(PD4) | _BV(PD3) | _BV(PD2))) ^ (_BV(PD4) | _BV(PD3) | _BV(PD2))){
// режим калибровки индикаторов
OCR1A = PWM_MAX_H;
OCR1B = PWM_MAX_M;
}
if(is_alarm_mode()){
// режим будильника
if(is_set_mode()){
// режим корректировки
if(is_set_hour_mode()){
// корректировка часов
if(++alarm_hour >= (HOURS_SCALE * 2)) alarm_hour = 0;
} else {
// корректировка минут
if(++alarm_min >= 60) alarm_min = 0;
}
}
// вывод уставки будильника
show_time(alarm_hour, alarm_min, 0);
// обновление памяти
eeprom_update_byte(&e_hour, alarm_hour);
eeprom_update_byte(&e_min, alarm_min);
} else {
// режим индикации времени
if(is_set_mode()){
// режим корректировки
if(is_set_hour_mode()){
// коррекция часов
if(++hour >= (HOURS_SCALE * 2)) hour = 0;
} else {
// корреция минут
if(++min >= 60) min = 0;
}
}
// индикация времени
show_time(hour, min, sec);
}
// срабатывание будильника
if((hour == alarm_hour) && (min == alarm_min) && toggle){
PORTB |= ALARM_OUT;
} else {
PORTB &= ~ALARM_OUT;
}
// ожидание флага обновления
while(!one_sec_flag && );
one_sec_flag = 0;
}
}
Пт мар 21, 2014 15:56:46
Пт дек 11, 2015 22:15:40
Сб дек 26, 2015 00:49:53
Ср июл 13, 2016 08:05:17
Ср июл 13, 2016 15:20:45
Вт июл 26, 2016 08:49:05