Notice: Memcache::get(): Server localhost (tcp 11211) failed with: Connection refused (111) in /home/a146/www/we.easyelectronics.ru/engine/lib/external/DklabCache/Zend/Cache/Backend/Memcached.php on line 134
Особенности адресации контроллера TM1638 для индикаторов с ОА. / Деталька / Сообщество EasyElectronics.ru

Особенности адресации контроллера TM1638 для индикаторов с ОА.

Приобрел модуль для экспериментов и возможно встраивания «LED&KEY» на чипе TM1638 от китайского чипмейкера Titan Micro Electronics. В пакете кроме самого модуля ничего не было, пришлось разбираться, благо модули на чипе TM1638 популярны у Ардуинщиков и в инете разрозненная информация по ним есть.
Все разнообразие модулей сводится к трем разновидностям, о них ниже…

  1. Уже упомянутый «LED&KEY», имеет 2х4 семисегментных индикатора 0,36" с ОК (3641АН, 9.14mm), 8 красных светодиодов, 8 кнопок, габариты 75х48х9;



    Схема:



    В конце поста приложена более крупная схема из инета.

  2. «JY-LKM1638», имеет 2х4 семисегментных индикатора 0,56" с ОК (5461AS, 14.20mm), 8 двухцветных светодиодов, 8 кнопок, габариты 102x50x10;



    Схема:



    В конце поста приложена более крупная схема из инета.
    Основное отличие от «LED&KEY» — более крупные индикаторы и двухцветные светодиоды. Также он сделан как проходной модуль, для облегчения соединения до 6 модулей в цепочку.

  3. «QYF-TM1638», имеет 2х4 семисегментных индикатора 0,36" с ОА (3461BS, 9.14mm), 16 кнопок, отдельные светодиоды отсутствуют, габариты 68х69х9;



    Схемы не нашел, основное отличие от «LED&KEY» — применение индикаторов с ОА. Я думал, что практически это означает, что выводимые знаки на нем будет необходимо инвертировать. Все оказалось гораздо хуже…

Подключение индикаторов

Начал разбираться, как к одному чипу можно подключить индикаторы с разной полярностью.
Выходы для управления индикаторами в даташите описаны так:



Схемы подключения индикаторов с ОК и ОА также присутствуют:



Судя по схемам подключения чип может обслуживать не более 8 индикаторов с ОК, и до десяти индикаторов с ОА.
То же самое написано в даташите:





Адресация символов

Чип имеет 16 регистров для хранения кодов отображаемых символов. Причем в 8 четных хранятся полноценные байты для отображения символов, в нечетных для отображения используются только два младших бита.
Причем это применимо только к индикаторам с ОК, управление индикаторами с ОА может кому то напомнить удаление аденоидов через задний проход. Подробнее:

Адресная табличка для управления индикаторами с ОК:
Здесь SEG1-SEG8 обозначают сегменты индикатора, GRID1-GRID8 разряды.



Вполне нормальная адресация, правда байты идут через один, что ставит под сомнение режим автоинкремента байтов чипом.

Адресная табличка для управления индикаторами с ОА:
Здесь GRID1-GRID8 обозначают сегменты индикатора, SEG1-SEG10 разряды.



Сначала я составил ее в уме, не поверил, перерисовал на твердой копии, перепроверил, офигел…
Даже на контроллере, имеющем битовый процессор вывод одного!!! знакоместа займет не менее 16 ассемблерных команд ((установка адреса + битовая операция) * 8 (кол-во бит в байте)).
Если говорить о контроллере его не имеющем (AVR, Arduino), то битовая операция превратится в чтение-модификацию-запись, т.е. не менее 32 ассемблерных команд.

Для чего я вообще занялся адресацией индикатора с ОА, ведь у меня на модуле индикаторы с ОК?

Для одного проекта у меня применен 10-разрядный семисегментный индикатор. Сейчас в нем прописаны ЖКИ на 2xML1001 или на HD44780. Но ЖКИ при всем удобстве не сравнить с крупным LCD индикатором.
Переделать модуль «LED&KEY» в 10-разрядный теоретически несложно. Тем более я хотел заменить индикаторы 0,36" на 0,56". Имеющиеся индикаторы заменить на 5641 (ОК, есть), добавить пару одиночных или двухразрядную сборку ОА (найду).
Практически это будет очень похоже на схему «JY-LKM1638», если двухцветные светодиоды заменить на двухразрядную сборку ОА. Конечно применять в одном дисплее индикаторы с ОК и ОА немного странно, но дома пошла бы.
После просмотра картинки адресации индикаторов с ОА контроллером TM1638, у меня такая крамольная мысль пропала.

P.S. Краткая выжимка из комментариев…
Я сразу, как поглядел на адресацию индикаторов с ОА, понял что это гиблое дело, даже проверочный код не стал писАть. Некоторым это показалось неочевидно. Развязался флейм, для проверки своего продположения я попросил VGA предоставить проверочный код. Код на ассемблере MCS-51 был предоставлен. Приведу результаты тестирования, вырезка, орфография сохранена:
Из за чего все это начиналось — накладные расходы на дебильную адресацию чипом TM1638 индикаторов с ОА. Теперь можно посчитать код от VGA:



Брекпойнты стоят в начале и конце функции преобразования. К ним привязана простейшая функция

FUNC void Counter (void) {
        unsigned long int val;
        val = states;
        printf("States (dec) = %lu\n", val);
}

Она просто выводит в окно отладчика кол-во выполненных команд на момент вызова.
Итого, 1169 — 31 = 1138 команд потрачено на дебильную адресацию…

Меня тут-же поправили, что речь идет не о командах, а о тактах. Для классических MCS-51 это не совсем так, команда у них выполняется за 1 и более циклов, каждый из них состоит из 12 тактов (периодов тактовой частоты). В новых MCS-51 эти понятия равнозначны. Удачи, читайте комментарии...
  • 0
  • 19 июня 2016, 21:24
  • anakost
  • 2
Файлы в топике: JY-LKM1638-schematic.jpg, led_key-schematic.gif

Комментарии (89)

RSS свернуть / развернуть
А что, у тебя настолько мало флеша, что 32 команды не влезут?
+1
  • avatar
  • Vga
  • 19 июня 2016, 21:42
Бывает и мало, например на AT89C2051, но я пишу на ассемблере и всегда (почти) могу извернутся и вместить то что мне надо.
Но такой адресации мне не надо, 32 команды это для одного разряда, для 10 их уже 320, и это навскидку, без накладных расходов…
0
32 команды это для одного разряда, для 10 их уже 320
Для таких задач есть циклы. Так что расходы на 10 разрядов не сильно будут отличаться от расходов на один.
0
Для примера.
Прежде всего, необходим фреймбуфер — так как читать из дисплея нельзя (насколько я понял даташит). Его можно использовать в двух режимах — хранить исходные данные и трансформировать их при выводе или хранить уже трансформированные данные. Предположим первый вариант и включение «8 ОК, 2 ОА».
void SendBuffer() {
  uint8_t D9 = Buf[8];
  uint8_t D10 = Buf[9];
  for(uint8_t i = 0; i < 8; i++) {
    SendByte(Buf[i]);
    SendByte((D9 & 0x01) | ((D10 & 0x02) >> 1));
    D9 >>= 1;
    D10 >>= 1;
  }
}

При компиляции под AVR получается менее 30 команд. При желании можно трансформировать в функцию для расчета очередного байта, которую можно включить в прерывании:
uint8_t NextByte(uint8_t Index) {
  if(Index & 0x01) {
    Index >>= 1;
    return ((Buf[8] >> Index) & 0x01) | (((Buf[9] >> Index) & 0x01) << 1);
  }
    else return Buf[Index >> 1];
}

Она компилируется менее оптимально (где-то на треть больше), но на асме можно воспользоваться особенностями архитектуры и оптимизировать.
+1
Она компилируется менее оптимально (где-то на треть больше)
А точнее, 36 команд против 25, при компиляции на GCC 4.5.3 -O2. Основная причина — необходимость сдвигать Buf[8] и Buf[9] каждый раз заново (на AVR и x51 есть только однобитный сдвиг, на арме с его шифтером разницы не будет).
0
SendByte((D9 & 0x01) | ((D10 & 0x02) >> 1));

Упс.
SendByte((D9 & 0x01) | ((D10 & 0x01) << 1));
0
Вывод непосредственно на HD44780 делает

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;  Подпрограмма вывода на индикатор							    		 ;; 
;;    A - исходный байт						                           		 ;;
;;  Используемые регистры:                                          		 ;;
;;    A		                           					             		 ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
LCD_SendDataByte:
	MOV		PN_IntfPort, A				; 
	CLR		PN_LCDEN					; Защелкнуть данные
	MOV		A, #TM_InitWait3			; Время задержки, 215 мс
	ACALL	LCD_Waiting					;
	RET									;
;

Она просто заносит в IntfPort требуемый символ из аккумулятора. Один раз для символа. А вы предлагаете это делать 32 раза.
0
Что-то я не понял, что я предлагаю делать 32 раза.
К тмоу же, аналогом приведенной функции в моем коде является SendByte, которую я не расписывал (она не зависит от того, CC или CA индикаторы). И, полагаю, софтовый SPI на 8051 займет заметно больше места, нежели перекодирование данных под CA.
+1
Эта подпрограмма и является софтовым SPI на MCS-51, она опускает EN, защелкивая байт, поднимает и защелкивает его LCD_Waiting. К сожалению модуль неполный, не влезает, хотя по меркам С тут и нет ничего…
0
Приведенный тобой код совершенно иррелевантен теме.
И объясни таки, что я предлагаю делять 32 раза.
+1
Это для контроллера с битовым процессором. Если контроллер его не имеет, посылка будет та же, но собирать биты символа ему придется через промежуточный буфер чтения-модификации-записи.
Это все что я хотел сказать…
0
Я таки не понимаю, о каком коде ты говоришь. В этой ветке — только LCD_SendDataByte, битовые возможности 8051 не использующий. Да и в простыне код, релевантный именно выводу на индикатор, а не подготовке данных, битовые возможности не использует (либо использует в том объеме, в каком они есть и на других МК — типа «дернуть одним пином из порта»).
0
Приведенный мной код требует одного вывода для отправки символа во внешний порт. Фактически это не так, HD44780 подключен по 4-х проводной схеме и фактически для вывода одного символа требуется послать 2 байта. Это издержки индикатора на HD44780.
Для отправки одного символа через МТ1638 на индикатор подключенный по ОА требуется послать 8 байт с адресом и еще 8 байт с битами символа.
Вы статью читали?
0
Для отправки одного символа через МТ1638 на индикатор подключенный по ОА требуется послать 8 байт с адресом и еще 8 байт с битами символа.
Зачем? Все гораздо проще — подготавливаешь в памяти буфер данных и отсылаешь его за один присест (одна команда для установки адреса на 0 и затем 16 команд передачи байта).
+1

Это из статьи, одному символу соответствуют SEG (1..10). Вот как приятно, например SEG1, пишем первый бит символа в бит 0 по адресу 00H, затем пишем второй бит символа в бит 0 по адресу 02H, <пропущено>, затем пишем восьмой бит символа в бит 0 по адресу 0ЕH. Это подготовка одного байта буфера.
Выплюнуть в контроллер это одинаково, что ОК, что ОА.
0
Я-то читал, а вот читал ли ты мой код? Именно этим он и занимается — берет буфер, скажем "__80.000" (закодированный сразу в кодах семисегментника, т.е. вместо 8, например, там будет 0x7F, а вместо точки — 0x80) и выплевывает его в индикатор в виде 16 последовательных байт. И на все про все на AVR уходит 25 команд (на 8051, похоже, хуже — либо я оптимизирую хуже, чем GCC).
+1
(на 8051, похоже, хуже — либо я оптимизирую хуже, чем GCC)
А, нет, в 21 команду уложился:
SendBuffer:
    MOV R0, #Buf           ; Buffer pointer
    MOV R2, #Buf+8         ; D9 buffer
    MOV R3, #Buf+9         ; D10 buffer
  loop:
    MOV A, @R0             ; Send CC byte
    ACALL SendByte
    MOV R1, #0             ; Prepare CA byte
    MOV A, R2              ; Load D9
    JNB A.0, S1            ; Move LSB to R1
    ADD R1, #1         
  S1:
    RR A                   ; Shift and store D9
    MOV R2, A
    MOV A, R3              ; Load D10
    JNB A.0, S2            ; Move LSB to R1
    ADD R1, #2
  S2:
    RR A                   ; Shift and store D10
    MOV R3, A
    MOV A, R1              ; Send CA byte
    ACALL SendByte
    INC R0                 ; Increment pointer
    CJNE R0, #Buf+8, loop  ; Check loop condition
    RET
0
А понял, вы думаете что SEG это сегменты индикатора. Нет это разряды, сегменты это GRID.
0
Нет, я думаю, что на тебя тупняк напал. Приведенная функция выведет данные на дисплей, при условии:
1) Дисплей включен нестандартно, первые 8 разрядов с CC, следующие 2 с CA
2) Данные для вывода на дисплей лежат в буфере uint8_t Buf[10], в виде маски для семисегментного индикатора (т.е. цифра 0 — 0x3F, 1 — 0x06, ..., 8 — 0x7F).
3) Функция SendByte отправляет на индикатор байт данных с автоинкрементом адреса
4) Перед вызовом SendBuffer адрес в индикаторе выставлен на 0.
0
Упс. Вот что бывает, когда невнимательно смотришь в док по асму и пишешь в блокнотик, а не в ассемблер:
ADD R1, #1         
...
ADD R1, #2

Нужно заменить или на
ORL 1, #1 ; Абсолютный адрес регистра R1 в нулевом банке - не помню уже синтаксис, возможно надо писать @1
...
ORL 1, #2

или на
INC R1
...
INC R1
INC R1
0
Первый байт, выплюнутый на индикатор, распределится по сегментам SEG1.0-SEG8.0, второй битами 0-1 что то включит/выключит через сегменты SEG9-SEG10 (что именно?), третий распределится по сегментам SEG1.1-SEG8.1 и т.д.
Я говорю о подключении индикатора с ОА, не с ОК, или мы друг друга не понимаем?
0
Данный пример рассматривает предложенный тобой изврат «8 цифр ОК и 2 цифры ОА». Надо отметить, на 8-битках с их убогим сдвигом он выигрывает у варианта «10 цифр с ОА». Впрочем, в таком случае заметно лучше будет работать вариант с 16-байтным буфером уже в формате RAW-данных для отправки на индикатор. В таком случае, вывод символа в буфер будет не Buf[3] = DigitToSegs[8], а Put(3, DigitToSegs[8]).
void Put(uint8_t Digit, uint8_t C) {
  uint8_t Shift = Digit > 7 ? 1 : 0;
  uint8_t Mask = 1 << (Digit % 8);
  for(uint8_t i = 0; i < 8; i++)
    if(C & (1 << i))
      Buf[i + Shift] |= Mask;
    else
      Buf[i + Shift] &= !Mask;
}

Данная функция, однако, занимает уже 51 команду на AVR. Быть может, можно ее и оптимизировать.
+1
>> предложенный тобой изврат «8 цифр ОК и 2 цифры ОА».
Я от него отказался и написал почему.
Извините, недостаточно знаю С, чтобы понять хитрые извраты C. Вы уже приводили функцию для индикатора с ОА?
0
Я от него отказался и написал почему.
Почему? Потому что для этого надо написать функцию размером в пару десятков команд, при двух килобайтах флеша в распоряжении?
Вы уже приводили функцию для индикатора с ОА?
В данном случае это она и есть. И тут нету извратов, достаточно простой и прямолинейный код.

Ну и чтобы десять раз не ходить — поясню твою ошибку. Она видна из приведенного кода — ты мыслишь шаблоном «считать цифирки по одной и сразу слать в индикатор». Это не нужно. Вместо этого надо выделить фреймбуфер и складывать цифирки туда, а затем просто отправить буфер на индикатор в один присест.
С фреймбуфером возможны два варианта. Первый — выводить в него данные как в строку, т.е. если надо в третий разряд вывести число 7 — пишем Buf[2] = 7 (MOV #Buf+2, #7), а конвертировать в требуемые индикатору данные при отправке. Этот вариант у меня реализуется функцией SendBuffer. Второй вариант — хранить буфер в том виде, в каком он отправляется на индикатор, тогда требуется конвертировать выводимый символ при записи его в буфер. Этим и занимается функция Put.
+1
т.е. если надо в третий разряд вывести число 7 — пишем Buf[2] = 7 (MOV #Buf+2, #7)
Ну, точнее, поместить туда надо не само число, а соответствующий ему код семисегментного индикатора, т.е. байт, где каждому сегменту индикатора соответствует бит. Так же как в строке хранится не число 7, а ASCII-код символа «7».
0
Она видна из приведенного кода — ты мыслишь шаблоном «считать цифирки по одной и сразу слать в индикатор».
Мой код шлет в индикатор данные из промежуточного буфера, а складывает его туда другой модуль.
А вам бы посоветовал прокомментировать код (в смысле в коде), хоть что то будет понятно и проверяемо…
0
Вижу я, как оно «шлет из промежуточного буфера». Хотя, комментарии наводят на мысль, что в коде где-то есть сперва преобразование выводимого числа в упакованный BCD, затем распаковка в неупакованный BCD и уже он побуквенно выводится в индикатор с попутным удалением leading zeroes.
А вам бы посоветовал прокомментировать код (в смысле в коде)
В стиле «CLR F0; очистить пользовательский флаг»? Поведение кода описывается кодом, а не комментариями к нему, и у тебя я изучал именно код (комменты к нему по большей части довольно бесполезны).
+1
Ну что тут можно ответить, это из другого модуля

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;  Подпрограмма преобразования целого 4-x байтного двоичного числа  		 ;;
;;  без знака в 10 байтное неупакованное двоично-десятичное         		 ;;
;;  Источник - 4 младших разряда AV_Quotient, приемник - AV_BCDBuffer		 ;;
;;  Задействованные разряды источника (4 мл. байта) при работе обнуляются	 ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
FM_BinToBCD:
	MOV		R0, #AV_BCDBuffer		; буфер результата
	MOV     R2, #0x00		  		; обнулим счетчик кол-ва значащих цифр
locBB_00:
	CLR		F0						; бит нулевого частного (окончания счета)
	MOV		R1, #AV_Quotient+3		; со старшего разряда 
	MOV		R3, #4					; 4 байта
	MOV		B, #0					; обнуление остатка
locBB_01:
; -- Старший ниббл ---------------------------------------------------------- ;
	MOV     A, @R1					;
	ANL     A, #0xF0				;
	ORL     A, B					;
	SWAP    A						;
	MOV     B, #10					;
	DIV     AB						; деление на 10
	SWAP    A						; 
	XCH     A, @R1					;
; -- Младший ниббл ---------------------------------------------------------- ;
	SWAP    A						;
	ANL     A, #0xF0				;
	ORL     A, B					;
	SWAP    A						;
	MOV     B, #10					; 
	DIV     AB						; деление на 10
	ORL     A, @R1					;
	JZ		locBB_02				;
	SETB	F0						;
locBB_02:
	MOV     @R1, A					;
	DEC		R1						; к младшему разряду
	DJNZ	R3, locBB_01			; на выходе B - очередная цифра числа
	MOV     @R0, B			  		; сохранить в памяти
	INC     R0						;
	INC     R2			      		; кол-во найденных цифр
	JB		F0, locBB_00			; продолжать пока частное > 0
	MOV		A, R2					;
	JNZ		locBB_03				; проверка счетчика на 0
	INC		A						; правый ноль учитывается
locBB_03:
	JNB		MK_LimitValue, locBB_04	;
	ADD		A, #-3					;
	MOV		AV_MAN_CNT, #3			; кол-во значащих цифр дробной части
	SJMP	locBB_05				;
locBB_04:
	JNB		MK_LimitTicks, locBB_05	;
	ADD		A, #-6					;
	MOV		AV_MAN_CNT, #6			; кол-во значащих цифр дробной части
locBB_05:
	MOV		AV_NUM_CNT, A			; кол-во значащих цифр целой части
	RET								;
; --------------------------------------------------------------------------- ;
;	
0
ОК, упакованного нет (хотя комменты его упоминают — «Подпрограмма вывода результата из буфера упакованных BCD цифр» — лишний раз показывая, что комментам верить нельзя, только коду). Но зачем тебе BCD? Для вывода на LCD тебе нужен ASCII, переводи сразу в него. А для вывода на LED — тебе нужен 7SEG-код, опять же, трансформируй прямо в него.
И вот подобные избыточные преобразования uint32_t -> BCD -> ASCII — уже сожрут места не хуже чем конвертация из CC-кода в CA-код.

P.S. Дай полный код (можно в личку), я интегрирую в него мой вариант кода и ты сможешь проверить экспериментально, раз уж код читать не умеешь.
0
Да без проблем, только к личке код не прицепляется, только текст.
0
В личке можно кинуть ссылку на файлообменник, если не хочется код публиковать. Если это не проблема — можно и прямо к топику приколоть.
0
можно и прямо к топику приколоть
Ну я же сказал, не собираюсь даже писать код для такой дебильной адресации. Я думал вы про другой какой. А тут никто меня не переубедит, что 16*8 байт выплюнутые в MT1638 ничем ни хуже 2*8 байт выплюнутых в HD44780 или просто 8 байт посланных по SPI.
Какой тут еще код на это уе… ще?
0
Во-первых, какая разница, сколько байт туда выплюнуто? Имеет значение только решена ли задача (на это влияют время, потраченное на выплевывание, количество кода, занятого выплевыванием, etc — как именно влияют зависит от проекта).
Во-вторых, ты считаешь неправильно. Не изучал вплотную протокол TM1638, но выплюнуть туда требуется 16 байт данных и требуемое для этого количество команд (максимум 34 байта — если каждый байт данных надо предварять командой, минимум 17 байт — если адрес не передавать, а команду write достаточно послать один раз). Обрати внимание, для вывода на CC индикатор, подключенный к TM1638 требуется передать ровно столько же байт.
В-третьих, применение индикатора определяется не уебищностью его интерфейса (исключения бывают, но это не тот случай, к тому же со всей инициализацией типичная реализация интерфейса HD44780 занимает от 500 команд и больше), а его потребительскими качествами.

Отсюда вывод: ты нихрена не понял и гонишь пургу на вполне нормальный чип.
+1
Ну и вариант с отправкой буфера на AVR, причем сразу обобщенный. Включение — CA, произвольное число разрядов. 46 команд на AVR (92 байта, что достаточно заметно на камнях с 1-2 килобайтами, на 8051 код в среднем раза в полтора плотнее — т.е. байт 60 стоит ожидать при компиляции хорошим компилятором).
void SendBuffer(uint8_t Count, uint8_t *Buf) {
  for(uint8_t i = 0; i < 8; i++) {
    uint16_t Tmp = 0;
    for(int8_t j = Count - 1; j >= 0; j--) {
      Tmp = (Tmp << 1) | (Buf[j] & 0x01);
      Buf[j] >>= 1;
    }
    SendByte(Lo(Tmp));
    SendByte(Hi(Tmp));
  }
}

Можно немного (команд 7) сэкономить, выбросив поддержку индикаторов более 8 разрядов (равно как и заменяющих их дискретных диодов):
void SendBuffer(uint8_t Count, uint8_t *Buf) {
  for(uint8_t i = 0; i < 8; i++) {
    uint8_t Tmp = 0;
    for(int8_t j = Count - 1; j >= 0; j--) {
      Tmp = (Tmp << 1) | (Buf[j] & 0x01);
      Buf[j] >>= 1;
    }
    SendByte(Tmp);
    SendByte(0);
  }
}


И вот эти 10 строчек (полсотни-сотня байт) — проблема и «уебищный индикатор с дебильной адресацией»? Ну-ну.

P.S. Поскольку индикаторов у меня нет и проверить код не на чем — могут быть ошибки. Но врядли они сильно повлияют на размер и сложность кода.
+1
А на ARM'е, где есть нормальный шифтер, можно не портить буфер и при этом не терять в производительности и размере кода:
void SendBuffer(uint8_t Count, uint8_t *Buf) {
  for(uint8_t i = 0; i < 8; i++) {
    uint16_t Tmp = 0;
    for(int8_t j = Count - 1; j >= 0; j--) {
      Tmp = (Tmp << 1) | ((Buf[j] >> i) & 0x01);
    }
    SendByte(Lo(Tmp));
    SendByte(Hi(Tmp));
  }
}
0
А еще на нем лучше использовать int i, int j.
0
Это все говорильня Сишника — «какой у меня короткий код получился».
Если это все перевести в ассемблер, получится конфуз. Тут и так все понятно, вместо прямой отправки 10 байт на индикатор из буфера придется их побитно перемешать в дополнительном буфере.
Нелепые лишние телодвижения и лишняя память, как кому нравится. Мне так не очень…
-3
Это все говорильня Сишника — «какой у меня короткий код получился».
Нет, это все говорильня ассемблерщика — «как я на ассемблере в 2051 укладываю». И выкладывает простыню, которой постыдится компилятор. Впрочем, они уже давно знают ассемблер лучше ассемблерщиков.
Если это все перевести в ассемблер, получится конфуз.
Я перевел в ассемблер. Как компилятором, так и вручную. И результат вполне компактен. Это у тебя только говорильня — «дебильная адресация», «на 10 разрядов надо 320 команд», «посылать 128 байт».
Нелепые лишние телодвижения и лишняя память, как кому нравится.
Покуда они укладыываются в доступные ресурсы — все ОК. Потому как все незадействованные ресурсы выкинуты впустую.
+1
Я перевел в ассемблер.
Код в студию (ассемблер), без него это разговор слепого с глухим…
-1
Ручной вариант, автоматический вариант (код ему сам скормишь, не маленький).
+1
Ручной вариант я покручу, посмотрю, отвечу.
А автоматический вариант, вы где это выдернули? Регистр EAX относится к x86 процессору, а RBR кажется к X86-64 Architecture.
0
А ты выбери в списке компиляторов GCC-AVR. И не забудь задать опцию -O2. Компилятора для x51 там нет, поэтому размеры я везде для AVR и указывал.
0
Зачем мне на ассемблере GCC-AVR? Для доступа к AT&T-синтаксису? Зачем он мне?
0
***ть. Затем, чтобы убедиться, что эти строчки на С разворачиваются во вполне компактный листинг на ассемблере и не гнать пургу вида «Это все говорильня Сишника — «какой у меня короткий код получился». Если это все перевести в ассемблер, получится конфуз». А затем вспомнить эмпирические наблюдения и прикинуть, что на 8051 ассемблерный код будет еще в пару раз компактней.
+4
Пока я только увидел листинг X86-64 Architecture на синтаксисе AT&T. Если это и относится к ТМ1638, я этой связи не вижу.
0
+1
Вот выхлоп компилятора и оформите отдельным, независимым ассемблерным файлом.
А потом уже можно будет говорить о «Перекодирование в «дебильную кодировку ОА на ТМ1638» действительно такое простое, что укладывается в два десятка ассемблерных команд».
0
Зачем? Я дал ссылку на онлайн-компилятор, где каждый может убедиться своими руками. Также можно взять SDCC или Keil C51 и проверить то же самое, но уже на 8051.
0
P.S. Тут, кстати, те, кому не лень вчитываться в листинг говорят, что компилятор gcc AVR сильно чудит, неясно на кой хрен считая все в 16 битах. То есть, выдаваемые им листинги неоптимальны и могут быть значительно сокращены.
+1
И еще, как описан Buf, что делает SendByte. Подробнее пожалуйста…
0
Я все это уже писал. И кто тут не читает, спрашивается?

P.S. Поскольку мои сообщения часто подразумевают чтение по порядку — используй стрелочку под комментарием, чтобы узнать, к какой ветке он относится.
+1
Данные для вывода на дисплей лежат в буфере uint8_t Buf[10], в виде маски для семисегментного индикатора (т.е. цифра 0 — 0x3F, 1 — 0x06, ..., 8 — 0x7F).
И как я могу проверить ваши слова не имея этой таблицы? Мне ее самому составлять?
0
Разумеется. Ты что, впервые на семисегментник выводишь? Я же не знаю, как у тебя сегменты индикатора по пинам TV1638 распаяны. Цифры в примере для распайки A -> GRID0/SEG0, B -> GRID1/SEG1,… H -> GRID8/SEG8. Также подразумевается, что разряды висят на GRIDx/SEGx последовательно.
0
Ах вот оно чо, Михалыч!
Значит вводим табличную перекодировку, табличку NxM забиваем во флеш и радуемся!
Дисплей 10 символьный, 10х8 — 80 байт таблица перекодировки. Но тут сложнее, буфер индикатора — 16 байт побитной адресации.
Код в студии будет или как?
0
*FACEPALM*
uint8_t DigitToSegs[10] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6B, 0x7B, 0x07, 0x7F, 0x6F};

И меня откровенно заебало, что ты пытаешься уличить меня в подъебе. Его нет. Перекодирование в «дебильную кодировку ОА на ТМ1638» действительно такое простое, что укладывается в два десятка ассемблерных команд. Без каких-то там табличек, отсылок по двести байт на 10 символов и так далее.
+2
Понятно кода нет, но «Перекодирование в «дебильную кодировку ОА на ТМ1638» действительно такое простое, что укладывается в два десятка ассемблерных команд».
Но кода нет, и не будет…
-2
Кода нет?! Я тут что уже второй день пишу, в десятке вариантов? То, что ты не умеешь читать си — твоя проблема, а не моя, но я даже на ассемблер перевел два варианта.
+3
Хоть один рабочий, со всеми требуемыми определениями, константами, буферами и т.д. я не видел. Ни один нельзя взять и вставить ни в Студию, ни в Keil. Обрывки.
Нормальный ассемблерный код будет?
0
Нет. Мне лень писать драйвер TM1638 с софтовым SPI. Если ты подключал СС-варианты — то у тебя все требуемое уже есть. Я же реализую только ту часть, которая отличает СА индикаторы от СС.
0
Впрочем, можешь дать проект под СС индикатор, если такой у тебя есть, и я туда интегрирую свой код.
0
Полный код это излишне, давай договоримся так, имеем входной 10-байтный (10-разрядный) буфер. Символы кодированы в ASCII. Заполнение входного буфера к тексту программы не считаем. Также заведем и выходной буфер того же размера (перекодировать во входной не получится, уже накладные расходы). Main будет состоять из Init (стек и заполнение входного буфера) и Prog (собственно сама перекодировка). Зацикливать не нужно. Голый алгоритм, сравниваем правильность перекодировки входного буфера в выходной, и ее время.
-1
Символы кодированы в ASCII.
Зачем ASCII, если вывод идет на 7-сегментный индикатор?
Алсо, у меня нет под рукой 8051-тулчейна, так что болванку проекта (инит, подготовку входного буфера) напиши сам и скинь мне.
0
Устроит болванка на А51?
0
Вполне.
0
NAME TM_Coder

_VARS SEGMENT DATA
RSEG _VARS
IV_Value: DS 10; Буфер входных значений
IV_Value9 DATA IV_Value+9;
IV_Value8 DATA IV_Value+8;
IV_Value7 DATA IV_Value+7;
IV_Value6 DATA IV_Value+6;
IV_Value5 DATA IV_Value+5;
IV_Value4 DATA IV_Value+4;
IV_Value3 DATA IV_Value+3;
IV_Value2 DATA IV_Value+2;
IV_Value1 DATA IV_Value+1;
IV_Value0 DATA IV_Value+0;
OV_Value: DS 10; Буфер выходных результатов
OV_Value9 DATA OV_Value+9;
OV_Value8 DATA OV_Value+8;
OV_Value7 DATA OV_Value+7;
OV_Value6 DATA OV_Value+6;
OV_Value5 DATA OV_Value+5;
OV_Value4 DATA OV_Value+4;
OV_Value3 DATA OV_Value+3;
OV_Value2 DATA OV_Value+2;
OV_Value1 DATA OV_Value+1;
OV_Value0 DATA OV_Value+0;
PROGRAM SEGMENT CODE
CSEG AT 0;
RSEG PROGRAM;
USING 0;

Init:
MOV IV_Value0, 00000001b;
MOV IV_Value1, 00000010b;
MOV IV_Value2, 00000100b;
MOV IV_Value3, 00001000b;
MOV IV_Value4, 00010000b;
MOV IV_Value5, 00100000b;
MOV IV_Value6, 01000000b;
MOV IV_Value7, 10000000b;
MOV IV_Value8, 00010001b;
MOV IV_Value9, 10000100b;
RET
Prog:
RET
Out:
RET
Main:
ACALL Init; Заполним входной буфер
ACALL Prog; Преобразуем в коды TM1638 с ОА
// ACALL Out; Выведем для проверки

END
0
Забыл тег, кроме того для проверки алгоритма взял двоичный код.

NAME	TM_Coder


_VARS	SEGMENT	DATA
		RSEG	_VARS
IV_Value:		DS		10  ; Буфер входных значений
IV_Value9       DATA    IV_Value+9	    ; 
IV_Value8       DATA    IV_Value+8	    ; 
IV_Value7       DATA    IV_Value+7	    ; 
IV_Value6       DATA    IV_Value+6	    ; 
IV_Value5       DATA    IV_Value+5	    ; 
IV_Value4       DATA    IV_Value+4	    ; 
IV_Value3       DATA    IV_Value+3	    ; 
IV_Value2       DATA    IV_Value+2	    ; 
IV_Value1       DATA    IV_Value+1	    ; 
IV_Value0       DATA    IV_Value+0	    ; 
OV_Value:		DS		10  ; Буфер выходных результатов
OV_Value9       DATA    OV_Value+9	    ; 
OV_Value8       DATA    OV_Value+8	    ; 
OV_Value7       DATA    OV_Value+7	    ; 
OV_Value6       DATA    OV_Value+6	    ; 
OV_Value5       DATA    OV_Value+5	    ; 
OV_Value4       DATA    OV_Value+4	    ; 
OV_Value3       DATA    OV_Value+3	    ; 
OV_Value2       DATA    OV_Value+2	    ; 
OV_Value1       DATA    OV_Value+1	    ; 
OV_Value0       DATA    OV_Value+0	    ; 
	
PROGRAM	SEGMENT	CODE
	CSEG	AT	0		    ;	
	RSEG	PROGRAM			    ;
	USING	0  			    ;

Init:
	MOV	IV_Value0, 00000001b	    ;
	MOV	IV_Value1, 00000010b	    ;
	MOV	IV_Value2, 00000100b	    ;
	MOV	IV_Value3, 00001000b	    ;
	MOV	IV_Value4, 00010000b	    ;
	MOV	IV_Value5, 00100000b	    ;
	MOV	IV_Value6, 01000000b	    ;
	MOV	IV_Value7, 10000000b	    ;
	MOV	IV_Value8, 00010001b	    ;
	MOV	IV_Value9, 10000100b	    ;
	RET
	
Prog:
	RET
	
Out:
	RET
	
Main:
	ACALL	Init				; Заполним входной буфер
	ACALL	Prog				; Преобразуем в коды TM1638 с ОА
//	ACALL	Out				; Выведем для проверки

END
0
Стек забыл

NAME	TM_Coder

STACK	SEGMENT	IDATA
	RSEG	STACK
	DS		0x10  					; 
		
_VARS	SEGMENT	DATA
		RSEG	_VARS
IV_Value:		DS		10    ; Буфер входных значений
IV_Value9       DATA    IV_Value+9	    ; 
IV_Value8       DATA    IV_Value+8	    ; 
IV_Value7       DATA    IV_Value+7	    ; 
IV_Value6       DATA    IV_Value+6	    ; 
IV_Value5       DATA    IV_Value+5	    ; 
IV_Value4       DATA    IV_Value+4	    ; 
IV_Value3       DATA    IV_Value+3	    ; 
IV_Value2       DATA    IV_Value+2	    ; 
IV_Value1       DATA    IV_Value+1	    ; 
IV_Value0       DATA    IV_Value+0	    ; 
OV_Value:		DS		10			; Буфер выходных результатов
OV_Value9       DATA    OV_Value+9	    ; 
OV_Value8       DATA    OV_Value+8	    ; 
OV_Value7       DATA    OV_Value+7	    ; 
OV_Value6       DATA    OV_Value+6	    ; 
OV_Value5       DATA    OV_Value+5	    ; 
OV_Value4       DATA    OV_Value+4	    ; 
OV_Value3       DATA    OV_Value+3	    ; 
OV_Value2       DATA    OV_Value+2	    ; 
OV_Value1       DATA    OV_Value+1	    ; 
OV_Value0       DATA    OV_Value+0	    ; 
	
PROGRAM	SEGMENT	CODE
	CSEG	AT	0		    ;	
	RSEG	PROGRAM			    ;
	USING	0  			    ;

Init:
	MOV	IV_Value0, 00000001b	    ;
	MOV	IV_Value1, 00000010b	    ;
	MOV	IV_Value2, 00000100b	    ;
	MOV	IV_Value3, 00001000b	    ;
	MOV	IV_Value4, 00010000b	    ;
	MOV	IV_Value5, 00100000b	    ;
	MOV	IV_Value6, 01000000b	    ;
	MOV	IV_Value7, 10000000b	    ;
	MOV	IV_Value8, 00010001b	    ;
	MOV	IV_Value9, 10000100b	    ;
	RET
	
Prog:
	RET
	
Out:
	RET
	
Main:
;---------------------------------------------------------------------
	MOV		SP, #STACK-1	 ; 
;---------------------------------------------------------------------
	ACALL	Init			; Заполним входной буфер
	ACALL	Prog			; Преобразуем в коды TM1638 с ОА
//	ACALL	Out			; Выведем для проверки

END
0
Пробуй
NAME    TM_Coder

STACK   SEGMENT IDATA
        RSEG    STACK
        DS              0x10                                    ; 
                
_VARS   SEGMENT DATA
                RSEG    _VARS
IV_Value:               DS              10    ; Буфер входных значений
IV_Value9       DATA    IV_Value+9          ; 
IV_Value8       DATA    IV_Value+8          ; 
IV_Value7       DATA    IV_Value+7          ; 
IV_Value6       DATA    IV_Value+6          ; 
IV_Value5       DATA    IV_Value+5          ; 
IV_Value4       DATA    IV_Value+4          ; 
IV_Value3       DATA    IV_Value+3          ; 
IV_Value2       DATA    IV_Value+2          ; 
IV_Value1       DATA    IV_Value+1          ; 
IV_Value0       DATA    IV_Value+0          ; 
OV_Value:               DS              16                      ; Буфер выходных результатов
OV_Value15      DATA    OV_Value+15          ; 
OV_Value14      DATA    OV_Value+14          ; 
OV_Value13      DATA    OV_Value+13          ; 
OV_Value12      DATA    OV_Value+12          ; 
OV_Value11      DATA    OV_Value+11          ; 
OV_Value10      DATA    OV_Value+10          ; 
OV_Value9       DATA    OV_Value+9          ; 
OV_Value8       DATA    OV_Value+8          ; 
OV_Value7       DATA    OV_Value+7          ; 
OV_Value6       DATA    OV_Value+6          ; 
OV_Value5       DATA    OV_Value+5          ; 
OV_Value4       DATA    OV_Value+4          ; 
OV_Value3       DATA    OV_Value+3          ; 
OV_Value2       DATA    OV_Value+2          ; 
OV_Value1       DATA    OV_Value+1          ; 
OV_Value0       DATA    OV_Value+0          ; 
        
PROGRAM SEGMENT CODE
        CSEG    AT      0                   ;   
        RSEG    PROGRAM                     ;
        USING   0                           ;

Init:
        MOV     IV_Value0, 00000001b        ;
        MOV     IV_Value1, 00000010b        ;
        MOV     IV_Value2, 00000100b        ;
        MOV     IV_Value3, 00001000b        ;
        MOV     IV_Value4, 00010000b        ;
        MOV     IV_Value5, 00100000b        ;
        MOV     IV_Value6, 01000000b        ;
        MOV     IV_Value7, 10000000b        ;
        MOV     IV_Value8, 00010001b        ;
        MOV     IV_Value9, 10000100b        ;
        RET
        
Out:
        RET
        
ResetAddr:
    MOV R0, #OV_Value
    RET

SendByte:
    MOV @R0, A
    INC R0
    RET

SendBuffer: ; Assume Count in A, Buf* in R7
    MOV R2, #8 ; i(R2) = 8
    MOV R3, A  ; Count(R3) = Count(A)
  loop8:
    MOV R4, #0 ; Tmp(R5:R4) = 0
    MOV R5, #0
    MOV A, R3  ; j(R6) = Count
    MOV R6, A  
    ADD A, R7  ; R1 = Buf+Count
    MOV R1, A
  loopC:
    DEC R1     ; A = Buf[j]
    MOV A, @R1 
    RRC A      ; C = A & 0x01 ; A >>= 1
    MOV @R1, A ; Buf[j] = A
    MOV A, R4  ; Tmp = (Tmp << 1) | C
    RLC A
    MOV R4, A
    MOV A, R5
    RLC A
    MOV R5, A
    DJNZ R6, loopC ; if(--j) goto loopC
    MOV A, R4      ; SendByte(Lo(Tmp))
    ACALL SendByte
    MOV A, R5      ; SendByte(Hi(Tmp)
    ACALL SendByte
    DJNZ R2, loop8 ; if(--i) goto loop8
    RET

Main:
;---------------------------------------------------------------------
        MOV             SP, #STACK-1     ; 
;---------------------------------------------------------------------
        ACALL   Init                    ; Заполним входной буфер
        ACALL   ResetAddr
        MOV R7, #IV_Value
        MOV A, #10
        ACALL   SendBuffer                    ; Преобразуем в коды TM1638 с ОА
//      ACALL   Out                     ; Выведем для проверки

END
0
Не ошибается тот, кто ничего не делает. Ошибся в синтаксисе портянки, довольно давно не прогал MCS-51. Дело не в этом, результирующий код, перенес несколько строк в Init:

NAME    TM_Coder

STACK   SEGMENT IDATA
        RSEG    STACK
        DS              0x10                                    ; 
                
_VARS   SEGMENT DATA
                RSEG    _VARS
IV_Value:		DS		10   				; 
OV_Value:       DS		16          		; 
        
PROGRAM SEGMENT CODE
        CSEG    AT      0                   ;   
        RSEG    PROGRAM                     ;
        USING   0                           ;

Main:
;---------------------------------------------------------------------
        MOV             SP, #STACK-1     	; 
;---------------------------------------------------------------------
        ACALL   Init                    	; 
        ACALL   SendBuffer                  ; 
//      ACALL   Out                     	; 

Init:
		MOV		R0, #IV_Value				;
		MOV		@R0, #00000001b        		;
		INC		R0							;
		MOV		@R0, #00000010b        		;
		INC		R0							;
		MOV		@R0, #00000100b        		;
		INC		R0							;
		MOV		@R0, #00001000b        		;
		INC		R0							;
		MOV		@R0, #00010000b        		;
		INC		R0							;
		MOV		@R0, #00100000b        		;
		INC		R0							;
		MOV		@R0, #01000000b        		;
		INC		R0							;
		MOV		@R0, #10000000b        		;
		INC		R0							;
		MOV		@R0, #00010001b        		;
		INC		R0							;
		MOV		@R0, #10000100b        		;
											;
		MOV 	R0, #OV_Value				; ResetAddr
											;
        MOV 	R7, #IV_Value				;
        MOV 	A, #10						;
        RET
        
Out:
        RET
        
SendByte:
    MOV @R0, A
    INC R0
    RET

SendBuffer: ; Assume Count in A, Buf* in R7
    MOV R2, #8 ; i(R2) = 8
    MOV R3, A  ; Count(R3) = Count(A)
loop8:
    MOV R4, #0 ; Tmp(R5:R4) = 0
    MOV R5, #0
    MOV A, R3  ; j(R6) = Count
    MOV R6, A  
    ADD A, R7  ; R1 = Buf+Count
    MOV R1, A
loopC:
    DEC R1     ; A = Buf[j]
    MOV A, @R1 
    RRC A      ; C = A & 0x01 ; A >>= 1
    MOV @R1, A ; Buf[j] = A
    MOV A, R4  ; Tmp = (Tmp << 1) | C
    RLC A
    MOV R4, A
    MOV A, R5
    RLC A
    MOV R5, A
    DJNZ R6, loopC ; if(--j) goto loopC
    MOV A, R4      ; SendByte(Lo(Tmp))
    ACALL SendByte
    MOV A, R5      ; SendByte(Hi(Tmp)
    ACALL SendByte
    DJNZ R2, loop8 ; if(--i) goto loop8
    RET

END


После компиляции посмотрел в MAP файл:

Входной буфер по адресу 08H, выходной 12H.
Зашел в дебагер, проверил что делает Init:

Нормально все, проверил что делает SendBuffer:

Это так и задумывалось?
0
Это так и задумывалось?
А вот это уже с бумажкой проверять надо, ну или написать драйвер TM1638 (функции ResetAddr и SendByte) и подключить индикатор с СА. Если все правильно, то в первом разряде будет засвечен сегмент А, во втором В и так далее до восьмого с точкой (ну или как там замаплены сегменты на ножки драйвера), в девятом A и E, в десятом C и H.

Да, все правильно. Можешь сам проверить по свой таблице адресации.
Написал на ассемблере, в тетрадке и с первой попытки все работает. Что-то тут не так…
Кстати, на AVR тоже есть возможность вот так вот выдвинуть бит из одного регистра и вдвинуть в другой, авторы софтового USB этим пользовались. Так что при ручной оптимизации на асме должно получиться намного лучше полусотни команд от AVR-GCC (особенно учитывая, что он чудит и считает в 16 битах то, что надо считать в восьми).
0
Из за чего все это начиналось — накладные расходы на дебильную адресацию чипом TM1638 индикаторов с ОА. Теперь можно посчитать код от VGA:

Брекпойнты стоят в начале и конце функции преобразования. К ним привязана простейшая функция

FUNC void Counter (void) {
        unsigned long int val;
	val = states;
        printf("States (dec) = %lu\n", val);
}

Она просто выводит в окно отладчика кол-ко выполненных команд на момент вызова.
Итого, 1169 — 31 = 1138 команд потрачено на дебильную адресацию…
0
Во первых, не команд, а тактов. Довольно много для слоупочных 8051, 0.5-1мс.
Во вторых, вывод 16 байт в индикатор программным SPI займет не меньше, так что оверхед от перекодировки хотя и не станет незаметным, но составит процентов 50 от вывода на индикатор.
Насколько это существенно? Зависит от программы. По моим наблюдениям, 90% программ для МК проводят 90% времени в wait-циклах. Если 0.1% этого времени уйдет на вывод на индикатор — это будет 0.1% ресурса, спасенного от выбрасывания впустую.
0
Если 0.1% этого времени
На самом деле 1-2%, обновлять-то индикатор надо хотя бы раз 10 в секунду.
0
Насколько это существенно?
А зачем вообще об этом думать? Тот же MAX7219 делает это так, что о нем забываешь. Выплюнул в SPI и забыл. Зачем этот геморой с перекодировкой.
0
При этом он поддерживает только CC, с которым и на TM1638 проблем нет (на самом деле, вероятно можно и СА подключить — получив такую же адресацию, как на TM1638, но 8 разрядов против 10) и не поддерживает клавиатуру.
Что до геморроя с перекодировкой — ну извини, если функция на 10 строчек, которая пишется за пару минут для тебя «геморрой» — то это исключительно твоя неспособность как программиста.
+1
Кстати, раз уж у тебя есть кайло, то можешь скормить ему вот это и проверить, насколько отличается сгенерированный компилятором код.
#include <REG2051.H>
#include <stdint.h>

/* uncomment if stdint.h not found
typedef unsigned char uint8_t;
typedef signed char int8_t;
typedef unsigned int uint16_t;
*/

/* uncomment if Lo & Hi not defined
#define Lo(x) ((x) & 0xFF)
#define Hi(x) Lo((x) >> 8)
*/

uint8_t OutBuf[16];
uint8_t *BufPtr;

void ResetAddr(void) {
  BufPtr = OutBuf;
}

void SendByte(uint8_t Data) {
  *BufPtr++ = Data;
}

void SendBuffer(uint8_t Count, uint8_t *Buf) {
  for(uint8_t i = 0; i < 8; i++) {
    uint16_t Tmp = 0;
    for(int8_t j = Count - 1; j >= 0; j--) {
      Tmp = (Tmp << 1) | (Buf[j] & 0x01);
      Buf[j] >>= 1;
    }
    SendByte(Lo(Tmp));
    SendByte(Hi(Tmp));
  }
}

void main(void) {
  uint8_t Str[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x11, 0x84};
  ResetAddr();
  SendBuffer(10, Str);
}
0
P.S. В настройках проекта не забудь включить оптимизацию на максимум.
0
Код любой, AVR или MCS-51 без разницы.
0
На ассемблере 8051 можно оптимизировать и уложиться в 25 команд (32 байта, даже вдвое меньше, чем я прикидывал и втрое — чем на GCC-AVR):
SendBuffer: ; Assume Count in A, Buf* in R0
    MOV R2, #8 ; i(R2) = 8
    MOV R3, A  ; Count(R3) = Count(A)
  loop8:
    MOV R4, #0 ; Tmp(R5:R4) = 0
    MOV R5, #0
    MOV A, R3  ; j(R6) = Count
    MOV R6, A  
    ADD A, R0  ; R1 = Buf+Count
    MOV R1, A
  loopC:
    DEC R1     ; A = Buf[j]
    MOV A, @R1 
    RRC A      ; C = A & 0x01 ; A >>= 1
    MOV @R1, A ; Buf[j] = A
    MOV A, R4  ; Tmp = (Tmp << 1) | C
    RLC A
    MOV R4, A
    MOV A, R5
    RLC A
    MOV R5, A
    DJNZ R6, loopC ; if(--j) goto loopC
    MOV A, R4      ; SendByte(Lo(Tmp))
    ACALL SendByte
    MOV A, R5      ; SendByte(Hi(Tmp)
    ACALL SendByte
    DJNZ R2, loop8 ; if(--i) goto loop8
    RET

Хотя по прежнему не исключено, что я налажал. Проверять не на чем. Но опять же, не настолько налажал, чтобы результаты отличались более чем вдвое.
0
Не хотел код выкладывать, ну да ладно. Так понимаю спойлера тут нет, пойдет портянкой. Простой ассемблерный модуль для вывода на HD44780 из под AT89C2051 (слишком большой, не С, сказали каку вывожу, оставил только вывод):

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Подпрограммы работы с двухстрочным символьно-цифровым индикатором HD44780 ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;=== Таблица переходов на режимы ====================================;
locSelect:
;- 00 - Частота -----------------------------------------------------;
	MOV		DPTR, #TBL_Freq				; Адрес строки, 3 байта
	MOV		R0, #LenFreq				; Длина строки, 2 байта
	JBC		BA_Mark_f, locSM_00			; Выход со сбросом бита, 3 байта
;- 01 - Положительный импульс ---------------------------------------;
	MOV		DPTR, #TBL_Durt				; Адрес строки, 3 байта
	MOV		R0, #LenDurt				; Длина строки, 2 байта
	JBC		BA_Mark_n, locSM_00			; Выход со сбросом бита, 3 байта
;- 10 - Отрицательный импульс ---------------------------------------;
	MOV		DPTR, #TBL_Durt				; Адрес строки, 3 байта
	MOV		R0, #LenDurt				; Длина строки, 2 байта
	JBC		BA_Mark_u, locSM_00			; Выход со сбросом бита, 3 байта
;- 11 - Период ------------------------------------------------------;
	MOV		DPTR, #TBL_Perd				; Адрес, 3 байта
	MOV		R0, #LenPerd				; Длина строки, 2 байта
locSM_00:	
	ACALL	LCD_TxtToDisp				; Вывод строки режима
	JB		BA_Mark_n, locSM_01			; Флаг не сброшен, выход
	MOV		A, #ID_LCD_CH_Pol			; Положительный импульс
	ACALL	LCD_SendTetraByte			; Добавим символ импульса
locSM_01:
	JB		BA_Mark_u, locSM_02			; Флаг не сброшен, выход
	MOV		A, #ID_LCD_CH_Neg			; Отрицательный импульс
	ACALL	LCD_SendTetraByte			; Добавим символ импульса
locSM_02:
	ANL		BA_SaveMode, #NOT Mk_All	; Сброс флагов
	RET									;
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;  Подпрограмма регулируемой задержки					               		 ;; 
;;    A - время задержки					                           		 ;;
;;  Используемые регистры:                                          		 ;;
;;    A		                           					             		 ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
LCD_Waiting:
	CLR		PN_LCDEN					; Защелкнуть данные
	SETB	MK_TimeEnd					; Взвести флаг счетчика задержки
	MOV		AV_SysMode, A				; Загрузить время задержки
	JB		MK_TimeEnd, $				; Ждать окончания задержки
	RET									;
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;  Подпрограмма вывода на индикатор							    		 ;; 
;;    A - исходный байт						                           		 ;;
;;  Используемые регистры:                                          		 ;;
;;    A		                           					             		 ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
LCD_SendDataByte:
	MOV		PN_IntfPort, A				; 
	CLR		PN_LCDEN					; Защелкнуть данные
	MOV		A, #TM_InitWait3			; Время задержки, 215 мс
	ACALL	LCD_Waiting					;
	RET									;
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;  Подпрограмма разбития байта на полубайты и вывода на индикатор    		 ;; 
;;    A - исходный байт						                           		 ;;
;;  Используемые регистры:                                          		 ;;
;;    A, R6	                           					             		 ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
LCD_SendTetraByte:
	MOV		R6, A						; 
	RR		A							; Старшую половину
	RR		A							; в середину
	ORL		A, #ID_LCD_SET				; Сформировать полный байт
 	ACALL	LCD_SendDataByte			;
	MOV		A, R6						; 
	RL		A							; Младшую половину
	RL		A							; в середину
	ORL		A, #ID_LCD_SET				; Сформировать полный байт
 	ACALL	LCD_SendDataByte			;
	MOV		PN_IntfPort, #ID_LCD_BUSY		; 
	RET									;
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;  Подпрограмма вывода строки из Flash										 ;; 
;;    DPTR - строка, R1 - кол-во символов                           		 ;;
;;  Используемые регистры:                                          		 ;;
;;    A, B, R0, R7, DPTR	                                        		 ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
LCD_TxtToDisp:
	MOV		B, R0						;
locTD_00:
	MOV		A, R0						;	
	CLR		C							;
	SUBB	A, B						;
	MOVC	A, @A+DPTR					;
	ACALL	LCD_SendTetraByte			;
	DJNZ	B, locTD_00					;
	RET									;
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;  Подпрограмма вывода результата из буфера упакованных BCD цифр 			 ;; 
;;  Используемые регистры:                                          		 ;;
;;    A, R0, R1, R7, флаг пользователя F0                              		 ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
LCD_NumToDisp:
	CLR		F0							; сбросим флаг пользователя
	MOV		R0, #10						; выводим 10 десятичных разрядов
	MOV		R1, #AV_BCDBuffer+9			; неупакованные BCD цифры	
locSD_00:
;------ Проверка флагов MK_LimitValue и MK_LimitTicks ------------------------;
	MOV		A, BA_SysMark				;
	ANL		A, #((1 << TM_V) | (1 << TM_T))	; Если флаги не установлены, в аккумуляторе 0
	JZ		locSD_01					;
	CLR		C							;
	MOV		A, R0						; Счетчик цикла в аккумулятор
	SUBB	A, AV_MAN_CNT				; Сравниваем с кол-вом цифр после запятой
	JNZ		locSD_01					;
	MOV		A, #'.'						;
	ACALL	LCD_SendTetraByte			; Выводим десятичную точку
locSD_01:
	MOV		A, @R1						;
	DEC		R1							;
	JNZ		locSD_03					;
	JB		F0, locSD_04				;
	CJNE 	R0, #1, locSD_02			; выведем последний ноль	
	SJMP	locSD_04					;
locSD_02:
	MOV		A, #' '						; заменяем 0 на пробел
	SJMP	locSD_05					;
locSD_03:
	SETB	F0							; встретился не 0, ставим флаг
locSD_04:
	ADD		A, #'0'						; ASCII смещение
locSD_05:
	ACALL	LCD_SendTetraByte			;
	DJNZ	R0, locSD_00				;
	RET									;
;
_CONST	SEGMENT	CODE
		RSEG	_CONST
;
;=========== Символьные таблицы ==============================================;
;
TBL_TITLE:								; Название	
$IF Russian								;
	DB	'Частотомер FM-130'				; 
$ELSE
	DB	'Freqmeter FM-130'				;	
$ENDIF
LenTitl			EQU		$-TBL_TITLE 	; Длина строки
;
TBL_VERSION:							; Строка версии	и даты сборки
	DB	'v1.4 '							;	
	DB	__DATE__						;
LenVers			EQU		$-TBL_VERSION	; Длина строки
;
TBL_Mode:
$IF Russian								;
	DB	'Режим: '						; 
$ELSE
	DB	'Mode: '						; 
$ENDIF
LenMode			EQU		$-TBL_Mode	 	; Длина строки
;
TBL_Durt:
$IF Russian								;
	DB	'Импульс '						; 
$ELSE
	DB	'Pulse '						; 
$ENDIF
LenDurt			EQU		$-TBL_Durt	 	; Длина строки
;
TBL_Freq:
$IF Russian								;
	DB	'Частота'						; 
$ELSE
	DB	'Frequency'						; 
$ENDIF
LenFreq			EQU		$-TBL_Freq	 	; Длина строки
;
TBL_Perd:
$IF Russian								;
	DB	'Период'						; 
$ELSE
	DB	'Period'						; 
$ENDIF
LenPerd			EQU		$-TBL_Perd	 	; Длина строки
;
TBL_Herz:
$IF Russian								;
	DB	' Гц'							; 
$ELSE
	DB	' Hz'							; 
$ENDIF
LenHerz			EQU		$-TBL_Herz	 	; Длина строки
TBL_MSec:
$IF Russian								;
	DB	' мкс'							; 
$ELSE
	DB	' mcs'							; 
$ENDIF
LenMSec			EQU		$-TBL_MSec	 	; Длина строки
;-----------------------------------------------------------------------------;
$RESET (UNIT_2)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
END		
0
И?
0
На С вывод на индикатор ОА контроллером МТ1638 можно оформить одной строчкой. Не нужно этим гордиться, я показываю, как это накладно на ассемблере, а значит накладно для памяти, накладно для быстродействия. Говнокод не приветствуется…
0
Приведенная мной функция разворачивается в 25 команд на ассемблере AVR, что явно меньше предложенных тобой 320. Полагаю, на 8051 она даже компактнее будет.
Сложно найти соответствие в твоей простыне, так как мухи (подготовка данных) плотно смешаны с котлетами (выводом на дисплей).
Мой код предполагает, что вывод данных производится в буфер uint8_t Buf[10], где хранится строка для выведения на дисплей, и что одиночный байт выводится в дисплей командой SendByte (по очередному адресу).
Говнокод не приветствуется…
Извини, но смешивание мух с котлетами я именно говнокодом и называю. Хотя, возможно, в данном случае он позволяет сократить размер ценой читабельности (что осмысленно только в том случае, если в итоге сэкономленные байты не окажутся залиты FF-ками).
+2
как то уж совсем печальная простыня вышла=( Вы по-прежнему думаете, что выхлоп С был бы хуже=)))
Время бесценно, но Вам наверное это ни о чем не говорит. Не принимайте на свой счет. Это касается любых крайностей. Да, Вы бы все-таки прислушались бы к VGA, он по теме сейчас говорит=)
+2
Циклом можно только выгружать из подготовленного буфера, заносить в него так не всегда возможно.
Так работать можно только если надо помигать светодиодом, а лишнюю шлеш всяго лучше потратить на грамотное меню для пользователя.
0
заносить в него так не всегда возможно.
Это почему же?
а лишнюю шлеш всяго лучше потратить на грамотное меню для пользователя.
Иногда два дополнительных разряда важны.
+1
Не конечно можно вместо колеса отпилить круг от пенька и ездить вокруг дома.
0
Ну и TL;DR моей позиции в обсуждении выше: разница между индикатором с СС и индикатором с CA на TM1638 при правильном подходе составляет до полусотни команд (проиллюстрировано кодом, в том числе на ассемблере 8051). Не так уж мало, но вполне можно выделить даже на МК с 2кб флеша.
Сколько в это можно впихнуть дополнительных менюшек? Ну, не знаю. У Леонида Ивановича в его паяльной станции меню — 30 кило кода на С (и килобайт 5-8 — скомпиленного), 50 команд с таким меню — один пункт, быть может.
0
  • avatar
  • Vga
  • 20 июня 2016, 02:10
Как раз недавно приобрел себе модуль, для «поиграться». Оставил неоднозначное впечатление. Вроде и функционал хороший (регулировка яркости чего стоит), а реализовано действительно как то через жопу (автоинкремент есть, а использовать нормально нельзя). Выкладываю, что накодил (асм 8051): www.dropbox.com/s/wzchjh7dyr8msbf/TM1638.asm?dl=0
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.