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
