winbond reset status

Posted by: 邱小新 at 上午10:08 in ,

記錄一下系統開機或是被 reset 時的一些旗標狀態。

旗標狀態

resetWTRFPOREWTWDCON判斷
power-on reset01001000000bPOR=1
external resetxxx0x0x0xx0bEWT=1*
POR=0#
watchdog reset1xx0x0x01x0bWTRF=1

註解

  1. x: 保留原值,不會變更。
  2. *: 系統如果有開啟 watchdog 功能,在發生 external reset 時,EWT 一定是 1。
  3. #: 一開機時 POR=1,此時把 POR 設成 0,下次發生 external reset 時,POR 一定是 0,以此來區分。適合沒有開啟 watchdog 功能的系統。

判斷順序

  1. 開啟 watchdog: WTRF=1(watchdog) -> EWT=1(external) -> POR=1(power-on)。
  2. 關閉 watchdog: POR=1(power-on) -> POR=0(external),watchdog 不用判斷。

reset 開機狀態

  1. Program Counter 強制跳回 0000h,也就是從頭開始執行。
  2. SFR 都會返回 reset value,datasheet 有一張表。
  3. stack point 會變成 07h。
  4. Vdd 小於 2V 時,RAM 資料會遺失;反之則會保存。

好用的 STATUS register

Posted by: 邱小新 at 上午10:47 in

以往在寫 uart 的 putchar 函數時,都是用下列方式來寫。在初始化時還要先把 TI 設成 1。但是如此會造成 ES 中斷一直發生。只適合沒有開啟 ES 中斷程序來用。

void putchar(char value) { while (!TI); TI = 0; SBUF = value; }

若是改用下列方式來寫,初始化時不用把 TI 設成 1,ES 中斷就不會一直發生。但是如此會被 block,等到傳送完才繼續其它工作,對效能來說不是很好。適合需要開啟 ES 中斷程序來用。

void putchar(char value) { SBUF = value; while (!TI); TI = 0; }

W77E352 有一個 register 叫 STATUS(0xC5),它的 SPTA0 位元為 uart 傳送忙碌指標,當 TI 為 1 時,自動清成 0。在 W79E632 也有 STATUS register,但是卻沒有 SPTA0 位元,不知是否可用?

void putchar(char value) { while (STATUS & SPTA0); SBUF = value; } void uart_isr(void) interrupt (4) { if (TI) TI = 0; }

ir remote control 加入 repeat/long key

Posted by: 邱小新 at 下午1:47 in

在 ir remote control NEC protocol 裏只是提到如何處理一個按鍵的處理。在實際應用上,常常會有遇到按著不放時,要重覆處理這個按鍵,或是按住某個按鍵多久才處理。現在我們就來討論這二個應用方法。

下面主要是用來做一個 buffer,免得 mcu 處理來不急,ir_put 都放在 interrupt 內使用,並沒有給一般常式使用;而 ir_get 只有 ir_process 會用到,但是 counter 跟 ir_put 共用到,所以加上中斷排除,免得發生同時修改的慘況。

#define IR_BUFFER_MAX 4 #define IR_BUFFER_MASK 3 static xdata unsigned char ir_buffer[IR_BUFFER_MAX]; static data unsigned char get_index, put_index, counter; static unsigned char ir_debound, ir_key, ir_repeat_key, ir_long_key; static unsigned char code ir_repeat[] = { IR_UP, IR_DOWN, IR_LEFT, IR_RIGHT }; #define IR_REPEAT_MAX (sizeof(ir_repeat)/sizeof(unsigned char)) static void ir_put(unsigned char value) { if (counter < IR_BUFFER_MAX) { ir_buffer[put_index] = value; counter++; put_index++; put_index &= IR_BUFFER_MASK; } } static unsigned char ir_get(void) { unsigned char value; value = ir_buffer[get_index]; ET0 = LOW; counter --; ET0 = HIGH; get_index++; get_index &= IR_BUFFER_MASK; return value; }

下面是在 interrupt 內會用到 ir_put 的片斷,只有二個地方;一個是輸入 repeat 訊號,一個是輸入按鍵訊號。

if (IS_IR_REPEAT(pulse)) { if (ir_pos == 32) ir_put(IR_REPEAT); return; } if(ir_pos >= 32) { ir_mode = 0; stop_timer(); if ((ir_code[0] | ir_code[1]) != 0xef) return; if ((ir_code[2] | ir_code[3]) != 0xff) return; if (ir_code[0] == 0x40) ir_put(ir_code[2]); }

下面是放在 main loop 裏的函數,ir_function 用來處理按鍵事件,而 ir_process 是用來計算處理 normal,repeat,long 按鍵的函數。

void ir_function(unsigned char key) { switch (key) { default: printf_tiny("ir recv: %x,%d,%d,%d\n\r", ir_key, get_index, put_index, counter); } } void ir_process(void) { unsigned char key; if (counter == 0) { return; } key = ir_get(); switch (key) { case IR_REPEAT: if (ir_debound == 0) { ir_debound++; if (ir_key == IR_LONG_KEY) { ir_long_key = IR_LONG_KEY; break; } for (key=0; key<IR_REPEAT_MAX; key++) { if (ir_key == ir_repeat[key]) break; } if (key < IR_REPEAT_MAX) ir_repeat_key = ir_key; } else if (ir_repeat_key != IR_NONE) { if (ir_debound > IR_REPEAT_COUNT) ir_function(ir_repeat_key); else ir_debound++; } else if (ir_long_key != IR_NONE) { if (ir_debound < IR_LONG_COUNT) { ir_debound++; } else if (ir_debound == IR_LONG_COUNT) { ir_function(ir_long_key); ir_debound++; } } break; default: ir_debound = 0; ir_long_key = IR_NONE; ir_repeat_key = IR_NONE; ir_key = key; if (key != IR_LONG_KEY)&#12288;ir_function(key); } }

I2C protocol 時間計算

Posted by: 邱小新 at 上午10:23 in

I2C protocol 原理及應用有提供了一個範例程式碼,裏面的 i2c_wait 在當時是用試誤法來測出需要幾個 nop 指令。最近正好在做新的案子,使用不同的 crystal,就想用時脈來計算出真正需要多少個 nop 指令。

以下推導過程,使用 22.1184MHz 的振盪器,I²C匯流排速度為標準模式(100 Kbit/s)。

  1. 1 clock = 1/22.1184 us
  2. 1 machine cycle = 12 clock = 12/22.1184 us
  3. 1 nop = 1 machine cycle = 12/22.1184 us = 542.534722 ns
  4. 100 kbit/s = 1bit per 1/100000s = 10 us
  5. 1 bit cycle = 10/0.542 = 18.432 machine cycle
  6. 2 * i2c_wait = 1 bit cycle ==> i2c_wait = 9.2 machine cycle = 9 nop

實際上使用的 nop 有 8 個,可以正常 read/write 256 bytes eeprom。但是由於還會呼叫 lcall,ret 等指令,理論上應該少更多才對。不知是 eeprom 不穩,無法達到 100Kbit/s,還是其它原因?

以下推導過程,使用 40MHz 的振盪器,I²C匯流排速度為標準模式(100 Kbit/s)。

  1. 1 clock = 1/40 us
  2. 1 machine cycle = 12 clock = 12/40 us
  3. 1 nop = 1 machine cycle = 12/40 us = 0.3 us
  4. 100 kbit/s = 1bit per 1/100000s = 10 us
  5. 1 bit cycle = 10/0.3 = 33.33 machine cycle
  6. 2 * i2c_wait = 1 bit cycle ==> i2c_wait = 16.67 machine cycle = 16 nop

實際上使用的 nop 有 10 個,只有測試讀寫 1 byte。由於還會呼叫 lcall,ret 等指令,所以少了 6 個 nop 是可以理解的。但是實際上測試需要做讀寫大量的資料,不然像上例的 eeprom,單獨讀寫一個沒問題,大量讀寫時,卻出現很多錯誤。