Да и в Си для AVR, после ассемблера буду чувствовать себя слепым котёнком, не видя,
что там происходит на аппаратном уровне, хоть Си и облегчает организацию
сложных и не очень, алгоритмов.
Ну, в большинстве мест результирующий ассемблерный код либо очевиден, либо нет необходимости в полном контроле. А там, где она есть, очень просто написать функцию целиком на ассемблере.
C действительно облегчает процесс создания алгоритма. Поэтому я в последнее время чаще всего поведение программы реализю на С, а критичные функции пишу на ассемблере и вызываю из С. Получается быстро и эффективно.
Разница между теорией и практикой на практике гораздо больше, чем в теории.
Это наверное потому, что я к АСМу уже привык.
В Сишных исходниках полностью разберусь, так как знаю его немного.
Где-то есть у меня парочка проектов, которые я писал на Си. Просто надо было быстро сфарганить прошивку для МК, ну и написал на Си.
У меня так: Если есть время, пишу на АСМе, если нет времени, то на Си.
Жаль, что ассемблерные вставки в Си получаются такого рода:
Жаль, что ассемблерные вставки в Си получаются такого рода:
Ненене. Смотрите пример ниже.
Как я уже говорил, инлайновый ассемблерный синтаксис AVR-GCC больно заморочен, как по мне. Потому я пишу в отдельном файле. Например, вывод в HC595 (писал на асме, чтобы оптимизировать по скорости):
#include <avr/io.h>
#include "HC595drv.h"
.global HC595_ShiftByte, HC595_InitLines
HC595_InitLines:
;Configuring HC595 control lines as outputs
lds R25,HC595_DDR
ori R25,((1<<HC595_DS) | (1<<HC595_SH_CP) | (1<<HC595_ST_CP))
sts HC595_DDR,R25
;Setting lines to initial state
lds R25,HC595_PORT
andi R25,~((1<<HC595_DS) | (1<<HC595_SH_CP) | (1<<HC595_ST_CP))
sts HC595_PORT,R25
ret
HC595_ShiftByte:
;Storing interrupt flag state
lds R26,SREG
;Disabling interrupts to prevent process of being interrupted
cli
;Storing port value to leave its other bits unchanged
lds R25,HC595_PORT
;If bit 0 in byte is set...
sbrc R24,0
;setting data bit to "1" (otherwise leave it unchanged - i.e. 0)
ori R25,(1<<HC595_DS)
sts HC595_PORT,R25
;Shifting bit into HC595 register
ori R25,(1<<HC595_SH_CP)
sts HC595_PORT,R25
;Setting lines to initial state
andi R25,~((1<<HC595_DS) | (1<<HC595_SH_CP))
sts HC595_PORT,R25
;Shifting byte right, thus obtaining next bit
;and repeating bit transfer in the same manner
lsr R24
//...
//И тут то же самое еще семь раз (оптимизировал по скорости, потому развернул),
//пропустил для краткости
//...
;Now byte completely resides in an external register
;Setting store clock line to bring it out
ori R25,(1<<HC595_ST_CP)
sts HC595_PORT,R25
;Setting lines to initial state
andi R25,~((1<<HC595_DS) | (1<<HC595_SH_CP) | (1<<HC595_ST_CP))
sts HC595_PORT,R25
;Restoring I flag
sts SREG,R26
ret
Как видно, параметры передаются через регистры (если их немного, дальше задействуется память; в общем, см. доки к AVR-GCC).
Разница между теорией и практикой на практике гораздо больше, чем в теории.
У меня дизассемлированый листинг по другому выводится(так же, как я и указал выше.)
Среда AVR Studio + WinAVR. А Ваш листинг АСМа красив. Я вот извращался тоже с вставками, но у меня шло только на низкоуровневом ассемблере. Ну что-ж почитаю мануалы к AVR GCC
I am DX168B and this is my favourite forum on internet!
У меня дизассемлированый листинг по другому выводится
А-а-а, так вы про дизассемблер... Не, там, к сожалению, ничего не поделать, будут страшные цифири. То, что выше, я писал руками, это не дизассемблер, естесственно.
Разница между теорией и практикой на практике гораздо больше, чем в теории.
Чет, появились новые сообщения, пока я расчехлялся отправить это...
YS писал(а):
Код несколько заморочен оттого, что написан инлайном по правилам GCC. Я ассемблерные функции обычно пишу в отдельном файле, так проще.
А можно каким ни будь похожим методом получать данные из массива в ОЗУ? Чтоб просто добавить подпрограмму в общий файл и при работе с переменным массивом вызывать ее.
Так, все что хотел, ну,кроме адекватного изменения частоты из массива переменных, я сделал. Правда, на эмуляторе работает из-под хвоста, но как только с кем ни будь проконсультируюсь по поводу фьюзов. пересяду на макет.
Возможно, я бы смог ими воспользоваться, если бы хорошо понимал смысл исходной программы. А так, мое понимание очень напоминает объяснение блондинки на кафедре цифровой схемотехники.
void static inline Signal_OUT(const uint8_t *signal, uint8_t ad2, uint8_t ad1, uint8_t ad0)//Функция, отвечающая за вывод массива в порт. Первый параметр - указатель на нужный массив.
{
asm volatile( "eor r18, r18 ;r18<-0" "\n\t"//обнуление
"eor r19, r19 ;r19<-0" "\n\t"//обнуление
"1:" "\n\t"//метка
"add r18, %0 ;1 cycle" "\n\t"//Инкремент ригистра зачем-то. Не помню уже, что за %
"adc r19, %1 ;1 cycle" "\n\t"
"adc %A3, %2 ;1 cycle" "\n\t"//Инкремент чего-то и зачем-то. Не помню уже, что за %
"lpm ;3 cycles" "\n\t" //Загрузка из памяти программ откуда-то куда-то.
"out %4, __tmp_reg__ ;1 cycle" "\n\t" //вывод в порт, (наверное же порт А) чего-то там из __tmp_reg__ (чего это и куда, ХЗ).
"sbis %5, 2 ;1 cycle if no skip" "\n\t" //sbis Где-то слышал, но давно и неправда. Видимо, когда на третьем курсе азы ассемблера в нас вбивали.
"rjmp 1b ;2 cycles. Total 10 cycles" "\n\t" //Переход по метке. Что за b после единицы?
: //Двоеточие, тоже, наверно, что-то значит.
:"r" (ad0),"r" (ad1),"r" (ad2),"e" (signal),"I" (_SFR_IO_ADDR(PORTA)), "I" (_SFR_IO_ADDR(SPCR)) // Непонятная строка, в которой чередуются буковки и параметры функции. Видимо, в этом есть высший смысл.
:"r18", "r19" //И два регистра.
);
ANDI R16,$1C
BREQ _END ;Проверяем на ошибки
CLR R17
_END:
Вот я дурак. ANDI - же устанавливает флаг Z, если в результате ноль получился.
А я млин обрезаю маской, а потом командой cpi проверяю, ноль ли обрабатываемом регистре?
На АСМе я смогу написать, но я пока не умею миксовать ассемблерный код с Сишным.
MrHowl22 писал(а):Ладно, вопрос такой. В Uniprof как выставить фьюзы в Atmega 16 под разные внутренние тактовые и под внешний кварц 16 Мгц.
А то, когда последний раз через нее прошивал, вроде по даташиту смотрел, а все равно забачил контроллер. Считывает вот так
Ищите калькулятор фьюзов в гугле.
Только будьте внимательны, в калькуляторах установленная галочка означает запрограммированный фьюз(в бит записан ноль) а в UniProf, запрограммированный фьюз - это снятая галочка. (галка - лог.1 , нет галки - лог. 0) В UniProf надо сначала считать фьюзы (кнопкой READ) изменить только нужные фьюзы и за тем только записать (кнопкой WRITE)
В МК AVR фьюзы включаются записью в них лог. 0. То есть, если в UniProf нет галки напротив фьюза SPIEN, значит туда записан ноль, а по правилу AVR, ноль - это запрограммированный фьюз. SPIEN - Enable SPI programming. То есть Включить программирование по SPI. Так как там ноль записан, значит программирование по SPI разрешено.
Например рассмотрим RSTDISBL. Переводится, как RESET DISABLE или Отключить вывод RESET. Если запрограммировать (записать ноль) то нога RESET станет обычной ногой порта.
Если записать 1, то вывод RESET будет выводом RESET. В UniProf на этом фьюзе стоит галочка. Значит там единица, а если там единица, значит фьюз выключен, что означает, что нога RESET является выводом RESET, а не обычным портом ввода\вывода.
Оба фьюза (RSTDISBL и SPIEN) учавствуют в возможности прошивки через SPI.
Если SPIEN отключить или RSTDISBL включить, то МК залочится. А значит, UniProf этот МК не увидит.
I am DX168B and this is my favourite forum on internet!
DX168B писал(а):
Ищите калькулятор фьюзов в гугле.
Уже нашел, уже прошил. Теперь проблема с ЖК. Вот уж не думал, с чем будет. Но, чет не инициализируется. По нажатию кнопок пробует обновляться, но толку, если изначально только рад прямоугольников...
Шо ж за бред.
Раз ряд квадратов, то скорее всего кодировка символов неверная. Как я понял, ЖК тут знакосинтезирующий, а не графический. Наверное компилятор не в ту кодировку переводит надписи.
I am DX168B and this is my favourite forum on internet!
Ряд квадратов - значит ЖК не инициализирован. Он такой, если просто подключить питание. Были уже такие траблы когда-то. тогда я их решил. но, тогда я писал под другим компилятором.
Так, ЖК запустил. Правда, глючит. Но, надеюсь, отлажу.
Ну, что7 Кто ни будь поможет дружескими советами и консультациями переработать вышеприведенный ассемблерный кусочек? А то не хотелось бы тратить кучу времени на изучение языка программирования, ради одной подпрограммы? Тем более, что мне вскоре понадобиться рабочий генератор, а не знания языков низкого уровня.
Стоит в программе в блоке после else
Далее за ней сразу идет отключение портов. Значит, генерация сигнала происходит внутри программы, согласно параметрам.
void static inline Signal_OUT(const uint8_t *signal, uint8_t ad2, uint8_t ad1, uint8_t ad0)
{
asm volatile( "eor r18, r18 ;r18<-0" "\n\t" //обнуление
"eor r19, r19 ;r19<-0" "\n\t"//обнуление
"1:" "\n\t" //метка
"add r18, %0 ;1 cycle" "\n\t" //прибавляем к r18 ad0
"adc r19, %1 ;1 cycle" "\n\t" //прибавляем к r19 ad1
"adc %A3, %2 ;1 cycle" "\n\t" //прибавляем куда-то (к signal, что-ли? Вряд ли. Это ж метка на адрес массива) ad2
"lpm ;3 cycles" "\n\t" //загрузка из памяти программ. непонятно. вообще без идентификаторов
"out %4, __tmp_reg__ ;1 cycle" "\n\t" //загрузка в порт из временного регистра.
"sbis %5, 2 ;1 cycle if no skip" "\n\t" //проверяет наличие 2 в I" (_SFR_IO_ADDR(SPCR) Если да - выход. нет, - переход по метке.
"rjmp 1b ;2 cycles. Total 10 cycles" "\n\t"
:
:"r" (ad0),"r" (ad1),"r" (ad2),"e" (signal),"I" (_SFR_IO_ADDR(PORTA)), "I" (_SFR_IO_ADDR(SPCR))
:"r18", "r19"
);
}
Можно просто объяснить, что означает каждая команда в этой проге. Каждый символ. Очень подробно, как для тупых. Например, что за b после rjmp 1b. Это, вроде, переход по метке? А метка "1".
Что за %. Это какая-то адресация? Что за "":" и буквы в кавычках. Почему LPM такой одинокий. и т.п.
uint8_t sig[] = { }; //объявим в начале массив. Ну, и заполним его 255 занчений. Писать их не буду,ибо много букаф.
const uint8_t *Red[]={sig}; //объявим массив констант-указателей из одного элемента (указатель на тот самый массив переменных). (Аналогично было сделано воригинале, но массив состоял из семи указателей)
Итеперь пишем аналогичную программу, с единственным отличием.
void static inline Signal_OUT2(const uint8_t *ReD, uint8_t aD2, uint8_t aD1, uint8_t aD0)
{
asm volatile( "eor r18, r18 ;r18<-0" "\n\t"
"eor r19, r19 ;r19<-0" "\n\t"
"2:" "\n\t"
"add r18, %0 ;1 cycle" "\n\t"
"adc r19, %1 ;1 cycle" "\n\t"
"adc %A3, %2 ;1 cycle" "\n\t"
"ld R0, Z+ ;3 cycles" "\n\t" // Загрузка в R0 происходит из ригистра z (ну, я, по крайней мере, на это надеюсь). Перед ReD стоит ограничитель "е", указывающий на адресный ригистр. могу предположить, что подразумевается, что в R0 загружается переменная из массива, на который указывает *ReD. Могу предположить, что требуется так же инкремент адреса. Иначе, это действие лишино смысла.... Наверно. плохо понимаю, как это работает.
"out %4, __tmp_reg__ ;1 cycle" "\n\t"
"sbis %5, 2 ;1 cycle if no skip" "\n\t"
"rjmp 2b ;2 cycles. Total 10 cycles" "\n\t"
:
:"r" (aD0),"r" (aD1),"r" (aD2),"e" (ReD),"I" (_SFR_IO_ADDR(PORTA)), "I" (_SFR_IO_ADDR(SPCR))
:"r18", "r19"
);
}