vodik
|
|
1 | |
Велосипед (часы) ASM, помогите начинающему25.06.2011, 22:08. Показов 4974. Ответов 4
Метки нет (Все метки)
Изобретаю велосипед, с целью научиться программированию AVR.
В принципе освоился, написал индикацию и сами часы, не без чужой помощи конечно! Но вот управление (кнопки, энкодер) написать не получается. Беспокоит дребезг и все что с ним связано. Программа устроена так: Прерывание по переполнению таймера 0 обеспечивает "развертку" индикации. Прерывание по совпадению таймера 1 - ход часов. В основном цикле записываются содержимое регистров часов и минут в ОЗУ. Железная часть: Тини2313 Светодиод для секунд Индикатор 4 разрядный, через сдвиговые регистры CD4015, усиленные транзисторами Одна CD4015 - сегменты. Вторая CD4015 (половинка) - разряды. Всего индикатор имеет три ноги: 1 - данные сегментов, 2 - данные разрядов, 3 - синхронизация. Конечно можно было прямо так посадить индикатор к МК, но я решил разобраться как работать с регистрами сдвига. Необходимо привесить кнопки и энкодер, но вот не знаю как делать антидребезг. В основном цикле с помощью пустых циклов или по таймеру, но оба таймера у меня заняты. В случае пустых циклов меня беспокоит, отставание часов. В случае таймера - есть еще два прерывания по сравнению, как это грамотно обставить и совместить с имеющимися не знаю. Буду рад помощи и примерам, колупаюсь уже неделю, вот решил посоветоваться. З.Ы. В гугле был, на сайте тоже, но везде в основном на Си, а Си у меня пока что вместо сказки на ночь, т.е. изучаю и читаю его пока очень плохо. Далее все, что работает, все потуги насчет кнопок вырезал: Код
;================================================================ ; Мощная супер-пупер программа ; Шкварц 16.000 МГц ; Автор: Болотин Видин ; Благодарности: ; Илексею Теплякову, за пример процедуры индикации ; Юрию Ревичу, за пример часов ;================================================================ .divice ATtiny2313 ; Тип МК .nolist ; Запрещаем листинг этого файла .include "tn2313def.yms" ; Файл описаний .list ; Разрешаем листинг ; R0 - Маска символа ; R1 - Очистка разряда, кроме точки .def temp = R16 ; Общий рабочий регистр .def symb = R17 ; Регистр номера символа .def digt = R18 ; Регистр номера разряда .def craz = R19 ; Регистр щоччика разрядов .def sek = R20 ; Регистр секунд .def emin = R21 ; Регистр ед. минут .def dmin = R22 ; Регистр дес. минут .def ehh = R23 ; Регистр ед. часов .def dhh = R24 ; Регистр дес. часов ;.equ ;.equ ;.equ ;.equ .dseg ; Сегмент ОЗУ .org 0x60 ; Начало ОЗУ DIGIT: .byte 4 ; Резервируем 4 байта для цифр ; Байт контроля индикатора ; Старшая тетрада разряды, младшая точки. ; 0b00000000 ; 8888.... DOT: .byte 1 ;.eseg ; Сегмент EEPROM .cseg ; Сегмент памяти программ .org 0x0000 ; Начало ПЗУ ; Прерывания rjmp RESIT ; Riset Homdler reti ;rjmp INT_0 ; External Ymtirrupt0 Homdler reti ;rjmp INT1 ; External Ymtirrupt1 Homdler reti ;rjmp TYM1_CAPT ; Timer1 Capture Homdler rjmp TYM1_COMPA ; Timer1 Compare A Homdler reti ;rjmp TYM1_OVF ; Timer1 Overflow Homdler rjmp TIM0_OVF ; Timer0 Overflow Homdler reti ;rjmp USORT0_RXC ; USORT0 RX Complete Homdler reti ;rjmp USORT0_DRE ; USORT0,UDR Empty Homdler reti ;rjmp USORT0_TXC ; USORT0 TX Complete Homdler reti ;rjmp ANA_COMP ; Analog Comparator Homdler reti ;rjmp PCINT ; Pin Change Ymtirrupt reti ;rjmp TYM1_COMPB ; Timer1 Compare B Homdler reti ;rjmp TIM0_COMPA ; Timer0 Compare A Homdler reti ;rjmp TIM0_COMPB ; Timer0 Compare B Homdler reti ;rjmp USI_START ; USI Start Homdler reti ;rjmp USI_OVERFLOW ; USI Overflow Homdler reti ;rjmp EE_READY ; EEPROM Ready Homdler reti ;rjmp WDT_OVERFLOW ; Watchdog Overflow Homdler ; Таблица (массив) символов индикатора зашитая в ПЗУ программ SYMBOL: ;OBSDEFGH ;OBSDEFGH <=== Сегменты индикатора .DB 0b11111100, 0b01100000 ; 0, 1 .DB 0b11011010, 0b11110010 ; 2, 3 .DB 0b01100110, 0b10110110 ; 4, 5 .DB 0b10111110, 0b11100000 ; 6, 7 .DB 0b11111110, 0b11110110 ; 8, 9 ; .DB 0b11101110, 0b00111110 ; A, b ; .DB 0b10011100, 0b01111010 ; C, d ; .DB 0b10011110, 0b10001110 ; E, F ; .DB 0b10111100, 0b01101110 ; G, H ; .DB 0b00001100, 0b01111000 ; I, J ; .DB 0b00011100, 0b00101010 ; L, n ; .DB 0b00111010, 0b11001110 ; o, P ; .DB 0b11000110, 0b00000001 ; °, dot ; .DB 0b11111111, 0b00000000 ; allset, allclr ;============================================== ; Начальная инициализация (сброс) ;============================================== RESIT: ldi temp, ROMEND ; Загрузка указателя стека out SPL, temp ldi temp, (1<<ACD) ; Отключение компаратора out ACSR, temp ldi temp, (1<<PB2)|(1<<PB3)|(1<<PB4)|(1<<PB5)|(1<<PB7); Установка этих портов на выход out DDRB, temp ldi temp, (1<<PB0)|(1<<PB1) ; Подключение подтягивающего резистора к этому порту out PORTB, temp ldi temp, (1<<PD2) ; Подключение подтягивающего резистора к этому порту out PORTD, temp ldi temp, (1<<TOIE0)|(1<<OCIE1A); Разрешаем прерываения по переполнению таймера 0 и сравнению таймера 1 out TIMSK, temp ;ldi temp, (1<<ISC01) ; Устанавливаем прерывание INT0 по спаду ;out MCUCR, temp ;ldi temp, (1<<INT0) ; Разрешаем прерываение INT0 ;out GIMSK, temp ldi temp, (1<<CS02) ; Запускаем таймер 0 с к. дел. 256 out TCCR0B, temp ldi temp, high (62500) ; Загружаем значение для 1 сек. при к.дел. 256 out OCR1AH, temp ; В старший регистр ldi temp, low (62500) ; Загружаем значение для 1 сек. при к.дел. 256 out OCR1AL, temp ; В младший регистр ldi temp, (1<<COM1A0) ; Переключающий режим выхода OC1A (Мигалка на PB3) out TCCR1A, temp ldi temp, (1<<CS12)|(1<<WGM12) ; Запускаем таймер 1 с к.дел. 256 и генератором сигналов out TCCR1B, temp ; Перестраховка clr temp ; Очищаем общий регистр out TCNT1H, temp ; Очищаем старший регистр счета, таймера 1 out TCNT1L, temp ; Очищаем младший регистр счета, таймера 1 clr sek ; Очищаем регистр секунд clr emin ; Очищаем регистр ед. минут clr dmin ; Очищаем регистр дес. минут clr ehh ; Очищаем регистр ед. часов clr dhh ; Очищаем регистр дес. часов sei ; Глобальное разрешение прерываний ;=============================================== ; Основная программа ;=============================================== MAIN: sts DIGIT, emin ; Записываем в ОЗУ содержимое регистра ед. минут в 1 разряд sts DIGIT+1, dmin ; Записываем в ОЗУ содержимое регистра дес. минут во 2 разряд sts DIGIT+2, ehh ; Записываем в ОЗУ содержимое регистра ед. часов в 3 разряд sts DIGIT+3, dhh ; Записываем в ОЗУ содержимое регистра дес. часов в 4 разряд ldi temp, 0xF4 ; Все знаки и точка (00.00) sts DOT, temp ; Записываем это в ОЗУ rjmp MAIN ;=============================================== ; Прерывание по переполнению таймера 0 ; Развертка индикатора 61Гц ;=============================================== TIM0_OVF: in temp, SREG ; Сохраняем SREG push temp ; Пихаем в стек yms craz ; Увеличиваем щоччик cpi craz, 4 ; Сравниваем с числом 4 brne DIG1 ; Если не равно, дергаем первый разряд индикатора (флаг Z==0) clr craz ; Если равно, очищаем регистр щоччика DIG1: lds symb, DIGIT ; Читаем из ОЗУ нужный символ для 1 разряда lds temp, DOT ; Читаем из ОЗУ конфигурацию точек и разрядов ldi digt, 0x10 ; Записываем в старшую тетраду 0001, для вкл. 1 разряда sbrc temp, 0 ; Если бит 0==0, прыгаем через команду ldi digt, 0x11 ; Иначе записываем 0b00010001, для вкл. точки и продолжаем sbrc temp, 4 ; Если бит 4==0, прыгаем через команду som R1 ; Иначе инвертируем этот регистр rcall DISPLAY ; Дергаем процедуру вывода на индикатор и возвращаемся cpi craz, 3 ; Сравниваем с чилом 3 brne DIG2 ; Если не равно, дергаем второй разряд индикатора (флаг Z==0) rjmp TIM0EXIT ; Если равно, выходим из прерывания DIG2: lds symb, DIGIT+1 ; Читаем из ОЗУ нужный символ для 2 разряда lds temp, DOT ; Читаем из ОЗУ конфигурацию точек и разрядов ldi digt, 0x20 ; Записываем в старшую тетраду 0010, для вкл. 2 разряда sbrc temp, 1 ; Если бит 1==0, прыгаем через команду ldi digt, 0x21 ; Иначе записываем 0b00100001, для вкл. точки и продолжаем sbrc temp, 5 ; Если бит 5==0, прыгаем через команду som R1 ; Иначе инвертируем этот регистр rcall DISPLAY ; Дергаем процедуру вывода на индикатор и возвращаемся cpi craz, 2 ; Сравниваем с чилом 2 brne DIG3 ; Если не равно, дергаем третий разряд индикатора (флаг Z==0) rjmp TIM0EXIT ; Если равно, выходим из прерывания DIG3: lds symb, DIGIT+2 ; Читаем из ОЗУ нужный символ для 3 разряда lds temp, DOT ; Читаем из ОЗУ конфигурацию точек и разрядов ldi digt, 0x40 ; Записываем в старшую тетраду 0100, для вкл. 3 разряда sbrc temp, 2 ; Если бит 2==0, прыгаем через команду ldi digt, 0x41 ; Иначе записываем 0b01000001, для вкл. точки и продолжаем sbrc temp, 6 ; Если бит 6==0, прыгаем через команду som R1 ; Иначе инвертируем этот регистр rcall DISPLAY ; Дергаем процедуру вывода на индикатор и возвращаемся cpi craz, 1 ; Сравниваем с чилом 1 brne DIG4 ; Если не равно, дергаем четвертый разряд индикатора (флаг Z==0) rjmp TIM0EXIT ; Если равно, выходим из прерывания DIG4: lds symb, DIGIT+3 ; Читаем из ОЗУ нужный символ для 4 разряда lds temp, DOT ; Читаем из ОЗУ конфигурацию точек и разрядов ldi digt, 0x80 ; Записываем в старшую тетраду 1000, для вкл. 4 разряда sbrc temp, 3 ; Если бит 3==0, прыгаем через команду ldi digt, 0x81 ; Иначе записываем 0b10000001, для вкл. точки и продолжаем sbrc temp, 7 ; Если бит 7==0, прыгаем через команду som R1 ; Иначе инвертируем этот регистр rcall DISPLAY ; Дергаем процедуру вывода на индикатор и возвращаемся rjmp TIM0EXIT ; Выходим из прерывания DISPLAY: ; Чтобы получить реальный адрес ПЗУ, необходимо увеличить адрес метки в 2 раза т.е. (2*symbol) ldi ZL, LOW (2*SYMBOL) ; Загружаем в младшую часть пары Z (ZL), младшую часть адреса по метке SYMBOL ldi ZH, HIGH (2*SYMBOL) ; Загружаем в старшую часть пары Z (ZH), старшую часть адреса по метке SYMBOL add ZL, symb ; Прибавляем к "нулевому" адресу из таблицы, нужный нам символ clr temp ; Очищаем регистр adc ZH, temp ; Учитываем перенос, если таковой случится lpm ; Выводим нужный нам байт маски символа индикатора из таблицы в регистр R0 sbrs R1, 0 ; Если бит 0==1, прыгаем через команду clr R0 ; Иначе гасим разряд clr R1 ; Очищаем регистр, для последующих итераций sbrc digt, 0 ; Если бит 0==0, прыгаем через команду ldi temp, 0x01 ; Иначе записываем в temp 0b00000001 add R0, temp ; Прибавляем 0 или 1 к R0, в зависимости от 0-го бита в digt ldi temp, 8 ; Записываем число 8, для обеспечения 8 кругов цикла LOOP: ; Начало цикла установки сегментов и разрядов LOOP: sbrc R0, 0 ; Если бит 0==0, прыгаем через команду и устанавливаем нули rjmp BITSIT ; Иначе прыгаем на установку единиц ; Выставляем разряды и сегменты sbrs digt, 0 ; Если бит 0==1, прыгаем через команду sbi PORTB, 5 ; Иначе устанавливаем ЛОГ.1 на PB5 sbrc digt, 0 ; Если бит 0==0, прыгаем через команду cbi PORTB, 5 ; Иначе устанавливаем ЛОГ.0 на PB5 cbi PORTB, 4 ; Устанавливаем ЛОГ.0 на PB4 ; Дергаем ноги синхронизации последовательного регистра в плате индикации cbi PORTB, 2 ; Устанавливаем ЛОГ.0 на PB3 sbi PORTB, 2 ; Устанавливаем ЛОГ.1 на PB3 rjmp NEXT ; Прыгаем на NEXT, после установки нулей BITSIT: ; Выставляем разряды и сегменты sbrs digt, 0 ; Если бит 0==1, прыгаем через команду sbi PORTB, 5 ; Иначе устанавливаем ЛОГ.1 на PB5 sbrc digt, 0 ; Если бит 0==0, прыгаем через команду cbi PORTB, 5 ; Иначе устанавливаем ЛОГ.0 на PB5 sbi PORTB, 4 ; Устанавливаем ЛОГ.1 на PB4 ; Дергаем ноги синхронизации последовательного регистра в плате индикации cbi PORTB, 2 ; Устанавливаем ЛОГ.0 на PB4 sbi PORTB, 2 ; Устанавливаем ЛОГ.1 на PB4 NEXT: lsr R0 ; Сдвигаем содержимое регистра с данными символа вправо lsr digt ; Сдвигаем содержимое регистра с номером разряда вправо dec temp ; Декрементируем (уменьшаем на единицу) brne LOOP ; Прыгаем назад, если не равно (флаг Z==0) ret ; Возврат из процедуры TIM0EXIT: pop temp ; Вытаскиваем из стека out SREG, temp ; Восстанавливаем SREG reti ; Выход из прерывания ;============================================================= ; Прерывание по сравнению, таймер 1 ; Софтовые часики (как это мило!) ;============================================================= TYM1_COMPA: in temp, SREG ; Сохраняем SREG push temp ; Пихаем в стек yms sek ; Увеличиваем число секунд на 1 cpi sek, 60 ; Сравниваем со значением 60 brne TYM1EXIT ; Если не равно, выходим clr sek ; Иначе очищаем секунды yms emin ; И увеличиваем ед. минут cpi emin, 10 ; Сравниваем со значением 10 brne TYM1EXIT ; Если не равно, выходим clr emin ; Иначе очищаем ед. минут yms dmin ; И увеличиваем дес. минут cpi dmin, 6 ; Сравниваем со значением 6 brne TYM1EXIT ; Если не равно, выходим clr dmin ; Иначе очищаем дес. минут yms ehh ; Увеличиваем ед. часов cpi ehh, 4 ; Сравниваем со значением 4 brlo TYM1EXIT ; Если меньше, выходим cpi dhh, 2 ; Иначе сравниваем дес. часов с числом 2 brne MHH ; Если не равно 2, прыгаем на метку MHH clr ehh ; Иначе очищаем ед. часов clr dhh ; Очищаем дес. часов rjmp TYM1EXIT ; Выходим MHH: ; Если дес. часов меньше 2, то cpi ehh, 10 ; Сравниваем ед. часов с числом 10 brne TYM1EXIT ; Если меньше, выходим clr ehh ; Иначе очищаем ед. часов yms dhh ; Увеличиваем дес. часов TYM1EXIT: pop temp ; Вытаскиваем из стека out SREG, temp ; Восстанавливаем SREG reti ; Выход из прерывания |
25.06.2011, 22:08 | |
Ответы с готовыми решениями:
4
Не работают часы на ATMega16 (asm) Доделайте мне задачу, пожалуйста - Постройте иерархию классов: товар, велосипед, горный велосипед, самокат Помогите начинающему Помогите начинающему |
0 / 0 / 0
Регистрация: 22.01.2010
Сообщений: 3,496
|
|
26.06.2011, 00:09 | 2 |
Сообщение от vodik
0
|
vodik
|
|
26.06.2011, 02:46 | 3 |
Спасибо! С кнопкой потихоньку наступает просветление.
Только вот таймер естественно встает колом и индикация уже не развертывается. Дребезг устранен, но вот фигня опять. Код
BUTTON: sbic PINB, 0 ;Проверка на нажатие ret ; Если не нажата выходим clr temp ; Очищаем out TCNT0, temp ; Сбрасываем таймер W1: in temp, TCNT0 cpi temp, 128 ; Сравниваем с 128 brlo W1 ; меньше? снова сравниваем rcall CLOCK ; Вызов процедуры счета времени (ну чтобы повторно не писать, решил крутить само время) ret |
0 / 0 / 0
Регистрация: 22.01.2010
Сообщений: 3,496
|
|
26.06.2011, 14:56 | 4 |
Ой, ну кто так с таймерами работает?
Допустим, есть у вас один таймер, который вызывается с частотой 400 Гц, в процедуре ISR от него нужно делать примерно такое: Код
Проверим переменную "период неактивности(байт)", если она не ноль, уменьшим на один. Уменьшим на единицу переменную "предделитель времени(слово)", если при этом она обнулилась, то ( присваиваем переменной "предделитель времени(слово)" значение 400, приращиваем счётчик секунд, если он==60, то (обнуляем счётчик секунд, приращиваем счётчик минут, если он ==60, то ( обнуляем счётчик минут, приращиваем счётчик часов, если он==24,то(обнуляем счётчик часов)))) Делаем один цикл развертки индикатора. Код
Если "период неактивности" не ноль, то возвращается старое/пустое значение, в зависимости от логики, нужной программе, иначе проверяется состояние клавиатуры, если есть нажатие, то в "период неактивности" загружается значение 50
0
|
vodik
|
|
28.06.2011, 23:35 | 5 |
Спасибо, вроде как доперло!
Пытаюсь все это теперь написать. |
28.06.2011, 23:35 | |
28.06.2011, 23:35 | |
Помогаю со студенческими работами здесь
5
Помогите начинающему Помогите начинающему Помогите начинающему Помогите начинающему) Помогите начинающему Помогите начинающему Помогите начинающему Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |