Wladimir_TS писал(а):Неоднократно замечал что при старте проекта в студии имеет место быть 1 холостое прерывание от внешних источников если они разрешены и если настроено на фронт или спад сразу после SEI. В железе его нет
когда ты в студии запустил симуляцию и курсор встал на нулевой адрес, сначала пины соответствующих входов нужно поставить в нужное состояние. тогда не будет возникать ложного запуска прерывания.
изначально студия на всех пинах всех портов ставит нули.
а в железе пины при запуске стоят в нужном состоянии, потому в железе ложного запуска прерывания при старте не происходит.
Мудрость приходит вместе с импотенцией...
Когда на русском форуме переходят на Вы, в реальной жизни начинают бить морду.
Всем привет!
В ходе изучения ASM в учебных целях решил сделать частотомер на 50 Гц. Принцип работы - измерение периода. Камень - atmega8, частота 8 МГц. Напяжение через диод и резисторный делитель подаю на ICP1. Для начала был написан такой код.
Спойлер
;=============
; Сегмент FLASH памяти
.CSEG
;=============
; Таблица прерываний
.ORG 0x00 RJMP RESET
.ORG 0x05 RJMP TIM1_CAPT
;=============
; Прерывание по сбросу, стартовая инициализация
RESET:
; Инициализация стека
LDI Temp1, LOW(RAMEND)
OUT SPL, Temp1
LDI Temp1, HIGH(RAMEND)
OUT SPH, Temp1
; Очистка ОЗУ и регистров R0-R31
LDI ZL, LOW(SRAM_START) ; Адрес начала ОЗУ в индекс
LDI ZH, HIGH(SRAM_START)
CLR Temp1 ; Очищаем R16
RAM_Flush:
ST Z+, Temp1
CPI ZH, HIGH(RAMEND + 1)
BRNE RAM_Flush
CPI ZL, LOW(RAMEND + 1)
BRNE RAM_Flush
LDI ZL, (0x1F-2) ; Адрес регистра R29
CLR ZH
Reg_Flush:
ST Z, ZH
DEC ZL
BRNE Reg_Flush
CLR ZL
CLR ZH
; Регистры и SRAM полностью очищены (обнулены)
; Но регистры ввода-вывода (IO) НЕОБХОДИМО очищать
; Глобальный запрет прерываний
CLI
;настройка порта D
ldi Temp1, 0xFF // порт D на выход
out DDRD, Temp1
;настройка порта B
cbi DDRB,0 ; ICP1 на вход
;настройка таймера1
ldi Temp1, 0b00000000
out TCCR1A, Temp1
ldi Temp1, 0b01000011
out TCCR1B, Temp1
ldi Temp1, 0b00100000
out TIMSK, Temp1
out TIFR, Temp1
SEI ;разрешаем прерывания
;***************************************************
LOOP:
rjmp LOOP
;==============
TIM1_CAPT: ;обработчик прерывания по захвату таймера 1
SBIC PORTD,EXT
rjmp metka
SBI PORTD,EXT ;установить бит
rjmp EXIT
metka:
CBI PORTD,EXT ;установить бит
EXIT:
RETI
Подключенный к PD0 логический анализатор выдал красивую картинку
Следующий этап - получение данных с регистра захвата. В качестве индикатора применил модуль из 4 семисегментных индикаторов на TM1637. Что-бы число импульсов влазило в индикатор, решил ограничится 1 байтом. По сему коэфициент предделителя выбрал 1024, должно получаться что-то около 156. Вывод на TM1637 уже давно отработан и в коде не приводится.
Спойлер
;=============
; Сегмент FLASH памяти
.CSEG
;=============
; Таблица прерываний
.ORG 0x00 RJMP RESET
.ORG 0x05 RJMP TIM1_CAPT
;=============
; Прерывание по сбросу, стартовая инициализация
RESET:
; Инициализация стека
LDI Temp1, LOW(RAMEND)
OUT SPL, Temp1
LDI Temp1, HIGH(RAMEND)
OUT SPH, Temp1
; Очистка ОЗУ и регистров R0-R31
LDI ZL, LOW(SRAM_START) ; Адрес начала ОЗУ в индекс
LDI ZH, HIGH(SRAM_START)
CLR Temp1 ; Очищаем R16
RAM_Flush:
ST Z+, Temp1
CPI ZH, HIGH(RAMEND + 1)
BRNE RAM_Flush
CPI ZL, LOW(RAMEND + 1)
BRNE RAM_Flush
LDI ZL, (0x1F-2) ; Адрес регистра R29
CLR ZH
Reg_Flush:
ST Z, ZH
DEC ZL
BRNE Reg_Flush
CLR ZL
CLR ZH
; Регистры и SRAM полностью очищены (обнулены)
; Но регистры ввода-вывода (IO) НЕОБХОДИМО очищать
; Глобальный запрет прерываний
CLI
;настройка порта D
ldi Temp1, 0xFF // Порт D на выход
out DDRD, Temp1
;настройка порта B
cbi DDRB,0 ; ICP1 на вход
;настройка таймера1
ldi Temp1, 0b00000000
out TCCR1A, Temp1
ldi Temp1, 0b01000101
out TCCR1B, Temp1
ldi Temp1, 0b00100000
out TIMSK, Temp1
out TIFR, Temp1
; ===========
CLT ;сбрасываем бит Т
ORI R30, 0b00000001 ;устанасливаем бит 0 R30 в 1
SEI ;разрешаем прерывания
;***************************************************
LOOP:
BRTC LOOP ;если Т=0 не делаем ничего
CLI ;запрещаем прерывания
;***************************************************
; вывод значения Temp1 на TM1637
;***************************************************
; ===========
; delay loop generator
; 8000000 cycles:
; -----------------------------
; delaying 7999992 cycles:
ldi R17, $48
WGLOOP00: ldi R18, $BC
WGLOOP1: ldi R19, $C4
WGLOOP2: dec R19
brne WGLOOP2
dec R18
brne WGLOOP1
dec R17
brne WGLOOP00
; -----------------------------
; delaying 6 cycles:
ldi R17, $02
WGLOOP3: dec R17
brne WGLOOP3
; -----------------------------
; delaying 2 cycles:
nop
nop
; ===========
SEI ;разрешаем прерывания
CLT ;сбрасываем бит Т
rjmp LOOP
;==============
TIM1_CAPT: ;обработчик прерывания по захвату таймера 1
SBRS R30,0
rjmp ZAHVAT
CLR Temp1 ;обнуляем счётчмк
out TCNT1H,Temp1
out TCNT1L,Temp1
ANDI R30, 0b11111110 ;устанасливаем бит 0 R30 в 0
rjmp EXT
ZAHVAT:
in Temp1,ICR1L ;копируем значение регистра захвата
in Temp2,ICR1H
ORI R30, 0b00000001 ;устанасливаем бит 0 R30 в 1
SET ;устанавливаем бит Т
EXT:
RETI
В кратце логика такая. До первого прерывания ничего не делаем. По первому прерыванию сбрасываем TCNT1 в ноль, по второму забираем в Temp1 младший байт регистра захвата и разрешаем его вывод на индикатор. Перед выводом запрещаем прерывания, выводим значение Temp1. Задержка 1 сек. Разрешаем прерывания и всё по новой. В теории красиво, а на деле на индикаторе бегают цифры от 3 до 200.
Где косяк?
TIM1_CAPT: ;обработчик прерывания по захвату таймера 1
SBRS R30,0
rjmp ZAHVAT
CLR Temp1 ;обнуляем счётчмк
out TCNT1H,Temp1
out TCNT1L,Temp1
;================
LDI R16,1<<PSR10
OUT SFIOR,R16 ; обнулить предделитель таймера
;================
ANDI R30, 0b11111110 ;устанасливаем бит 0 R30 в 0
rjmp EXT
ZAHVAT:
in Temp1,ICR1L ;копируем значение регистра захвата
in Temp2,ICR1H
ORI R30, 0b00000001 ;устанасливаем бит 0 R30 в 1
SET ;устанавливаем бит Т
EXT:
RETI
Таймер при выходе из прерывания по окончании измерения не останавливается. За время вывода на индикацию и задержки входной сигнал на PB0/ICP1 установит флаг ICF1, т.е. при переходе на LOOP прерывание вызовется сразу, а не по фронту входного сигнала.
Спойлер
Это очевидно. Масло масляное. Указывайте в комментариях непростые, неочевидные вещи. Алгоритм, пояснения. То, о чем другим будет непонятно, и что вы забудете через месяц.
[uquote="akl",url="/forum/viewtopic.php?p=3909119#p3909119"]Неполная очистка таймера, т.к. задействован предделитель
...............
Таймер при выходе из прерывания по окончании измерения не останавливается. За время вывода на индикацию и задержки входной сигнал на PB0/ICP1 установит флаг ICF1, т.е. при переходе на LOOP прерывание вызовется сразу, а не по фронту входного сигнала.[/uquote]
Предделитель сильно не повлияет. Плюс-минус один такт. А по второму согласен на все сто. Пока будем выводить значения и висеть в задержке флаг ICF1 будет установлен и первое прерывание произойдёт не по фронту а в момент разрешения прерывания. Попробовал решить вопрос в лоб, сбрасывать ICF1 в конце LOOP перед установкой бита I. Показания немного стабилизировались. Буду разбираться с вашим кодом. Спасибо за совет.
Добавлено after 8 minutes 46 seconds:
Ещё вопрос в догонку. Установив биты предделителя CS12 – CS10 в "000" мы останавливаем таймер. При этом TCNT1 обнуляется? Если опять запустить счёт он продолжится со значения в момент остановки или начнётся с нуля?
Зависит от режима работы.
При "ручном" останове текущее содержимое таймера не изменится (если момент останова не совпадет с соответствующей точкой работы таймера - например сброс по совпадению).
Насчет обнуления - это только или по сбросу или по выходу из сна оговорено - смотрим соответствующий раздел даташита.
[uquote="a1000",url="/forum/viewtopic.php?p=3909218#p3909218"]... Установив биты предделителя CS12 – CS10 в "000" мы останавливаем таймер. При этом TCNT1 обнуляется? Если опять запустить счёт он продолжится со значения в момент остановки или начнётся с нуля?[/uquote]Да, таймер останавливает счёт. Предделитель нет! При последующем запуске продолжение счёта в моменты переполнения предделителя.
Сидел разбирался с кодом akl. Напишу, что я понял и свои вопросы. Стартовая инициализация. Вы убрали очистку ОЗУ. Вы считаете это лишним, или спрятали что-бы не загромождать код? С настройками таймера понятно - счётчик стоит, прерывания по захвату разрешены, предделитель сброшен. В R22 заготовка для TCCR1B. Основной цикл. Пока Т=0 - крутимся вначале. Прерывание. По первому заходу всё ясно - запускаем счётчик, сбрасываем флаг из R30 и в основной цикл. А вот второй заход (с переходом на метку ZAHVAT) мне не совсем понятен. Я бы сократил его до
А там все необходимые настройки таймера и флагов уже прописаны. Так-что все манипуляции с регистрами в прерываниями я считаю излишними.
И последний вопрос. Если вы убрали очистку ОЗУ из блока стартовой инициализации, что очистит TCNT1?
[uquote="a1000",url="/forum/viewtopic.php?p=3909483#p3909483"]...убрали очистку ОЗУ из блока стартовой инициализации, что очистит TCNT1?[/uquote]Область адресов RAM и область регистров I/O разные. А так, да просто не включил при инициализации
Про внешние прерывания..
Там написано что прерывания осуществляются от пинов: INT0 и PCINT0....PCINT5. с последними все понятно... но вот INT0 на каком выводе ???
Или я чтото не понял...?
Всем доброго дня. Завалялось у меня несколько штук attiny261, ради интереса решил попробовать запустить PWM6 Mode. Даташит изучал как мог, с помощью гуглопереводчика. Попробовал в протеусе - на выходах сигнала нет. В железе еще не собирал.. Подскажите, где ошибка? CKSEL fuses 0001, SUT1..0 = 10.
Код инициализации таймера1 под спойлером.
Всем счастливого нового года!
Прикупил на али такую приблуду
Называется LED-матрица 8x8 с драйвером max7219. Поигрался с ней в среде Arduino и решил запустить её на ASM. МК atmega8. Включение отдельных пикселей реализовал без проблем. Решил сделать бегущий огонёк в первой строке. Вот код
Спойлер
Попробовал поменять РВ2 на РВ1 и РВ0 - вообще не запустилось. Включает тестовый режим и всё. Это когда светят все пиксели. Убрал дёрганье ножкой между посылкой первого и второго байта - стало работать как надо на РВ2.