ATtiny13, 實驗五個按鍵的特製紅外線遙控器

https://wp.me/ph3BR-2aJ

 

沒有最好, 只有更好

第一次做好PLC紅外線遙控器, 八個按鍵的, 可用了, 發現它比較耗電, 使用上要先打開遙控器的電源, 然後再用遙控按鍵, 優點, 抄來改一下就可用, 缺點, ARDUINO UNO 體積大也雖然會自動延時一陣子斷電, 但總體還是耗電, 那是IR REMOTE CONTROL 初版.

後來學習了 AVR MCU 的睡眠模式設定, 實驗過後, 確認 MCU 睡死以後待機電流 0.6uA. 打個比方, 若一個 AAA 電池大約有 150 mAh 的電量, 如果按照 0.6uA = 0.0006mA 耗電流計算, 睡著了不再叫醒它上班, 等28年後還有電力. 待機時間計算方法大概是這樣, 150 mAh / 0.0006mA = 250000 h (小時) = 10416 天 = 28.5 年. (不過電池會漏水爆漿, 這裡不表了)

LeTV 電視, 經過賈躍亭惡搞後, 樂視網跟樂視電視機已呈絕唱, 那個鳥電視每次啟動都依然給你看喊口號廣告然後努力加載, 僅此而已, 啥B都沒得看, 鳥公司在倒閉以前也不把 FIRMWARE 更新去掉那沒鳥用的 “努力加載", 努力是沒用的.

Onkyo AV 擴大機

MTV7000D 電視盒

上述三個機器, 常用的幾個遙控按鍵的訊號格式, 源碼如下, 應該夠不言自明 (老外說的 self-explanatory), 白話說法, 你懂的….

IR_command.h


/*
* xiaolaba, 2016-JAN-12
* IR signal raw code captured, by uses original IR remote control,
* raw code only, MSB first,
* original signal, NEC IR format, leader code 9ms, 16 units, unit time 9/16 = 526.5us,
* 16 bit address + 8 bit command + 8 bit /command, total 32 bit
* REF : https://www.sbprojects.net/knowledge/ir/nec.php
* LeTV, model X50 air, remote control model, N/A
* Onkyo AV reciver, model TX-NR609, remote control model RC-803M
* Magic TV, model MTV7000D, DTMB TV receiver, remote control model N/A
*/

#pragma once

#define MagicTV_CH+_button 0x1B546897
#define MagicTV_CH-_button 0x1B54E817

#define LeTV_Power_button 0x32A650AF
#define LeTV_SOURCE_button 0x32A630CF
#define LeTV_VOL_UP_button 0x32A6A857
#define LeTV_VOL_DOWN_button 0x32A638C7

#define LeTV_UP 0x32A6D02F
#define LeTV_DOWN_button 0x32A6708F
#define LeTV_OK_button 0x32A6B04F

#define Onkyo_VOL_UP_button 0x4BB640BF
#define Onkyo_VOL_DOWN_button 0x4BB6C03F
#define Onkyo_Apple_TV_button 0x4BB6B04F // Onkyo Game button
#define Onkyo_MagicTV_button 0x4B3631CE // Onkyo BD/DVD button

 

這些訊號的數據是以前抓的, 按照第一篇的方式就有了.

有了源碼, 有了可用的遙控器的經驗, 有了 MCU 省電的實驗結果, 移植一下應該不會難, 差不多兩年後, 放假就玩一玩. 不過, ARDUINO UNO 有 “無限大容量", 但是 ATtiny13, 比對只有個小腦袋, 不大 1K 對 32K 的容量, 裝不下原來的原碼, 需要瘦身才可. 瘦身計畫也很成功, 例如,

不要 SERIAL OUTPUT

刪除字串

差不多把原碼精簡剩下2K, 但是還是離開 1K 超過了 100%

再來刪除 LOOP, PIN MODE, digitwrite 等等, 畢竟 ARDUINO IDE 是不計成本產出臃腫源碼的, 目的是方便普通玩家, 要深入要更上層樓, 只能用 C, 關鍵時刻, 可能還需要動到 AVR INLINE ASSEMBLER, 後話, 純粹為了玩一下.

遇到的問題, 電池電壓下降 RC OSC 會變慢, LeTV 容錯能力很好, 37KHZ 照樣遙控無誤, 反觀Onkyo / MTV7000D, 對時間碼的要求很嚴格, 9ms/16 偏移 2% 就沒反應了. 所以實驗了一個 10MHz 的 XTAL 震盪器, 如下圖, C1 改用220PF, C2 改用 30PF, 隨便試用得到得結果, 沒想到很容易做成也很好用了, C3 不要, Vc 那點接 ATtiny13 CLKI.

10MHz 震盪器搭起棚架實驗筆記

neo_10MHz Pierce oscillator uses a single NPN transistor – xiaolaba

 

第二個問題, CPU 頻率設定等於 XTAL 頻率, 不除以 8 (CDIV8=1), CPU 速度高了, 計時精度不變, 但是解析度高了, 9ms / 16 還是一樣誤差, 用 #define factor ( ( ( (F_CPU/1000) *9 ) /16 ) /4 ), 編譯結果時序差很多, 理論上, F_CPU = 10000000, 單一CPU指令耗時 0.1uS, 9mS/16, 每份額佔據 562.5uS, 需要 562.5 / 0.1 = 5625 CPU 週期, 延時計數要 5625 / 4 = 1046次…….還是有些沒有考慮到的誤差或錯誤編碼存在, 只能手動微調延時, 延時計數設定成1435, offset 37%, 最後順利遙控了三個機器, 只用一個 ATtiny13….8個腳, Vcc / GND / CLKI / RESET 去掉4個, 只剩4個, 做不出來5個鍵的遙控器…..?!

Tiny13 IR Remote control 搭起棚架實驗筆記

neo_Tiny13_38KHZ_schematic_PCB_xiaolaba

 

……還沒完….

廣告

ATtiny13, High Voltage serial programmer for chip re-enable

https://wp.me/ph3BR-2az

有時天氣乾燥, 很容易且無緣無故IC就掛了, 有的是鎖死, 有的乾脆靜電打壞了. 鎖死的話用高壓模式重新燒綠就好了

抄來的改一下, 增加了幾個功能, 讀 LOCK / SIGNATURE

complete project files, https://github.com/xiaolaba/Tiny13_HV_resetter

用 Arduino Nano, 方便接線


//REF :https://sites.google.com/site/wayneholder/attiny-fuse-reset
//2018-03-06, xiaolaba
//add read full signature 3 bytes, some device has wrong signature byte-x but functional properly
//add read cal, some device always responds 0xff, 0xff !? but functional properly
//add read lock, tiny13 only has two lock bits documented or saying programmable
//add chip erase

// AVR High-voltage Serial Fuse Reprogrammer
// Adapted from code and design by Paul Willoughby 03/20/2010
// http://www.rickety.us/2010/03/arduino-avr-high-voltage-serial-programmer/
//
// Fuse Calc:
// http://www.engbedded.com/fusecalc/

#define VCC 2 // Target VCC
#define SDO 3 // Target Data Output
#define SII 4 // Target Instruction Input
#define SDI 5 // Target Data Input
#define SCI 6 // Target Clock Input
#define RST 13 // Output to level shifter for !RESET from transistor

#define HFUSE 0x747C
#define LFUSE 0x646C
#define EFUSE 0x666E

// Define ATTiny series signatures
#define ATTINY13 0x1E9007 // L: 0x6A, H: 0xFF 8 pin
#define ATTINY24 0x1E910B // L: 0x62, H: 0xDF, E: 0xFF 14 pin
#define ATTINY25 0x1E9108 // L: 0x62, H: 0xDF, E: 0xFF 8 pin
#define ATTINY44 0x1E9207 // L: 0x62, H: 0xDF, E: 0xFFF 14 pin
#define ATTINY45 0x1E9206 // L: 0x62, H: 0xDF, E: 0xFF 8 pin
#define ATTINY84 0x1E930C // L: 0x62, H: 0xDF, E: 0xFFF 14 pin
#define ATTINY85 0x1E930B // L: 0x62, H: 0xDF, E: 0xFF 8 pin

void setup() {
pinMode(VCC, OUTPUT);
pinMode(RST, OUTPUT);
pinMode(SDI, OUTPUT);
pinMode(SII, OUTPUT);
pinMode(SCI, OUTPUT);
pinMode(SDO, OUTPUT); // Configured as input when in programming mode
digitalWrite(RST, HIGH); // Level shifter is inverting, this shuts off 12V
Serial.begin(19200);
Serial.println("AVR High-voltage Serial Fuse Reprogrammer, send a char then will see response\r\n");
}

void loop() {
if (Serial.available() > 0) {
Serial.read();
pinMode(SDO, OUTPUT); // Set SDO to output
digitalWrite(SDI, LOW);
digitalWrite(SII, LOW);
digitalWrite(SDO, LOW);
digitalWrite(RST, HIGH); // 12v Off
digitalWrite(VCC, HIGH); // Vcc On
delayMicroseconds(60); // wait 20-60us
digitalWrite(RST, LOW); // 12v On
delayMicroseconds(10); // keep the state for at least 10us
// should be entered HV Programming mode
pinMode(SDO, INPUT); // Set SDO to input, relase
delayMicroseconds(300); // wait for 300us before giving instruction to SDI/SII

unsigned long sig = readSignature();
Serial.print("Signature is: 0x");
Serial.print(sig, HEX);
if (sig == ATTINY13) {
Serial.println(" = ATtiny13/13V, ATtiny13A");
}

ChipErase ();
Serial.println("Chip Erased");

unsigned long cal = readCalibration();
Serial.print("Calibration bytes: 0x");
Serial.print(cal>>8, HEX);
Serial.print(" 0x");
Serial.println(cal & 0xff, HEX);

Serial.print("Lock: "); Serial.println(readLock(), HEX);

Serial.print("before: "); readFuses();

if (sig == ATTINY13) {
writeFuse(LFUSE, 0x6A);
writeFuse(HFUSE, 0xFF);
} else if (sig == ATTINY24 || sig == ATTINY44 || sig == ATTINY84 ||
sig == ATTINY25 || sig == ATTINY45 || sig == ATTINY85) {
writeFuse(LFUSE, 0x62);
writeFuse(HFUSE, 0xDF);
writeFuse(EFUSE, 0xFF);
}

Serial.print("after: "); readFuses();

Serial.println("Power off sequency: set SCI to '0', set RESET to '1', turn Vcc power off\r\n");
digitalWrite(SCI, LOW);
digitalWrite(VCC, LOW); // Vcc Off
digitalWrite(RST, HIGH); // 12v Off
}
}

byte shiftOut (byte val1, byte val2) {
int inBits = 0;
//Wait until SDO goes high
while (!digitalRead(SDO))
;
unsigned int dout = (unsigned int) val1 << 2;
unsigned int iout = (unsigned int) val2 << 2;
for (int ii = 10; ii >= 0; ii--) {
digitalWrite(SDI, !!(dout & (1 << ii)));
digitalWrite(SII, !!(iout & (1 << ii)));
inBits <<= 1;
inBits |= digitalRead(SDO);
digitalWrite(SCI, HIGH);
digitalWrite(SCI, LOW);
}
return inBits >> 2;
}

void writeFuse (unsigned int fuse, byte val) {
shiftOut(0x40, 0x4C);
shiftOut( val, 0x2C);
shiftOut(0x00, (byte) (fuse >> 8));
shiftOut(0x00, (byte) fuse);
}

void readFuses () {
byte val;
shiftOut(0x04, 0x4C); // LFuse
shiftOut(0x00, 0x68);
val = shiftOut(0x00, 0x6C);
Serial.print("LFuse = 0x");
Serial.print(val, HEX);
shiftOut(0x04, 0x4C); // HFuse
shiftOut(0x00, 0x7A);
val = shiftOut(0x00, 0x7E);
Serial.print(", HFuse = 0x");
Serial.print(val, HEX);
shiftOut(0x04, 0x4C); // EFuse
shiftOut(0x00, 0x6A);
val = shiftOut(0x00, 0x6E);
Serial.print(", EFuse = 0x");
Serial.println(val, HEX);
}

unsigned long readSignature () { //3 bytes, signature
unsigned long sig = 0;
byte val;
for (int ii = 0; ii < 3; ii++) {
shiftOut(0x08, 0x4C);
shiftOut( ii, 0x0C);
shiftOut(0x00, 0x68);
val = shiftOut(0x00, 0x6C);
sig = (sig << 8) + val;
}
return sig;
}

unsigned int readCalibration () {
unsigned int cal = 0;
byte val;
for (int ii = 0; ii < 2; ii++) {
shiftOut(0b00001000, 0b01001100); //0x80, 0x4C
shiftOut( ii, 0x0C);
shiftOut(0x00, 0x78);
val = shiftOut(0x00, 0x7C);
cal = (cal << 8) + val;
}
return cal;
}

unsigned int readLock () {
unsigned int lock = 0;
byte val;
// for (int ii = 0; ii < 1; ii++) {
shiftOut(0x04, 0x4C);
// shiftOut( ii, 0x0C);
shiftOut(0x00, 0x78);
val = shiftOut(0x00, 0x7C);
lock = (lock << 8) + val;
// }
return lock;
}

void ChipErase () {
shiftOut(0x80, 0x4C);
shiftOut(0x00, 0x64);
shiftOut(0x00, 0x6C);
}

 

完整的檔案和HEX檔

 

Arduino, ATtiny13A, pure C programming

https://wp.me/ph3BR-2ah

 

Arduino IDE 1.8.5

install MicroCore support, https://mcudude.github.io/MicroCore/package_MCUdude_MicroCore_index.json

install MiniCore support, https://mcudude.github.io/MiniCore/package_MCUdude_MiniCore_index.json

Device ATtiny13A, 選 ATtiny13

燒錄器 USBTiny, 選 USBTinyISP

用WDT的話, 會出現 vector error 8, 需要禁止 MILLIS(), 位置,

C:\Users\xxxx\AppData\Local\Arduino15\packages\MicroCore\hardware\avr\1.0.3\cores\microcore\core_settings.h

內容, //#define ENABLE_MILLIS

 

根據 Microchip (併購了Atmel), Attiny13A 對比 ATtiny13, 兩個產品都是一樣的 Signature, 所以讀出來的都一樣, sig 1E 90 07,

13A_13_same_signature

13_signature_read

 

calibration 分別有兩組數字, 一個9.6MHZ, 一個4.8MHZ, (0x4c, 0x48) 但是沒有說明是5V 還是 3V3 使用, 通常都不夠準, 除非供電電壓穩定溫度也不變, 不要用電池. 內定 HFUSE=0xff, LFUSE=0x6A (9.6MHZ/8 = 1.2MHZ CPU CLOCK, 每個指令耗時大約 1/1.2 = 0.833..uS)

0x4c = 76

0x48 = 72

看看DATASHEET, 實在離9.6MHZ偏離很多, 所以 SOFTWARE 的UART一定亂碼, 那需要試試看用XTAL, 確定 UART 軟體可信度再來 TUNE OSC. 不用自己算 http://www.engbedded.com/fusecalc/, LFUSE=0x68, HFUSE=0xff, 就可以用外置 CLOCK, 但是只有一條PIN#2輸入, 意思就是不能用 XTAL, 必須用 OSCILLATOR.

13A_OSCCAL

 

ATtiny13, pin#2, CLKI, external clock driven pin

13_CLKI13_CLKI_pin2