OsalTimerScheduler 採 round-robin 設計, 並沒有偏重於哪一個 timerId. 它以 TIMER ISR 為主, 然而 Timer Callback 函數並不是由 ISR 呼叫, 而是由 Main 主程序 呼叫; Timer ISR 只是做 ++ -- 之類的. 套用這樣的架構, 程式設計師可以專注於 Timer Callback 函數的設計. 以本案例而言, 專注於 ProbeTimer() / CalibTimer() / InitWeightTimer() / SetWeightLevel() 等等Callback 函數的實做. 這樣的設計已經有一點兒接近 `微作業系統` (Tiny OS) 的概念了. 本篇文章介紹的 code 是經過驗證的.
WeightStack 控制器 震盪出至少 1KHZ 倍頻穩定的 GPIO High / Low
這裡所介紹的 `OSALTimerScheduler` 是 MCU 以 TIMER 為基礎的簡單 OS/Scheduler; 因此在應用 OSALTimerScheduler 之前必須震盪出 至少 1KHZ 倍頻穩定的 GPIO High / Low, 這非常重要. 這裡我已 AT89LP4052 TIMER0 2倍頻為例來說明. TIMER0 ISR 以 2KHZ __interrupt 穩定地被呼叫. 以下述 code 為例, 示波器上應可見到 P3_7 以 2KHZ 震盪. ISR 內 POLLING 的 function 我們先不管它.
... #define __interrupt interrupt ... void timer0_isr(void) __interrupt 1 { UINT16 c; static UINT32 x = 0; TF0 = 0; c = (UINT16)((UINT32)TM0_OVERFLOW_COUNT - (UINT32)TC0); TH0 = HI_UINT16(c); TL0 = LO_UINT16(c); x++; if(0 == x % TM0_FACTOR_1KHZ /*2*/) { IsrPollOsalTimer(); } P3_7 = !P3_7; /////////////////////////////////////////////////////////// if(0 == (x % STP_FACTOR_1KHZ)) { IsrPollWeight(); } } ...
TIMER0 初始化巨集如下 (細節就不多說了) :
...
#define InitTimer0(hz) { \
UINT16 c; \
\
IPH0 |= BV(1); \
TC0 = (UINT16)((UINT32)SYS_CLOCK_HZ / (UINT32)hz); \
c = (UINT16)((UINT32)TM0_OVERFLOW_COUNT - (UINT32)TC0); \
TMOD &= 0xF0; \
TMOD |= 0x01; \
TH0 = HI_UINT16(c); TL0 = LO_UINT16(c); \
TR0 = 1; \
ET0 = 1; \
EA = 1; \
}
...
為什麼要 1KHZ? OsalTimerScheduler API 設計
我設計的 API
unsigned char SetOsalTimer(nId, UINT32 , OSAL_TIMER_FUNC);
其中參數2 傳入單位為 1 mini-second 這就是 1KHZ 的由來.
如果參數2 傳入 1/10 mini-second, timer 就必須是 10KHZ的倍(multiple)頻. 除頻的定義於 TM0_FACTOR_1KHZ. TIMER0 ISR 呼叫 IsrPollOsalTimer(), 這樣保證了 "時間 tick" 是以 1 mini-second 持續遞增.
TIMER 頻率與 CHIP 能力有關, 最好是能夠在 TEST-PIN震盪出最高的頻率. 上述 "參數2 傳入單位為 1 mini-second", 若改為 "參數2 傳入單位為 1 micro-second", 則需 TIMER 1MHZ 頻率. 為什麼 TIMER 愈高頻愈好? 原因在於除頻, 以 TIMER 1MHZ為例, 若有10個 OsalTimer, 每一個 OsalTimer 能力上限為 100KHZ. 因此若 TIMER 能力可達 1MHZ, 則 24KHZ 之 PWM 穩定訊號 應該可以用 OsalTimer 輕易產生; 否則類似這 PWM 穩定訊號 應由 TIMER ISR 產生. 本範例中 MORTOR 的運轉端賴於 速度200~4000每秒突波, 這突波就是由 TIMER ISR 呼叫 `IsrPollWeight()` 產生 .
UART 溝通介面
接下來是 UART. UART 做為控制器與 UI 溝通的介面.
Uart Callback 函式:
typedef void (*rxcb)(unsigned char ch);
Uart static data
static rxcb callback; static unsigned char utb[MAX_TX_BUFFER+1]; static unsigned char *put; static unsigned char utleft;
Uart 初始化 (同樣地, 細節就不多說了). 主程序必須負責傳入 Uart Receiver Callback 函式.
void InitUart(rxcb cb) { P3M0 |= BV(0); P3M1 &= ~BV(0); P3M0 &= ~BV(1); P3M1 |= BV(1); osal_memset((put = utb), 0, MAX_TX_BUFFER + 1); utleft = 0; /////////////////////////////////////////////////////////////////////////////// // 256 - 65 = 0XBF // uart in mode 1 (8 bit), REN=1 SCON = 0x50; // Timer 1 in mode 2, 8-bit Auto Reload Timer/Counter TMOD |= 0x20; //TH1 = 0xFD; // 9600 Bds at 11.059MHz //TL1 = 0xFD; // 9600 Bds at 11.059MHz // 9600 Bds at 20 MHz TH1 = 0xBF; // 9600 Bds at 20 MHz TL1 = 0xBF; // Enable serial interrupt ES = 1; // Enable global interrupt EA = 1; // Timer 1 run TR1 = 1; if (cb) callback = cb; }
Uart RX ISR
void uart_isr(void) __interrupt 4 { if (RI) { unsigned char characterReceive; RI = 0; characterReceive = SBUF; if (callback) callback(characterReceive); } else //if (TI) { TI = 0; if (0 == utleft) { put = utb; } else { SBUF = *put++; utleft--; } } }
Uart TX (Transmiter)
unsigned char Tx(const unsigned char *buff, unsigned char len) { osal_memcpy((put = utb), (const void *)buff, (utleft = len)); SBUF = *put++, utleft--; return len; }
因此主程序只要 InitUart(), 實做 OnRx() 即可.
...
InitUart(OnRx);
void OnRx(unsigned char ch) {
...
}
...
OsalTimerScheduler
接下來進入重點了.
介紹一下 OsalTimerScheduler API . API 很簡單, 重點在於以下3個函數 (初始化, 設定TIMER, 取消TIMER).
void InitOsalTimer(void);
unsigned char SetOsalTimer(unsigned char nId, UINT32 nElapse, OSAL_TIMER_FUNC cb); // 參數2 傳入 mini-second
unsigned char KillOsalTimer(unsigned char nId);
#ifndef __OSAL_TIMER_H__ #define __OSAL_TIMER_H__ /////////////////////////////////////////////////////////// #define PROBE_OSALTIMER_ID 0 #define FOURTY_OSALTIMER_ID 1 #define CALIB_OSALTIMER_ID 2 #define MAX_OSAL_TIMER (CALIB_OSALTIMER_ID + 1) /////////////////////////////////////////////////////////// // 20MHZ #define SYS_CLOCK_HZ 20000210 // 20971520 //#define TM0_OVERFLOW_COUNT 65536 #define TM0_OVERFLOW_COUNT 65592 #define TM0_FACTOR_1KHZ 16 #define STP_FACTOR_1KHZ 2//((TM0_FACTOR_1KHZ * 10) / 16) /////////////////////////////////////////////////////////// #ifndef UINT8 #define UINT8 unsigned char #define INT8 signed char #define UINT16 unsigned int #define INT16 signed int #define UINT32 unsigned long #define INT32 signed long #define UCHAR unsigned char #endif // UINT8 /////////////////////////////////////////////////////////// #ifndef __xdata #define __xdata xdata #define __idata idata #define __pdata pdata #define __bit bit #define __interrupt interrupt #define __code code #endif // __xdata /////////////////////////////////////////////////////////// #ifndef BV #define BV(n) (1 << (n)) #endif #ifndef BF #define BF(x,b,s) (((x) & (b)) >> (s)) #endif #ifndef MIN #define MIN(n,m) (((n) < (m)) ? (n) : (m)) #endif #ifndef MAX #define MAX(n,m) (((n) < (m)) ? (m) : (n)) #endif #ifndef ABS #define ABS(n) (((n) < 0) ? -(n) : (n)) #endif /////////////////////////////////////////////////////////// #define OSAL_ASSERT(expr) st( if (!( expr )) osal_assert_handler(); ) #define OSAL_ASSERT_FORCED() osal_assert_handler() #define OSAL_ASSERT_STATEMENT(statement) st( statement ) #define OSAL_ASSERT_DECLARATION(declaration) declaration /////////////////////////////////////////////////////////// // takes a byte out of a uint32 : var - uint32, ByteNum - byte to take out (0 - 3) #define BREAK_UINT32( var, ByteNum ) \ (unsigned char)((UINT32)(((var) >>((ByteNum) * 8)) & 0x00FF)) #define BUILD_UINT32(Byte0, Byte1, Byte2, Byte3) \ ((UINT32)((UINT32)((Byte0) & 0x00FF) \ + ((UINT32)((Byte1) & 0x00FF) << 8) \ + ((UINT32)((Byte2) & 0x00FF) << 16) \ + ((UINT32)((Byte3) & 0x00FF) << 24))) #define BUILD_UINT16(loByte, hiByte) \ ((UINT16)(((loByte) & 0x00FF) + (((hiByte) & 0x00FF) << 8))) #define HI_UINT16(a) (((a) >> 8) & 0xFF) #define LO_UINT16(a) ((a) & 0xFF) #define BUILD_UINT8(hiByte, loByte) \ ((unsigned char)(((loByte) & 0x0F) + (((hiByte) & 0x0F) << 4))) #define HI_UINT8(a) (((a) >> 4) & 0x0F) #define LO_UINT8(a) ((a) & 0x0F) /////////////////////////////////////////////////////////// #define st(x) do { x } while (__LINE__ == -1) /////////////////////////////////////////////////////////// typedef void (*OSAL_TIMER_FUNC)(unsigned char nId); typedef struct OsalTimer_t { UINT32 timeout; OSAL_TIMER_FUNC cb; } OsalTimer_t; /////////////////////////////////////////////////////////// void InitOsalTimer(void); /////////////////////////////////////////////////////////// void OsalTimerScheduler(void); void IsrPollOsalTimer(void); UINT32 GetOsalTimerTick(void); /////////////////////////////////////////////////////////// unsigned char SetOsalTimer(unsigned char nId, UINT32 nElapse, OSAL_TIMER_FUNC cb); unsigned char KillOsalTimer(unsigned char nId); /////////////////////////////////////////////////////////// void *osal_memset(void *dest, unsigned char value, int len); void *osal_memcpy(void *dst, const void *src, unsigned int len); #endif //__OSAL_TIMER_H__
OSAL_TIMER_FUNC CallBack 函數及其核心結構:
OsalTimer_t 結構
timeout: 間隔多少 mini-seconds TIMEOUT
cb: 時間到了, 主程序傳入的 OSAL_TIMER_FUNC CallBack 函數 被喚醒做我們要做的事.
typedef void (*OSAL_TIMER_FUNC)(unsigned char nId); typedef struct OsalTimer_t { UINT32 timeout; OSAL_TIMER_FUNC cb; } OsalTimer_t;
static data, 包括 tick 及 OsalTimer_t 結構 所組成的陣列
static UINT32 osal_timer_tick; static struct OsalTimer_t to[MAX_OSAL_TIMER]; static unsigned char task;
Scheduler 採 round-robin 設計, 並沒有偏重於哪一個 timerId. Main 主程序初始化我們要的東西, 無窮迴圈 POLLING OsalTimerScheduler() 函數. 值得注意的是, Timer Callback 函數並不是由 ISR 呼叫, 而是由 Main 主程序 POLLING,
for(;;)
OsalTimerScheduler();
Timer ISR 只是做 ++ -- 之類的. 套用這樣的架構, 程式設計師可以專注於 Timer Callback 函數的設計. 以本案例而言, 專注於 ProbeTimer() / CalibTimer() / InitWeightTimer() / SetWeightLevel() 等等Callback 函數的實做. 這樣的設計已經有一點兒接近 微作業系統 的概念了.
int main (void) { InitTimer0(((UINT32)TM0_FACTOR_1KHZ * (UINT32)1000)); InitOsalTimer(); InitUart(OnRx); ... for(;;) OsalTimerScheduler(); return 0; }
InitOsalTimer() 只是將OsalTimer_t 結構 所組成的陣列設為0; GetOsalTimerTick() 只是傳回 tick 數; 前面提到的 timer0_isr() 以 1KHZ 的頻率 呼叫 IsrPollOsalTimer(), 其所做的事只是 tick++.
void InitOsalTimer(void) { osal_memset(to, 0, sizeof(to)); } UINT32 GetOsalTimerTick(void) { return osal_timer_tick; } void IsrPollOsalTimer(void) { osal_timer_tick++; } void *osal_memcpy(void *dst, const void *src, unsigned int len) { unsigned char *pDst; const unsigned char *pSrc; pSrc = src; pDst = dst; while (len--) *pDst++ = *pSrc++; return pDst; } void *osal_memset(void *dest, unsigned char value, int len) { int i; unsigned char *p; p = (unsigned char *)dest; for (i = 0; i < len; i++) *(p + i) = value; return (void *)(p + len); }
設定 TIMER, 傳入 timerId, timeout mini-seconds 以及 OSAL_TIMER_FUNC CallBack 函數
unsigned char SetOsalTimer(unsigned char nId, UINT32 nElapse, OSAL_TIMER_FUNC cb) { to[nId].cb = cb; to[nId].timeout = GetOsalTimerTick() + (UINT32)nElapse; return (nId + 1); }
取消 TIMER
unsigned char KillOsalTimer(unsigned char nId) { to[nId].timeout = 0; return (nId + 1); }
接下來我們來看 Scheduler
void OsalTimerScheduler(void) { unsigned char i; OSAL_TIMER_FUNC cb; for (i = 0; i < MAX_OSAL_TIMER; i++) { if(to[(task + i) % MAX_OSAL_TIMER].timeout == 0) continue; if(GetOsalTimerTick() >= to[(task + i) % MAX_OSAL_TIMER].timeout) { // 某個特定的timerId時間到了 cb = to[(task + i) % MAX_OSAL_TIMER].cb; osal_memset(&to[(task + i) % MAX_OSAL_TIMER], 0, sizeof(struct OsalTimer_t)); if (cb) cb((task + i) % MAX_OSAL_TIMER); // 且該特定的timerId OSAL_TIMER_FUNC CallBack 函數不為 0, OSAL_TIMER_FUNC CallBack 被呼叫 break; } } task++; task %= MAX_OSAL_TIMER; return;// (j + 1); }
WeightStack UI DEMO
在說明主程序 Main 前, 我們先來檢視 舉重堆疊的 UI 影片. 從影片中我們可以看到 UI 是一個 Android App; 它透過 uart 傳送 BINARY CODE 與控制器溝通, 達到控制舉重磅數.
主程序 Main
主程序 Main 應用 OsalTimerScheduler (Csample.c). OnRx() 為 uart CallBack 函數, OnUserMessage() 為 UI Android App 傳送至控制器的封包 ( 校正回原點 以及 設定磅數 ).
#ifndef __CSAMPLE1_H__ #define __CSAMPLE1_H__ #define InitTimer0(hz) { \ UINT16 c; \ \ IPH0 |= BV(1); \ TC0 = (UINT16)((UINT32)SYS_CLOCK_HZ / (UINT32)hz); \ c = (UINT16)((UINT32)TM0_OVERFLOW_COUNT - (UINT32)TC0); \ TMOD &= 0xF0; \ TMOD |= 0x01; \ TH0 = HI_UINT16(c); TL0 = LO_UINT16(c); \ TR0 = 1; \ ET0 = 1; \ EA = 1; \ } void OnRx(unsigned char ch); void OnOsalEvent(unsigned char nId); #endif
/////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// //#include <reg51.h> #include <AT89LP4052.H> #include <stdlib.h> //#include <stdio.h> #include "osal_timer.h" #include "csample1.h" #include "motors.h" #include "uart.h" #include "pwm.h" #include "weight.h" #include "eeprom.h" /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// #define PT0_SOF_0 0 #define PT0_SOF_1 1 #define PT0_LEN_H 2 #define PT0_LEN_L 3 #define PT0_RESERVE 4 #define PT0_D_RQ_RP 5 #define PT0_D_ID_H 6 #define PT0_D_ID_L 7 #define PT0_DATA_START 8 /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// // 1HZ 20 static UINT16 TC0; /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// static UINT8 rx_state = 0; /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// void OnOsalEvent(unsigned char nId) { if (0 == nId) {} return; } /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// void OnUserMessage(unsigned char *buffer, unsigned char len) { // FE FE 00 04 00 00 01 20 00 00 00 00 4B E9 // FE FE 00 04 00 00 01 20 00 00 00 01 8A 29 // FE FE 00 04 00 00 01 20 00 00 00 02 CA 28 // FE FE 00 04 00 00 01 20 00 00 00 03 0B E8 // FE FE 00 04 00 00 01 20 00 00 00 04 4A 2A // FE FE 00 04 00 00 01 20 00 00 00 05 8B EA // FE FE 00 04 00 00 01 20 00 00 00 06 CB EB // FE FE 00 04 00 00 01 20 00 00 00 07 0A 2B // FE FE 00 04 00 00 01 20 00 00 00 08 4A 2F // FE FE 00 04 00 00 01 20 00 00 00 09 8B EF // FE FE 00 04 00 00 01 20 00 00 00 0A CB EE // FE FE 00 04 00 00 01 20 00 00 00 0B 0A 2E // FE FE 00 04 00 00 01 21 00 00 00 00 76 29 UINT16 did; UINT32 level; //if(0 == buffer) return; if(0 == len) return; did = BUILD_UINT16(buffer[PT0_D_ID_L], buffer[PT0_D_ID_H]); //if (0X0120 != did && 0X0121 != did) return; if (0X0121 == did) { extern bit found_zero; if (0 == S3_PIN || 0 == S4_PIN) return; found_zero = 0; KillOsalTimer(FOURTY_OSALTIMER_ID); KillOsalTimer(CALIB_OSALTIMER_ID); STOP(2); SetOsalTimer(PROBE_OSALTIMER_ID, 97, ProbeTimer); // 校正回原點 (40磅) return; } if (0X0120 == did) { level = BUILD_UINT32(buffer[PT0_DATA_START + 3], buffer[PT0_DATA_START + 2], buffer[PT0_DATA_START + 1], buffer[PT0_DATA_START]); if (level > 11) return; SetWeightLevel((INT8)level); // 設定磅數 return; } return; } /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// static UINT32 calccrc(UINT8 crcbuf, UINT32 crc) { UINT8 i; crc ^= crcbuf; for(i = 0; i < 8; i++) { unsigned char chk; chk = crc & 1; crc >>= 1; crc &= 0X7FFF; if (1 == chk) crc ^= 0XA001; crc &= 0XFFFF; } return crc; } UINT32 Crc16(unsigned char *buf, UINT16 len) { UINT8 hi; UINT8 lo; UINT16 i; UINT32 crc = 0XFFFF; for (i = 0; i < len; i++) { crc = calccrc(*buf, crc); buf++; } hi = crc & 0XFF; lo = crc >> 8; crc = (hi << 8) | lo; return crc; } /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// void OnRx(unsigned char ch) { // FE FE 00 04 00 00 01 20 00 00 00 0B 0A 2E // FE FE 00 04 00 00 01 21 00 00 00 00 76 29 #define MAX_RX_BUFFER 24 UINT16 crc = 0; UINT16 chk = 1; static unsigned char frame[MAX_RX_BUFFER + 1]; static UINT16 LEN = 0; static UINT16 offs = 0; if (PT0_SOF_0 == offs) { if (ch == 0XFE) { frame[offs++] = ch; return; } offs = 0; return; } if (PT0_SOF_1 == offs) { if (ch == 0XFE) { frame[offs++] = ch; return; } offs = 0; return; } if (PT0_LEN_H == offs) { frame[offs++] = ch; return; } if (PT0_LEN_L == offs) { frame[offs++] = ch; LEN = BUILD_UINT16(frame[PT0_LEN_L], frame[PT0_LEN_H]); if (4 != LEN) offs = 0; return; } if (PT0_D_RQ_RP == offs) { if (0 != ch) { offs = 0; return; } frame[offs++] = ch; return; } //if (offs >= LEN + 2 + PT0_DATA_START) if (offs >= LEN + 1 + PT0_DATA_START) { frame[offs++] = ch; crc = (UINT16)Crc16(&frame[PT0_SOF_0], LEN + PT0_DATA_START); chk = BUILD_UINT16(frame[1 + PT0_DATA_START + LEN], frame[PT0_DATA_START + LEN]); /////////////////////////////////////////////////////////// if (crc == chk) OnUserMessage(frame, offs); // 從 UI 收到的 BINARY CODE offs = 0; return; } frame[offs++] = ch; return; } /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// int main (void) { /////////////////////////////////////////////////////////// InitTimer0(((UINT32)TM0_FACTOR_1KHZ * (UINT32)1000)); InitOsalTimer(); /////////////////////////////////////////////////////////// InitUart(OnRx); /////////////////////////////////////////////////////////// InitWeight(); //if (1 == TEN_POUNDS_PIN) found_zero = 1; //else found_zero = 0; KillOsalTimer(FOURTY_OSALTIMER_ID); KillOsalTimer(CALIB_OSALTIMER_ID); STOP(2); SetOsalTimer(PROBE_OSALTIMER_ID, 97, ProbeTimer); /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// for(;;) { //P3_7 = !P3_7; OsalTimerScheduler(); } return 0; } /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// void timer0_isr(void) __interrupt 1 { UINT16 c; static UINT32 x = 0; TF0 = 0; c = (UINT16)((UINT32)TM0_OVERFLOW_COUNT - (UINT32)TC0); TH0 = HI_UINT16(c); TL0 = LO_UINT16(c); x++; if(0 == x % TM0_FACTOR_1KHZ) { IsrPollOsalTimer(); } P3_7 = !P3_7; /////////////////////////////////////////////////////////// if(0 == (x % STP_FACTOR_1KHZ)) { IsrPollWeight(); } }
校正/移動 WeightStack
主程序 Main 應用 OsalTimerScheduler (Csample.c). OnRx() 為 uart CallBack 函數, OnUserMessage() 為 UI Android App 傳送至控制器的封包 ( 校正回原點 以及 設定磅數 ). IsrPollWeight() 是控制器實際在作機械動作的函數, 因此我們在 TIMER0 ISR 以 1KHZ頻率持續呼叫. 一時找不到線路圖, 所以機械動作原理就不著墨太深. 只要知道最上方磅數最小, 最下方磅數最大.
ProbeTimer(): 一開始觸發 ProbeTimer, 然後以快速地往上移動 (觸發 ProbeTimer), 直到 sensor 頂到 10磅, 此時會超過10磅位置, 觸發 CalibTimer .
CalibTimer(): 緩慢地往下移 (觸發 CalibTimer), 直到 sensor 10磅位置. 以此位置為基準, 下降到 40 磅位置 (觸發 SetWeightLevel, default 位置).
SetWeightLevel(): 根據 UI 設定的磅數, 移動舉重堆至我們要的位置.
#ifndef __WEIGHT_H__ #define __WEIGHT_H__ // CN3 PULSE P3_4 // CN4 DIR P3_5 // S1 上限 P3_2 // S2 下限 P3_3 // S5 10磅 P1_7 // S3 P1_6 // S4 P1_5 #define PULSE_PIN P3_4 #define DIR_PIN P3_5 #define UP_LIMIT_PIN P3_2 #define DOWN_LIMIT_PIN P3_3 #define TEN_POUNDS_PIN P1_7 #define S3_PIN P1_6 #define S4_PIN P1_5 #define InitWeight() do { \ P3M0 &= ~BV(4); P3M1 |= BV(4); \ P3_4 = 1; \ P3M0 &= ~BV(5); P3M1 |= BV(5); \ P3_5 = 1; \ P3M0 &= ~BV(7); P3M1 |= BV(7); \ P3_7 = 1; \ P3M0 |= BV(2); P3M1 &= ~BV(2); \ P3M0 |= BV(3); P3M1 &= ~BV(3); \ P1M0 |= BV(7); P1M1 &= ~BV(7); \ P1M0 |= BV(6); P1M1 &= ~BV(6); \ P1M0 |= BV(5); P1M1 &= ~BV(5); \ } while(0) void IsrPollWeight(void); void InitWeightTimer(unsigned char nId); void CalibTimer(unsigned char nId); void ProbeTimer(unsigned char nId); int SetWeightLevel(INT8 level); #endif // __WEIGHT_H__
#include <AT89LP4052.H> #include <stdlib.h> #include "osal_timer.h" #include "weight.h" /////////////////////////////////////////////////////////////////////////////// // /////////////////////////////////////////////////////////////////////////////// /* //明:10磅226pulse(出厂置),如果是300磅机型,最到最重要送226*29=6554pulse //1. 每可范0---1000 //2. 速度200-4000pulse/S */ /////////////////////////////////////////////////////////////////////////////// // /////////////////////////////////////////////////////////////////////////////// #define DIR(b) DIR_PIN = b #define DIR_UP 1 #define DIR_DOWN 0 #define CURRENT_DIR current_dir bit current_dir = DIR_UP; bit found_zero = 0; /////////////////////////////////////////////////////////////////////////////// // /////////////////////////////////////////////////////////////////////////////// #define STEPS (450*2) #define LEVEL_00 2 //#define LEVEL_01 (2*STEPS) #define LEVEL_01 (STEPS) #define LEVEL_02 (LEVEL_01 + STEPS) #define LEVEL_03 (LEVEL_02 + STEPS) #define LEVEL_04 (LEVEL_03 + STEPS) #define LEVEL_05 (LEVEL_04 + STEPS) #define LEVEL_06 (LEVEL_05 + STEPS) #define LEVEL_07 (LEVEL_06 + STEPS) #define LEVEL_08 (LEVEL_07 + STEPS) #define LEVEL_09 (LEVEL_08 + STEPS) #define LEVEL_10 (LEVEL_09 + STEPS) #define LEVEL_11 (LEVEL_10 + STEPS) #define LEVEL_12 (LEVEL_11 + STEPS) /////////////////////////////////////////////////////////////////////////////// // /////////////////////////////////////////////////////////////////////////////// #define MOVE(a ,b) do { current_counter = (a); target_counter = (b); } while(0) #define STOP(a) do { current_counter = (a); target_counter = (a); } while(0) /////////////////////////////////////////////////////////////////////////////// // /////////////////////////////////////////////////////////////////////////////// static INT16 saved_target_counter = 0; static bit saved_dir = DIR_UP; static INT16 target_counter = LEVEL_00; static INT16 current_counter = LEVEL_00; static INT16 next_counter = 0; /////////////////////////////////////////////////////////////////////////////// // /////////////////////////////////////////////////////////////////////////////// void IsrPollWeight(void) { static UINT8 flag = 0; /////////////////////////////////////////////////////////////////////////////// if (0 == target_counter) { PULSE_PIN = !PULSE_PIN; return; } /////////////////////////////////////////////////////////////////////////////// if (0 != S3_PIN && 0 != S4_PIN) { if (saved_target_counter) { target_counter = saved_target_counter; saved_target_counter = 0; if (target_counter > current_counter) { CURRENT_DIR = DIR_DOWN; DIR(CURRENT_DIR); } else { CURRENT_DIR = DIR_UP; DIR(CURRENT_DIR); } } else if (next_counter) { target_counter = next_counter; next_counter = 0; if (target_counter > current_counter) { CURRENT_DIR = DIR_DOWN; DIR(CURRENT_DIR); } else { CURRENT_DIR = DIR_UP; DIR(CURRENT_DIR); } } } /////////////////////////////////////////////////////////////////////////////// if (target_counter == current_counter) { if (1 == flag) { flag = 0; CURRENT_DIR = saved_dir; DIR(CURRENT_DIR); } return; } /////////////////////////////////////////////////////////////////////////////// // DOWN if (target_counter > current_counter) { if (0 == S3_PIN || 0 == S4_PIN) { if (0 == saved_target_counter && 0 != target_counter) { saved_target_counter = target_counter; saved_dir = DIR_PIN; flag = 1; CURRENT_DIR = DIR_UP; DIR(CURRENT_DIR); } if (current_counter >= LEVEL_12) target_counter = LEVEL_12; else if (current_counter >= LEVEL_11 && current_counter < LEVEL_12) target_counter = LEVEL_11; else if (current_counter >= LEVEL_10 && current_counter < LEVEL_11) target_counter = LEVEL_10; else if (current_counter >= LEVEL_09 && current_counter < LEVEL_10) target_counter = LEVEL_09; else if (current_counter >= LEVEL_08 && current_counter < LEVEL_09) target_counter = LEVEL_08; else if (current_counter >= LEVEL_07 && current_counter < LEVEL_08) target_counter = LEVEL_07; else if (current_counter >= LEVEL_06 && current_counter < LEVEL_07) target_counter = LEVEL_06; else if (current_counter >= LEVEL_05 && current_counter < LEVEL_06) target_counter = LEVEL_05; else if (current_counter >= LEVEL_04 && current_counter < LEVEL_05) target_counter = LEVEL_04; else if (current_counter >= LEVEL_03 && current_counter < LEVEL_04) target_counter = LEVEL_03; else if (current_counter >= LEVEL_02 && current_counter < LEVEL_03) target_counter = LEVEL_02; else if (current_counter >= LEVEL_01 && current_counter < LEVEL_02) target_counter = LEVEL_01; else return; } current_counter++; PULSE_PIN = !PULSE_PIN; return; } /////////////////////////////////////////////////////////////////////////////// // UP if (0 == S3_PIN || 0 == S4_PIN) { if (0 == saved_target_counter && 0 != target_counter) { saved_target_counter = target_counter; saved_dir = DIR_PIN; //CURRENT_DIR = DIR_UP; //DIR(CURRENT_DIR); } if (current_counter >= LEVEL_12) target_counter = LEVEL_12; else if (current_counter >= LEVEL_11 && current_counter < LEVEL_12) target_counter = LEVEL_11; else if (current_counter >= LEVEL_10 && current_counter < LEVEL_11) target_counter = LEVEL_10; else if (current_counter >= LEVEL_09 && current_counter < LEVEL_10) target_counter = LEVEL_09; else if (current_counter >= LEVEL_08 && current_counter < LEVEL_09) target_counter = LEVEL_08; else if (current_counter >= LEVEL_07 && current_counter < LEVEL_08) target_counter = LEVEL_07; else if (current_counter >= LEVEL_06 && current_counter < LEVEL_07) target_counter = LEVEL_06; else if (current_counter >= LEVEL_05 && current_counter < LEVEL_06) target_counter = LEVEL_05; else if (current_counter >= LEVEL_04 && current_counter < LEVEL_05) target_counter = LEVEL_04; else if (current_counter >= LEVEL_03 && current_counter < LEVEL_04) target_counter = LEVEL_03; else if (current_counter >= LEVEL_02 && current_counter < LEVEL_03) target_counter = LEVEL_02; else if (current_counter >= LEVEL_01 && current_counter < LEVEL_02) target_counter = LEVEL_01; else return; } current_counter--; // 產生 PULSE PULSE_PIN = !PULSE_PIN; return; } void InitWeightTimer(unsigned char nId) { if (0 == nId) {} // 移至 40磅位置 SetWeightLevel(1); ES = 1; } #define CALIB_STEPS (20 * 2) static INT8 calib_counter = CALIB_STEPS; void CalibTimer(unsigned char nId) { if (0 == nId) {} PULSE_PIN = !PULSE_PIN; if (calib_counter > 0) { calib_counter--; CURRENT_DIR = DIR_UP; DIR(CURRENT_DIR); SetOsalTimer(CALIB_OSALTIMER_ID, 1, CalibTimer); return; } CURRENT_DIR = DIR_DOWN; DIR(CURRENT_DIR); if (0 != TEN_POUNDS_PIN) { SetOsalTimer(CALIB_OSALTIMER_ID, 1, CalibTimer); return; } //found_zero = 0; STOP(LEVEL_01); PULSE_PIN = 1; SetOsalTimer(FOURTY_OSALTIMER_ID, 0, InitWeightTimer); //SetOsalTimer(FOURTY_OSALTIMER_ID, 91, InitWeightTimer); return; } // FE FE 00 04 00 00 01 21 00 00 00 00 76 29 void ProbeTimer(unsigned char nId) { if (0 == nId) {} if (found_zero) return; if (1 == TEN_POUNDS_PIN) { found_zero = 1; STOP(LEVEL_01); PULSE_PIN = 1; CURRENT_DIR = DIR_UP; DIR(CURRENT_DIR); calib_counter = CALIB_STEPS; SetOsalTimer(CALIB_OSALTIMER_ID, 0, CalibTimer); return; } CURRENT_DIR = DIR_UP; DIR(CURRENT_DIR); // MOVING UP MOVE(32767, 0); SetOsalTimer(PROBE_OSALTIMER_ID, 0, ProbeTimer); return; } //static unsigned char __code levels[] = {10, 15, 20, 25, 30, 35, 40, 45, 50, 60, 70, 80}; // FE FE 00 04 00 00 01 20 00 00 00 00 4B E9 // 0 // FE FE 00 04 00 00 01 20 00 00 00 01 8A 29 // FE FE 00 04 00 00 01 20 00 00 00 02 CA 28 // FE FE 00 04 00 00 01 20 00 00 00 03 0B E8 // FE FE 00 04 00 00 01 20 00 00 00 04 4A 2A // FE FE 00 04 00 00 01 20 00 00 00 05 8B EA // FE FE 00 04 00 00 01 20 00 00 00 06 CB EB // FE FE 00 04 00 00 01 20 00 00 00 07 0A 2B // FE FE 00 04 00 00 01 20 00 00 00 08 4A 2F // FE FE 00 04 00 00 01 20 00 00 00 09 8B EF // FE FE 00 04 00 00 01 20 00 00 00 0A CB EE // FE FE 00 04 00 00 01 20 00 00 00 0B 0A 2E // 11 int SetWeightLevel(INT8 level) { INT16 target; if (level < 0) return -1; if (level > 11) return -1; if (0 == found_zero) return -1; /////////////////////////////////////////////////////////////////////////////// if (current_counter != target_counter) return -1; if (0 == S3_PIN || 0 == S4_PIN) { if (0 == level) target = LEVEL_01; else if (1 == level) target = LEVEL_02; else if (2 == level) target = LEVEL_03; else if (3 == level) target = LEVEL_04; else if (4 == level) target = LEVEL_05; else if (5 == level) target = LEVEL_06; else if (6 == level) target = LEVEL_07; else if (7 == level) target = LEVEL_08; else if (8 == level) target = LEVEL_09; else if (9 == level) target = LEVEL_10; else if (10 == level) target = LEVEL_11; else target = LEVEL_12; next_counter = target; return -1; } /////////////////////////////////////////////////////////////////////////////// //PULSE_PIN = 1; //target = (UINT16)levels[level] * (2 * 226) / 10; if (0 == level) target = LEVEL_01; else if (1 == level) target = LEVEL_02; else if (2 == level) target = LEVEL_03; else if (3 == level) target = LEVEL_04; else if (4 == level) target = LEVEL_05; else if (5 == level) target = LEVEL_06; else if (6 == level) target = LEVEL_07; else if (7 == level) target = LEVEL_08; else if (8 == level) target = LEVEL_09; else if (9 == level) target = LEVEL_10; else if (10 == level) target = LEVEL_11; else target = LEVEL_12; /////////////////////////////////////////////////////////////////////////////// if (target == current_counter) return 0; /////////////////////////////////////////////////////////////////////////////// if (target > current_counter) { CURRENT_DIR = DIR_DOWN; DIR(CURRENT_DIR); //PULSE_PIN = 1; MOVE(current_counter, target); return 0; } /////////////////////////////////////////////////////////////////////////////// // MOVE(LEVEL_01, LEVEL_01); CURRENT_DIR = DIR_UP; DIR(CURRENT_DIR); //PULSE_PIN = 1; MOVE(current_counter, target); return 0; }
參考線路圖
Radeon Kernel configuration
Mali Binary Driver
Nvidia Kernel Configuration
Official Git Repositories on Android
Seattle Soc Device Tree (amd-overdrive.dts)
Kconfig Language
Android Bionic Kernel Header Generator
Official Git Repository on Android Kernel version 4.4
Firefly-rk3288_android5.1_git_20150910.tar
Git Repository on Kernel version 4.5
Soft Iron
Android for MSM Project
Notes: Qualcomm APQ8060 Dragonboard by follower
Dragonboard 410c
Android porting guide
分析 Android 系统編譯流程
ARM-software Gator
Android-x86
Android 編譯及運行
Android for MSM Project
96boards/android_device_linaro_db410c
Linaro, Porting to a new board
ARMv8-A Foundation Model memory map
Odroid / U-boot
ARM® Mali™ Midgard GPU User-Space Binary Drivers
Open Source Mali Utgard GPU Linux Kernel Drivers
Open Source Mali GPUs Linux Kernel Device Drivers – previous releases
ARMv8‑A Foundation Platform User Guide (Memory map)
firefly-rk3288_android5.1_git_20150910.tar
CentOS AA64 image
lollipop_hardware_rockchip_librkvpu
Android Device
Android 編譯系統參考手冊
Google Git on Android Platform External
init.recovery.rc
FireFly-Rk3288 Prebuild Images
Firefly-RK3288-Kernel-With-Mali764
Firefly-RK3288 Android5.1 支持5.5寸MIPI屏和触摸模组的修改方法
Firefly-RK3288 Android5.1 Lollipop 10.1寸屏和触摸的配置
Firefly-RK3288 MIPI单通道屏调试过程及问题整理
Linux Core U-Boot User's Guide
Ubuntu 14.04 installation instructions for x-gene Mustang reference board (ARM64)
在 Linux 命令列進行 PPTP VPN 連線
Why is it that my initrd only has one directory, namely, 'kernel'?
Making Bootable Linux CDs
Updating Intel cpu microcode
Configuring X (Red Hat Linux 7.2)
Signed kernel module support
Root File System on a Ramdisk
【翻譯】Qt Tutorial
hitbox實況平台使用心得與教學 (OBS、FF)
Burning iPXE into Intel network adapters
How to Connect to an iSCSI Target Using Windows
How to Disable Driver Signing Check on Windows
Git 學習筆記: 30 天精通 Git 版本控管
GIT版本控制
Email: jasonc@mail2000.com.tw
留言列表