Прикошачиваем генератор к UART (Help!)

Подключаем наши девайсы к компьютеру. Обсуждаются: порты, протоколы, драйвера, языки программирования и т.д.
Ответить
YS
Друг Кота
Аватара пользователя
Сообщения: 7518
Зарегистрирован: Вс мар 29, 2009 22:09:05

Сообщение YS »

Да и в Си для AVR, после ассемблера буду чувствовать себя слепым котёнком, не видя,
что там происходит на аппаратном уровне, хоть Си и облегчает организацию
сложных и не очень, алгоритмов.
Ну, в большинстве мест результирующий ассемблерный код либо очевиден, либо нет необходимости в полном контроле. А там, где она есть, очень просто написать функцию целиком на ассемблере.

C действительно облегчает процесс создания алгоритма. Поэтому я в последнее время чаще всего поведение программы реализю на С, а критичные функции пишу на ассемблере и вызываю из С. Получается быстро и эффективно. :beer:
Разница между теорией и практикой на практике гораздо больше, чем в теории.
Контактная информация:
Реклама
Друг Кота
Аватара пользователя
Сообщения: 4468
Зарегистрирован: Вс янв 24, 2010 19:19:52
Откуда: Главный Улей России (Moscow)

Сообщение DX168B »

Это наверное потому, что я к АСМу уже привык. :)
В Сишных исходниках полностью разберусь, так как знаю его немного.
Где-то есть у меня парочка проектов, которые я писал на Си. Просто надо было быстро сфарганить прошивку для МК, ну и написал на Си. :)
У меня так: Если есть время, пишу на АСМе, если нет времени, то на Си.

Жаль, что ассемблерные вставки в Си получаются такого рода:

Код: Выделить всё

sbis 0x1A, 0
rjmp -1
rcall +45
.....
.......
I am DX168B and this is my favourite forum on internet!
Контактная информация:
Реклама
YS
Друг Кота
Аватара пользователя
Сообщения: 7518
Зарегистрирован: Вс мар 29, 2009 22:09:05

Сообщение YS »

Жаль, что ассемблерные вставки в Си получаются такого рода:
Ненене. Смотрите пример ниже. :)

Как я уже говорил, инлайновый ассемблерный синтаксис AVR-GCC больно заморочен, как по мне. Потому я пишу в отдельном файле. Например, вывод в HC595 (писал на асме, чтобы оптимизировать по скорости):

Главная программа:

Код: Выделить всё

#include <avr/io.h>
#include "HC595drv.h"

void main(void)
{
  HC595_InitLines();

  while (1)
  {
    HC595_ShiftByte(0xFF);
  }
}
HC595drv.h:

Код: Выделить всё

#ifndef HC595DRV_H
#define HC595DRV_H

//Тут определяем пины, к которым физически подключена HC595

#define HC595_PORT	PORTC
#define HC595_DDR	DDRC
#define HC595_DS	PC5
#define HC595_SH_CP	PC3
#define HC595_ST_CP	PC4

#ifndef __ASSEMBLER__

//И объявляем функции, написанные в HC595drv.s

extern void HC595_InitLines(void);
extern void HC595_ShiftByte(unsigned char b);

#endif

#endif
HC595drv.s (ассемблерный файл):

Код: Выделить всё

#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).
Разница между теорией и практикой на практике гораздо больше, чем в теории.
Контактная информация:
Друг Кота
Аватара пользователя
Сообщения: 4468
Зарегистрирован: Вс янв 24, 2010 19:19:52
Откуда: Главный Улей России (Moscow)

Сообщение DX168B »

У меня дизассемлированый листинг по другому выводится(так же, как я и указал выше.)
Среда AVR Studio + WinAVR. А Ваш листинг АСМа красив. :) Я вот извращался тоже с вставками, но у меня шло только на низкоуровневом ассемблере. Ну что-ж почитаю мануалы к AVR GCC
I am DX168B and this is my favourite forum on internet!
Контактная информация:
Реклама
Эиком - электронные компоненты и радиодетали
YS
Друг Кота
Аватара пользователя
Сообщения: 7518
Зарегистрирован: Вс мар 29, 2009 22:09:05

Сообщение YS »

У меня дизассемлированый листинг по другому выводится
А-а-а, так вы про дизассемблер... Не, там, к сожалению, ничего не поделать, будут страшные цифири. То, что выше, я писал руками, это не дизассемблер, естесственно. :)))
Разница между теорией и практикой на практике гораздо больше, чем в теории.
Контактная информация:
Реклама
Первый раз сказал Мяу!
Сообщения: 31
Зарегистрирован: Ср окт 20, 2010 18:41:27

Сообщение MrHowl22 »

Чет, появились новые сообщения, пока я расчехлялся отправить это...

YS писал(а): Код несколько заморочен оттого, что написан инлайном по правилам GCC. Я ассемблерные функции обычно пишу в отдельном файле, так проще.
А можно каким ни будь похожим методом получать данные из массива в ОЗУ? Чтоб просто добавить подпрограмму в общий файл и при работе с переменным массивом вызывать ее.

Так, все что хотел, ну,кроме адекватного изменения частоты из массива переменных, я сделал. Правда, на эмуляторе работает из-под хвоста, но как только с кем ни будь проконсультируюсь по поводу фьюзов. пересяду на макет. :tea:
Реклама
YS
Друг Кота
Аватара пользователя
Сообщения: 7518
Зарегистрирован: Вс мар 29, 2009 22:09:05

Сообщение YS »

А можно каким ни будь похожим методом получать данные из массива в ОЗУ?
Есть команды lds и ld.
Разница между теорией и практикой на практике гораздо больше, чем в теории.
Контактная информация:
Первый раз сказал Мяу!
Сообщения: 31
Зарегистрирован: Ср окт 20, 2010 18:41:27

Сообщение MrHowl22 »

А, есть же еще функция самопрограмирования. Правда, часто менять содержимое программы не слишком полезно. Но,как вариант надо рассмотреть.
Первый раз сказал Мяу!
Сообщения: 31
Зарегистрирован: Ср окт 20, 2010 18:41:27

Сообщение MrHowl22 »

YS писал(а):
А можно каким ни будь похожим методом получать данные из массива в ОЗУ?
Есть команды lds и ld.
Возможно, я бы смог ими воспользоваться, если бы хорошо понимал смысл исходной программы. А так, мое понимание очень напоминает объяснение блондинки на кафедре цифровой схемотехники. :)))

Код: Выделить всё



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" //И два регистра.
	);
      
Друг Кота
Аватара пользователя
Сообщения: 4468
Зарегистрирован: Вс янв 24, 2010 19:19:52
Откуда: Главный Улей России (Moscow)

Сообщение DX168B »

Код: Выделить всё

      ANDI    R16,$1C
      BREQ   _END   ;Проверяем на ошибки
      CLR     R17
_END:
Вот я дурак. :kill: :oops: 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!
Контактная информация:
Первый раз сказал Мяу!
Сообщения: 31
Зарегистрирован: Ср окт 20, 2010 18:41:27

Сообщение MrHowl22 »

DX168B писал(а):
Ищите калькулятор фьюзов в гугле.
Уже нашел, уже прошил. Теперь проблема с ЖК. Вот уж не думал, с чем будет. Но, чет не инициализируется. По нажатию кнопок пробует обновляться, но толку, если изначально только рад прямоугольников...
Шо ж за бред.
Друг Кота
Аватара пользователя
Сообщения: 4468
Зарегистрирован: Вс янв 24, 2010 19:19:52
Откуда: Главный Улей России (Moscow)

Сообщение DX168B »

Раз ряд квадратов, то скорее всего кодировка символов неверная. Как я понял, ЖК тут знакосинтезирующий, а не графический. Наверное компилятор не в ту кодировку переводит надписи.
I am DX168B and this is my favourite forum on internet!
Контактная информация:
Первый раз сказал Мяу!
Сообщения: 31
Зарегистрирован: Ср окт 20, 2010 18:41:27

Сообщение MrHowl22 »

Ряд квадратов - значит ЖК не инициализирован. Он такой, если просто подключить питание. Были уже такие траблы когда-то. тогда я их решил. но, тогда я писал под другим компилятором.
Первый раз сказал Мяу!
Сообщения: 31
Зарегистрирован: Ср окт 20, 2010 18:41:27

Сообщение MrHowl22 »

Так, ЖК запустил. Правда, глючит. Но, надеюсь, отлажу.

Ну, что7 Кто ни будь поможет дружескими советами и консультациями переработать вышеприведенный ассемблерный кусочек? А то не хотелось бы тратить кучу времени на изучение языка программирования, ради одной подпрограммы? Тем более, что мне вскоре понадобиться рабочий генератор, а не знания языков низкого уровня.
Друг Кота
Аватара пользователя
Сообщения: 6332
Зарегистрирован: Вт апр 24, 2007 07:45:40
Откуда: Minsk

Сообщение Jack_A »

MrHowl22 писал(а): Ну, что7 Кто ни будь поможет дружескими советами и консультациями переработать вышеприведенный ассемблерный кусочек?
Перерабатывать ничего не буду, а вывод массива в порт выглядит так :

Код: Выделить всё

          ldi  XL,low(array)
          ldi  XH,high(array)
          lds  r16,N
loop:   ld   r17,x+
          out PORTD,r17
          dec r16
          brne loop
Первый раз сказал Мяу!
Сообщения: 31
Зарегистрирован: Ср окт 20, 2010 18:41:27

Сообщение MrHowl22 »

Спасибо, кнечно, но лучше консультация по проге.

Данная функция
Void static inline Signal_OUT(const uint8_t *signal, uint8_t ad2, uint8_t ad1, uint8_t ad0)

Стоит в программе в блоке после else
Далее за ней сразу идет отключение портов. Значит, генерация сигнала происходит внутри программы, согласно параметрам.

SG.acc - это прирост аккумулятора.

Код: Выделить всё

else
			{
			//start DDS
			Signal_OUT(SIGNALS[SG.mode],
						(uint8_t)((uint32_t)SG.acc>>16),
						(uint8_t)((uint32_t)SG.acc>>8),
						(uint8_t)SG.acc);
			//set signal level to 0
			R2RPORT=0x00;
			//display generator OFF
Как я понимаю алгоритм.

Код: Выделить всё

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 такой одинокий. и т.п.
YS
Друг Кота
Аватара пользователя
Сообщения: 7518
Зарегистрирован: Вс мар 29, 2009 22:09:05

Сообщение YS »

Подробное руководство по инлайновому ассемблеру AVR-GCC: http://we.easyelectronics.ru/AVR/assemb ... r-gcc.html

И оригинал из документации: http://www.nongnu.org/avr-libc/user-man ... e_asm.html

По прочтении этого отпадут вопросы по поводу всяких волшебных символов.

Если очистить код от шелухи, то получится следующее:

Код: Выделить всё


; готовим временные регистры, с которыми будем работать
            eor r18, r18   
            eor r19, r19    
; начинаем цикл
            1:"        

; прибавляем смещения
        
            add r18, %0  
            adc r19, %1   
            adc %A3, %2  

; загружаем данные

            lpm         

; и выводим

            out %4, __tmp_reg__

; проверяем условие цикла

            sbis %5, 2     
            rjmp 1b    
Почему LPM такой одинокий. и т.п.
Потому, что он не требует операндов. Он сам по себе кладет содержимое, адресованное регистром Z (ZH:ZL <=> ) в R0.
Разница между теорией и практикой на практике гораздо больше, чем в теории.
Контактная информация:
Первый раз сказал Мяу!
Сообщения: 31
Зарегистрирован: Ср окт 20, 2010 18:41:27

Сообщение MrHowl22 »

О, мануальчик. Почитаем... :tea:
Первый раз сказал Мяу!
Сообщения: 31
Зарегистрирован: Ср окт 20, 2010 18:41:27

Сообщение MrHowl22 »

Если попробовать так.

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"
);
}
YS
Друг Кота
Аватара пользователя
Сообщения: 7518
Зарегистрирован: Вс мар 29, 2009 22:09:05

Сообщение YS »

Особо в код не вчитывался (некогда), но ход мысли правильный.
Разница между теорией и практикой на практике гораздо больше, чем в теории.
Контактная информация:
Ответить

Вернуться в «Интеграция с ПК»