Helloworld на магнитоле

в 4:54, , рубрики: DIY, diy или сделай сам, embedded, lcd, microchip, pic16, микроконтроллер, Программинг микроконтроллеров, Электроника для начинающих, метки: , , , , ,

Неожиданный отпуск не повод расслабляться, и поэтому надо в срочном порядке сломать что-нибудь нужное и сделать что-нибудь ненужное.
Helloworld на магнитоле
Под катом немного электроники и совсем чуть-чуть ассемблера, специально для любителей ненормального железячного программирования.

«Hello, world!» — программа, результатом работы которой является вывод на экран или иное устройство фразы «Hello, world!» (с) Wiki
А устройством у нас будет передняя панель от древней, как копыто мамонта, автомагнитолы Китаенвуд, которая (не магнитола, а панель) бережно хранилась в закромах Родины как раз для
такого случая.

Helloworld на магнитоле

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

Helloworld на магнитоле

С другой стороны платы обнаружен драйвер неизвестного устройства LCD-дисплея с возможностью опроса клавиатуры LC75883.

Helloworld на магнитоле

Управляется драйвер с помощью SPI-подобного интерфейса CCB фирмы Sanyo, подробно описанного в даташите на микросхему, что позволяет легко решить поставленную задачу.
Интерфейс имеет 4 линии:
CL — тактирование;
CE — разрешение работы микросхемы;
DI — запись адреса/данных;
DO — чтение данных.
Также имеется вход сброса RES, который должен быть в логической 1 во время работы устройства.
Запись данных в драйвер происходит следующим образом:
Helloworld на магнитоле
Традиционно, для записи бита его значение устанавливается на линии DI и вырабатывается тактовый импульс по линии CL. В CCB данные идут начиная с младшего значащего бита LSB. При низком уровне CE происходит запись адреса (для режима записи данных 0x42). Далее устанавливается логическая 1 по линии CE и записываются данные. Запись данных происходит тремя блоками по 72 бита (9 байт) каждый. Каждый блок оканчивается битами DD, определяющими номер блока и принимающими значение 00, 01 или 10. Перед передачей каждого блока данных требуется запись адреса.
Блоки данных содержат:

  • данные для отображения на дисплее;
  • биты, управляющие спящим режимом;
  • биты определения режима работы (сканирование клавиатуры/сегменты LCD и сегменты LCD/обычные выводы) для некоторых выводов;
  • биты управления состоянием дисплея (включен/выключен);
  • биты определения формы импульсов для LCD.

Чтение данных происходит аналогично:
Helloworld на магнитоле
Для чтения надо записать в драйвер адрес 0x43 и считать 32 бита (4 байта) данных по линии DO. Считываются биты состояния кнопок и бит спящего режима.
Более подробные подробности интересующиеся лица могут посмотреть в даташите на микросхему.
Как мы видим, протокол обмена известен, но очень не хватает данных о соответствии элементов LCD дисплея соответствующим выводам драйвера. Определять их будем экспериментально, брутфорсом :)
Неоценимую помощь в определении некоторых параметров конфигурации драйвера (режим работы выводов, отвечающих за сканирование клавиатуры, управление сегментами LCD или являющимися обычными выводами) может дать сервис-мануал на магнитолу, который оказался в интернете.

Helloworld на магнитоле

Но я немного стормозил и определил эту схему обычной прозвонкой, и лишь потом сверился с сервис-мануалом. Зато из мануала стало ясно, что для подсветки (лампы накаливания где-то под LCD) и светодиодов требуется питание +12В.
Таким образом для запуска панельки потребуются: RES, CL, CE, DI, DO(опционально), +5В и DGND (цифровой общий) для логики, +12В и GND (общий) для иллюминации.
Испытывать панельку будем, подключив ее к [ардуино] самопальному PICKit-подобному изделию студенческих времен, именуемом в дальнейшем «девайс».

Helloworld на магнитоле

Девайс имеет в своем составе: PIC16F877A в DIP-панельке. Панелька позволяет использовать практически любой PIC в 40-выводном корпусе, чего за глаза хватит для любого прототипирования и отладки программы в железе. Так же есть 6 кнопок + 1 резет, 4 7-сегментных LED (точнее 2 14-сегментных), MAX220 для связи с компом (и даже программирования радиостанций), панелька под память I2C. Наплатная периферия подключается либо джамперами к фиксированным лапам контроллера, либо проволочными перемычками через контактные площадки к любым выводам. Кварцы сменные для изменения тактовой частоты. Внешние соединения также втыкаются в разъемчики от советских телевизоров :). Питание +5V можно брать прямо с платы. Программируется микроконтроллер с помощью разъема ICSP, куда подключается самодельный программатор ICD2.
Программа для управления панелькой написана на pure MPASM с ассемблерными вставками.
Интерфейс CCB реализуется программно. Прежде всего по даташиту на драйвер определяем необходимые тайминги. Производителем заявлено, что необходимые длительности импульсов, нарастаний и спадов фронтов, промежутки между импульсами должны составлять минимум 160 нс. В сумме на каждый бит потребуется ~0.5 мкс. Время исполнения инструкции микроконтроллером при кварце 4 МГц составит 1 мкс. Таким образом можно реализовывать обмен данными с драйвером в одном цикле, без задержек по таймеру и т.п. Но на всякий случай, для обеспечения стабильности работы, между инструкциями выдачи сигналов в порты вывода, будем добавлять пару-тройку nop (пустых инструкций).
В основном цикле программы вызываются задача вывода данных на LED индикаторы и сканирования кнопок на девайсе, задачи отправки и получения данных на драйвер LCD. Выполнение задачи происходит, если установлен флаг ее разрешения. Флаги устанавливаются либо по таймеру (скан кнопок и динамическая индикация), либо по событию (запись/чтение данных на драйвер). Таким образом, имеется некое подобие RTOS, что с положительной стороны зарекомендовало себя в проектах сложнее чем «помигать светодиодом».

MAINLOOP nop
	btfsc	LEDE		;Check LED out & keyscan
	call	LEDIO		
	btfsc	WDI
	call	LCDWR		;Write Subprogram
	btfsc	RDO
	call	LCDRD		;Read Subprogram
	goto	MAINLOOP

Прерывание по таймеру происходит с частотой 200 Гц, с этой частотой устанавливается флаг задачи динамической индикации. Т.к. имеется 4 знакоместа, то вывод кажой цифры производится с частотой 50 Гц, что комфортно для глаз оператора :) Опрос кнопок происходит также в задаче динамической индикации, но в 50 раз реже. Для устранения явления «дребезга контактов», кнопка считается нажатой, если два цикла опроса подряд соответствующий бит порта был в значении «1». Т.о. опрос кнопок происходит с частотой 2 Гц.
Непосредственно обработка прерывания:

;[Timer0]:
	btfss	INTCON,	TMR0IF
	goto	ENDISR
	bcf		INTCON,	TMR0IF
	movf	T0SET,	W
	addwf	TMR0,	F	;Correct timer
	;Function:
	bsf		LEDE	;Enable LED output (200 Hz)
	decfsz	IOCNT,	F	;IOCNT--
	goto	$+4
	movlw	CNTIO
	movwf	IOCNT
	bsf		KSE		;Enable keyscan (2 Hz)
;Конец обработки где-то здесь

Схемотехнически матрица кнопок строками (допустим) на девайсе подключается к общим анодам 7-сегментых индикаторов, поэтому импульсы сканирования выдаются на соответствующую пару кнопок, когда зажжен один из индикаторов. При нажатии кнопки (или двух сразу) на соответствующих столбцах матрицы, и, следовательно, на портах, сконфигурированных как входы от кнопок, появляются логические «1». Диоды используются, чтобы одновременно можно было нажимать хоть все 6 кнопок, и это не нарушало бы динамическую индикацию. Такое техническое решение позволяет опрашивать любое число нажатых кнопок без ошибок интерпретации результата.

Helloworld на магнитоле

Динамическая индикация (для одной цифры):

LEDIO	nop
	bcf		LEDE
	movf	LEDCNT,	W
	addwf	PCL,	F
	nop
	goto	LEDIO4
	goto	LEDIO3
	goto	LEDIO2
	goto	LEDIO1
;LED1
LEDIO1	nop
	bcf		LED4
	movf	ADDRED,	W
	andlw	b'11110000'
	movwf	TEMP
	swapf	TEMP,	W
	call	HEXTOLCD
	bcf		PCLATH,	1		;Prev 256 byte block
	movwf	LEDS
	bsf		LED1
	btfsc	KSE			;Check KeyScanEnabled
	call	KEYSCAN12
	goto	LEDEND
;
; Здесь почти то же самое, но для других цифр
;
LEDEND	nop
	decfsz	LEDCNT,	F	;LEDCNT--
	goto	$+3			;If not Zero
	movlw	CNTLED		
	movwf	LEDCNT
	return

Скан кнопок и полезная функция по нажатию (для одной пары):

KEYSCAN12	nop
	;bcf	KSE
	nop
	nop
	nop
;Scan SB1
SB1SCAN	nop
	btfss	SB135
	goto	SB1N
	btfsc	SB1o
	bsf		SB1p
	btfss	SB1o
	bsf		SB1o
	goto	SB2SCAN
SB1N	 	bcf	SB1o	
;Scan SB2
SB2SCAN	nop
	btfss	SB246
	goto	SB2N
	btfsc	SB2o
	bsf		SB2p
	btfss	SB2o
	bsf		SB2o
	goto	KSCEND12
SB2N	 	bcf	SB2o
KSCEND12 nop
;Set WDI&RDO
	btfsc	SB1p
	bcf		SB1o
	btfsc	SB2p
	bcf		SB2o
	btfsc	SB1p
	bsf		WDI
	btfsc	SB2p
	bsf		RDO
	bcf		SB1p
	bcf		SB2p
	return

На LED индикаторы выводятся в шестнадцатиричном виде:
1. адрес ячейки памяти, содержащий данные для передачи драйверу LCD;
2. содержимое этой ячейки.
Преобразование шестнадцатиричного числа в формат, пригодный для отображения на 7-сегментном индикаторе производится путем выделения старших (или младших) 4 бит и чтении из таблицы байта состояния сегментов индикатора.
6 кнопок позволяют осуществить следующее:
1. устанваливать флаг разрешения записи данных в драйвер LCD;
2. устанваливать флаг разрешения чтения данных из драйвера LCD;
3,4. изменять (инкремент и декремент) адрес показываемой на LED ячейки памяти
5,6. Изменять содержимое текущей ячейки памяти путем сдвига содержимого ячейки влево и установки младшего бита в «1» или «0».
При инициализации, все биты данных, передаваемые на драйвер (кроме битов управления), устанавливаются в значение «1». Биты управления устанавливаются в соответствии с даташитом так, чтобы драйвер начал функционировать в обычном режиме.
Запись данных в драйвер:

LCDWR	nop	;Write
	movlw	DATAWR1
	movwf	FSR		;FSR = Addr of data
;Write address
LCDWWA	nop
	movlw	CNT8
	movwf	RWBCOUNT	;RWBCOUNT = CNT8
	movf	ADDRWRITE,	W	;Addr write
	movwf	RWBUF
;Write address byte
LCDWWAB	nop
;CE	
	nop	
	btfsc	CE
	bcf		CE				;CE = 0
;CL	
	nop	
	bcf		CL				;CL = 0
;DI
	nop
	btfss	RWBUF,	7
	bcf		DI				;DI = 0
	btfsc	RWBUF,	7
	bsf		DI				;DI = 1
;CL	
	nop
	nop
	nop
	bsf		CL				;CL = 1
;Shift
	rlf		RWBUF,	F		;Shift buffer
	decfsz	RWBCOUNT, F	;Counter--
	goto	LCDWWAB		;If not Zero then next bit
;Write data
LCDWWD	nop
	movlw	CNT8
	movwf	RWBCOUNT	;RWBCOUNT = CNT8
	movf	INDF,	W
	movwf	RWBUF
;Write data byte
LCDWWDB	nop
;CL	
	nop	
	bcf		CL				;CL = 0
;CE	
	nop	
	btfss	CE
	bsf		CE				;CE = 1
;DI
	nop
	btfss	RWBUF,	7
	bcf		DI				;DI = 0
	btfsc	RWBUF,	7
	bsf		DI				;DI = 1
;CL	
	nop
	nop
	nop
	bsf		CL				;CL = 1
	rlf		RWBUF,	F		;Shift buffer
	decfsz	RWBCOUNT, F	;Counter--
	goto	LCDWWDB		;If not Zero then next bit
	incf	FSR,	F	;FSR++
	movlw	DATAWR2
	xorwf	FSR,	W
	btfsc	STATUS,	Z
	goto	LCDWWA		;Write address
	movlw	DATAWR3
	xorwf	FSR,	W
	btfsc	STATUS,	Z
	goto	LCDWWA		;Write address
	movlw	DATAWR3 + 9	;Test End on Write
	xorwf	FSR,	W
	btfss	STATUS,	Z
	goto	LCDWWD		;Write data
LCDWREND nop
	bcf		CL
	nop	
	bcf		CE
	nop
	bcf		DI
	nop
	bcf		WDI
	return

Перед заливкой в железо проверяем работу в протеусе и сравниваем полученные осциллограммы с желаемыми (в даташите).
CL — желтый;
CE — синий;
DI — красный.
Запись адреса и 1 байта данных:
Helloworld на магнитоле

Один цикл записи (9 байт):
Helloworld на магнитоле

Вся процедура записи данных (27 байт):
Helloworld на магнитоле

Чтение из драйвера происходит практически аналогично, поэтому этот момент оставим на рассмотрение читателя.
Заливаем прошивку в девайс, подсоединяем пациента и производим первое включение:

Helloworld на магнитоле

Жмем кнопку записи в драйвер, все элементы LCD и светодиоды зажглись. Работает, зараза :)

Helloworld на магнитоле

Обнулим случайным образом биты в некоторых ячейках памяти, соответствующие (но пока неизвестно какие) сегменты погасли.

Helloworld на магнитоле

Теперь последовательно, как марксисты, будем обнулять по биту в каждой ячейке, записывать результат в драйвер, записывать результат на бумажку. 27 байт * 8 бит * 2 нажатия = 432 нажатия. Через ~40 минут трудов (+ время для худжественных работ по изображению дисплея на бумаге) имеем искомую таблицу соответствия:

Helloworld на магнитоле
Helloworld на магнитоле

Теперь можно составить и долгожданную фразу «Hello, world!». Посокольку годных знакомест всего 8, ограничимся фразой покороче «HI WORLd». Закодируем в двоичном виде, кнопками (мы, комсомольцы, не можем без трудностей) забьем биты в ячейки памяти, и…

Helloworld на магнитоле

Для интересующихся, полный код прошивки здесь.
P.S. Между прочим, вмешавшись в обмен между самой магнитолой и панелькой и посылая свои команды, можно перехватывать нажатия кнопок и выводить на дисплей что-нибудь интересное. Но это никоим образом не призыв ломать работающее оборудование. Действуйте на свой страх и риск, да поможет вам св. Януарий :)

Автор: Int_13h

* - обязательные к заполнению поля