Не буду плодить новых тем, спрошу тут. Разбираясь с ассемблером, чисто в учебных целях решил объединить примеры работы с ADC и USART. Примеры были для ATmega16, переделывал на ATmega8. Заменил заголовочный файл и адреса прерываний. При компилировании результатов Atmel Studio упорно ругалась на строку с указателем вектора ADC Conversion Complete Код (C++): .org $00e // Указатель вектора ADC_Conv(Прерывание по выполнению конвертации АЦП) Долго не мог понять в чём причина, пока не обратил внимание на очерёдность записи векторов Код (C++): .org $00e // Указатель вектора ADC_Conv(Прерывание по выполнению конвертации АЦП) rjmp ADC_Conv // Переходим на ADC_Conv .org $00d // Адрес вектора прерывания по посылке байта rjmp USART_TXC ; // Переходим на USART_TXC Поменял местами Код (C++): .org $00d // Адрес вектора прерывания по посылке байта rjmp USART_TXC ; // Переходим на USART_TXC .org $00e // Указатель вектора ADC_Conv(Прерывание по выполнению конвертации АЦП) rjmp ADC_Conv // Переходим на ADC_Conv всё стало работать. Собственно вопрос - таблица векторов прерываний обязательно должна заполняться в порядке приоритета этих прерываний?
В приведённых Вами исходниках ни чего крамольного нет -- всё должно работать. Надо чуть больше информации (касается примера, где прерывания декларируются в "неправильном" порядке).
Вот весь код Код (C++): .include "m8def.inc" // Подключаем заголовочный файл ATMega16 .def temp = r16 // Присваиваем символические имена регистрам .def razr1 = r17 .def razr2 = r18 .equ BAUD = 51 //задаём битрейт USART .dseg // Сегмент ОЗУ .cseg // Программный сегмент .org 0 // Указатель вектора Reset rjmp Reset // Переходим на Reset .org $00e // Указатель вектора ADC_Conv(Прерывание по выполнению конвертации АЦП) rjmp ADC_Conv // Переходим на ADC_Conv .org $00d // Адрес вектора прерывания по посылке байта rjmp USART_TXC ; // Переходим на USART_TXC //.org $00e // Указатель вектора ADC_Conv(Прерывание по выполнению конвертации АЦП) //rjmp ADC_Conv // Переходим на ADC_Conv Reset: // Метка Reset ldi temp, high(RAMEND) // Инициализируем стек out sph, temp ldi temp, low(RAMEND) out spl, temp //////////////////////////////// настройка АЦП /////////////////////////////// ldi temp, 0xff // Константа в temp out DDRD, temp // Порт D на выход, сюда будем выводить данныые с АЦП ldi temp, 0b01100000 // REFS = 01 контролирует откуда берется опорное напряжение; ADLAR = 1 out ADMUX, temp // смещение битов(право или лево); мультиплексор на ножку ADC0, биты MUX = 00000 ldi temp, 0b11011111 // ADEN = 1 разрешаем АЦП; ADSC = 1 старт конвертации; ADIF, ADIE = 11 out ADCSRA, temp // разрешают прерывание, ADPS = 111 предделитель АЦП(смотри тех.док.) ///////////////////////////////////// настройка USART //////////////////////////////// ldi temp, high(BAUD) // Записываем вычисленное число для битрейта out UBRRH, temp // Сперва в старший регистр UBRR ldi temp, low(BAUD) // Затем в младший регистр out UBRRL, temp ldi temp, 0b11011000 // Устанавливаем биты RXCIE, TXCIE, RXEN, TXEN, биты разрешают прерывания out UCSRB, temp // И работу с USART ldi temp, 0b10000110 // Устанавливаем бит URSEL = 1, это бит селектора(х.з. зачем он, но надо) out UCSRC, temp // биты UCSZ говорят о том, что прием/передача будет по 8 бит sei // Разрешаем глобально прерывания Proga: // Основная программа, тут ничего не делаем rjmp Proga // переход на метку Proga ADC_Conv: // Прерывание АЦП, сюда влетим как конвертация сделалась cli // запрещаем прерывания in razr1, ADCL // Копируем в razr1 значение младшего регистра in razr2, ADCH // в razr2 значение старшего, читать всегда сперва младший! out UDR, razr2 // Отправляем полученное значение по USART sei // Разрешить прерывания reti // Выйти из прерывания USART_TXC: // Прерывание по отсылке байта cli // Запрещаем прерывания sbis UCSRA,UDRE // Стандартная проверка из тех.дока, если UDRE = 1 пропускаем следующую строку rjmp USART_TXC // Либо вращаемся в цикле ldi temp, 0b01100000 // Снова заряжаем АЦП, по аналогии как в предварительных настройках out ADMUX, temp ldi temp, 0b11011111 out ADCSRA, temp sei // Разрешить прерывания reti // Выйти из прерывания В таком виде выдаёт ошибку.
А так нет Код (C++): .include "m8def.inc" // Подключаем заголовочный файл ATMega16 .def temp = r16 // Присваиваем символические имена регистрам .def razr1 = r17 .def razr2 = r18 .equ BAUD = 51 //задаём битрейт USART .dseg // Сегмент ОЗУ .cseg // Программный сегмент .org 0 // Указатель вектора Reset rjmp Reset // Переходим на Reset //.org $00e // Указатель вектора ADC_Conv(Прерывание по выполнению конвертации АЦП) //rjmp ADC_Conv // Переходим на ADC_Conv .org $00d // Адрес вектора прерывания по посылке байта rjmp USART_TXC ; // Переходим на USART_TXC .org $00e // Указатель вектора ADC_Conv(Прерывание по выполнению конвертации АЦП) rjmp ADC_Conv // Переходим на ADC_Conv Reset: // Метка Reset ldi temp, high(RAMEND) // Инициализируем стек out sph, temp ldi temp, low(RAMEND) out spl, temp //////////////////////////////// настройка АЦП /////////////////////////////// ldi temp, 0xff // Константа в temp out DDRD, temp // Порт D на выход, сюда будем выводить данныые с АЦП ldi temp, 0b01100000 // REFS = 01 контролирует откуда берется опорное напряжение; ADLAR = 1 out ADMUX, temp // смещение битов(право или лево); мультиплексор на ножку ADC0, биты MUX = 00000 ldi temp, 0b11011111 // ADEN = 1 разрешаем АЦП; ADSC = 1 старт конвертации; ADIF, ADIE = 11 out ADCSRA, temp // разрешают прерывание, ADPS = 111 предделитель АЦП(смотри тех.док.) ///////////////////////////////////// настройка USART //////////////////////////////// ldi temp, high(BAUD) // Записываем вычисленное число для битрейта out UBRRH, temp // Сперва в старший регистр UBRR ldi temp, low(BAUD) // Затем в младший регистр out UBRRL, temp ldi temp, 0b11011000 // Устанавливаем биты RXCIE, TXCIE, RXEN, TXEN, биты разрешают прерывания out UCSRB, temp // И работу с USART ldi temp, 0b10000110 // Устанавливаем бит URSEL = 1, это бит селектора(х.з. зачем он, но надо) out UCSRC, temp // биты UCSZ говорят о том, что прием/передача будет по 8 бит sei // Разрешаем глобально прерывания Proga: // Основная программа, тут ничего не делаем rjmp Proga // переход на метку Proga ADC_Conv: // Прерывание АЦП, сюда влетим как конвертация сделалась cli // запрещаем прерывания in razr1, ADCL // Копируем в razr1 значение младшего регистра in razr2, ADCH // в razr2 значение старшего, читать всегда сперва младший! out UDR, razr2 // Отправляем полученное значение по USART sei // Разрешить прерывания reti // Выйти из прерывания USART_TXC: // Прерывание по отсылке байта cli // Запрещаем прерывания sbis UCSRA,UDRE // Стандартная проверка из тех.дока, если UDRE = 1 пропускаем следующую строку rjmp USART_TXC // Либо вращаемся в цикле ldi temp, 0b01100000 // Снова заряжаем АЦП, по аналогии как в предварительных настройках out ADMUX, temp ldi temp, 0b11011111 out ADCSRA, temp sei // Разрешить прерывания reti // Выйти из прерывания
Директива .org задаёт начальный адрес памяти, в которую будет "укладываться" всё, что после этой директивы -- всё до конца исходника или до следующей директивы .org. В том коде, где выдаёт ошибку, Вы устанавливаете: Код (C++): .org $00e rjmp ADC_Conv соответственно по адресу 0x00E будет записана команда перехода на обработчик ADC_Conv. Потом: Код (C++): .org $00d rjmp USART_TXC по адресу 0x00D будет команда перехода на обработчик USART_TXC. А далее: Код (C++): Reset: ldi temp, high(RAMEND) по адресу 0x00E (вместо перехода на обработчик ADC_Conv) будет записана команда загрузки значения. И так далее... Нужна ещё одна директива .org.
А вообще с этими директивами лучше не мудрить, а прописывать всю таблицу векторов полностью. Для неиспользуемых векторов обычно ставят заглушки, которые перенаправляют на вектор RESET.
Как-то думалось, что память отведённая под таблицу векторов по другому не используется. Видимо память экономят. Видел тут другой вариант заглушки для неиспользуемых векторов Код (C++): .ORG $000 ; (RESET) RJMP Reset .ORG $002 RETI ORG $004 RETI ................................................... тогда не понял зачем это нужно, но теперь думаю, что этот вариант имение травматичен чем RESET. Большое спасибо за быструю и квалифицированную помощь.
или на iret/reti (не помню), если заморачиваца не хочеца. Но вызов неиспользуемого/неописанного прерывания - критическая ошибка, я бы сётаки на RESET переназначил. iret/reti отличается от ret тем, что сначала восстанавливается SREG а потом делается возврат, нельзя сделать просто ret из прерывания, во-первых, стек будет несбалансирован, во-вторых хрен знает, куда вернешьса.
Си-шный компилятор различает участки памяти по типам (точнее это делает линкер). А ассемблер этого не делает и вся память для него одинаковая (имеется в виду flash) -- всё команды для исполнения. И исполнение начинается с адреса прерывания RESET. Если посмотреть на Ваш "работающий" код, так там часть таблицы прерываний используется для хранения кода программы. После метки 'Reset' следующие четыре команды занимают место четырёх последних векторов прерываний. И ничего -- всё работает.
Может не правильно выразился, но ни какого осуждения в сообщении выше не было. Была попытка объяснить суть кода, т.к. у Вас нет чёткого понимания, что получается на выходе.