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;
}

 

參考線路圖
 

 

 

 


 
 
 
Firefly-RK3288 Android 5.1 Compilation
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版本控制

 

 

 

 

z.png
Email: jasonc@mail2000.com.tw

 

 

 

arrow
arrow

    Lexra 發表在 痞客邦 留言(0) 人氣()