2012年2月11日 星期六

使用簡單多工3版遇到的問題

主要是在使用WakeUp()在中斷程式所遇到的問題。
因為新加的功能會方便很多,所以在中斷程式中Bee用了不少,結果還是發生程序卡死的狀況。
程式碼如下:

void ADC_Start(void)
{
    void ADC_GetValue(void);

    Start_ADC();
    Sleep_Next(ADC_GetValue);
}

中斷程式中為
    WakeUp(ADC_TASK_ID);

結果Run一段時間會卡死。
去看Task_State[ADC_TASK_ID]內容為3,就是TASK_WAIT
但ADC暫存器卻顯示已產生過中斷。

後來將Start_ADC();放在最後一行,問題就沒有了。

發生這樣的問題主要是下指令到產生中斷時間太短,在進入Sleep之前就產生中斷。
然後Sleep_Next才將TASK_WAIT寫入Task_State[ADC_TASK_ID],從此變成長睡不起。

以後要將會產生中斷的指令寫在函式最尾,以免再發生這樣的問題。


2012年2月5日 星期日

使用Cortex-M3後的事

在前公司,一直摸不到ARM。在技術上這是一個趨勢,早晚會碰到,不如早點用,但苦無機會。
現在用Cortex-M3,不只用,還要很熟。

在設計上有次算錯了效能,結果放了一個FPGA上去。FPGA廠商很高興,因為公司以往最多用到CPLD,遇到會寫的人,自然全力支援。
就有二家全力支援產品開發,都希望有機會採用。
後來Bee就發現MCU也可以做,加上開始熟STM32,結果用中斷程式不用十行就解決了。
對公司是好的,又少了一個零件。但FPGA廠商就沒這樣高興了。

去年底出了Cortex-M4,廠商送來Demo Board,趁空擋試了一下,將FreeRTOS給裝上去,看來還不錯。
不過因為案子皆已開跑,就先放著。
然後,其他也出M4的廠商也來訪,也送來一套。過完農曆年,又一家廠商來也想推。
看來Cortex-M系列真的很拼,MCU市場還真難賺。

2012年1月26日 星期四

簡單多工3版

在STM32上運行時發現,可以不使用DIS_INTR()這個巨集。
因為這個在原子操作下已無需要。
後來就將Task管理,從function pointer改為原子操作下的資料。
結果發現,還可以進一步省下記憶體。

另外Skip方式也改了,改到主程式去做。現在不會跳去Dummy(),檢查完畢就移到下一個工作去。

新增功能有
WakeUp(i),可以從中斷程式中呼叫,所以可以設定只由中斷叫醒的Task,免去使用全域變數做溝通。
Sleep_Next(p_func),Task沉睡,設定醒來時執行函式,只能由WakeUp(i)叫醒。

新版本程式如下:
#include "mt.h"
#define TASK_NUM    4
extern void SimInit(void);
extern void Task1(void);
extern void Task2(void);
extern void Task3(void);
extern void OS_ENTER_CRITICAL(void);
extern void OS_EXIT_CRITICAL(void);

#define INIT_DEVICE()        SimInit()
#define DIS_INTR()            OS_ENTER_CRITICAL()
#define ENA_INTR()            OS_EXIT_CRITICAL()
// O.S function declaim
void Dummy(void);
unsigned char FuncID;
volatile unsigned char Task_State[TASK_NUM];
unsigned short Task_Delay_Count[TASK_NUM];
void (*TaskFunc[TASK_NUM])(void);

// O.S functions
void InitSystem(void)
{
    unsigned char i;

    FuncID = 0;
    for ( i= 0; i< TASK_NUM ; i++)
    {
        Task_State[i] = TASK_RUNNING;
        Task_Delay_Count[i] = 0;
        TaskFunc[i] = Dummy;
    }
    TaskFunc[1] = Task1;
    TaskFunc[2] = Task2;
    TaskFunc[3] = Task3;
}

void Sleep_Next(void (*p_func)(void))
{
    TaskFunc[FuncID] = p_func;
    Task_State[FuncID] = TASK_WAIT;
}

void WakeUp(unsigned char n)
{
    Task_State[n] = TASK_RUNNING;
}

void Task_Set_Next(void (*p_func)(void))
{
    TaskFunc[FuncID]=p_func;
}

void Dummy(void)
{
    Sleep_Next(Dummy);
}

void Task_Delay_Ms_Next(unsigned int n, void (*p_func)(void))
{
    if (! n ) n = 1;
    TaskFunc[FuncID] = p_func;
    Task_Delay_Count[FuncID] = n;
    Task_State[FuncID] = TASK_DELAY;
}

void Task_Set_Skip_Next(unsigned int n, void (*p_func)(void))
{
    if (! n ) n = 1;
    TaskFunc[FuncID] = p_func;
    Task_Delay_Count[FuncID] = n;
    Task_State[FuncID] = TASK_SKIP;
}

// O.S functions end

// main loop
void main(void)
{
    INIT_DEVICE();
    InitSystem();
    while (1)
    {
        switch (Task_State[FuncID])
        {
        case TASK_RUNNING :
            TaskFunc[FuncID]();
            break;
        case TASK_SKIP :
            if ( Task_Delay_Count[FuncID] )
            {
                Task_Delay_Count[FuncID]--;
                if (! Task_Delay_Count[FuncID]) WakeUp(FuncID);
            }
            break;
        default:
            break;
        }
        if ( ++FuncID >= TASK_NUM ) FuncID = 0;
    }
}

void TimeTick(void)  // This is timer interrupt
{
    unsigned char i;

    for ( i=0; i< TASK_NUM; i++)
    {
        if ( Task_State[i] == TASK_DELAY )
        {
            if ( Task_Delay_Count[i] )
            {
                Task_Delay_Count[i]--;
                if (! Task_Delay_Count[i]) WakeUp(i);
            }
        }
    }
}

// Begin User Tasks

mt.h內容為
void Sleep_Next(void (*p_func)(void));
void WakeUp(unsigned char n);
void Task_Set_Next(void (*p_func)(void));
void Task_Delay_Ms_Next(unsigned int n, void (*p_func)(void));
void Task_Set_Skip_Next(unsigned int n, void (*p_func)(void));

//  Task State
#define        TASK_RUNNING    0
#define        TASK_SKIP          1
#define        TASK_DELAY        2
#define        TASK_WAIT          3

實驗程式如下:
#include < stdio.h >
#include "mt.h"
void Task1(void)
{
    printf("G");
    Task_Delay_Ms_Next(10,Task1);
}

void Task2(void)
{
    printf("z");
    Task_Delay_Ms_Next(1,Task2);
}

void Task3(void)
{
    printf(".");
    Task_Set_Skip_Next(60000,Task3);
}

Windows模擬結果

2011年9月4日 星期日

簡單多工2版

用在PIC18、PIC33時發現一些問題,做了修改,同時加入windows平台下的模擬程式。

主程式:
#define TASK_NUM    4
extern void SimInit(void);
extern void Task1(void);
extern void Task2(void);
extern void OS_ENTER_CRITICAL(void);
extern void OS_EXIT_CRITICAL(void);

#define INIT_DEVICE()        SimInit()
#define DIS_INTR()            OS_ENTER_CRITICAL()
#define ENA_INTR()            OS_EXIT_CRITICAL()
// O.S function declaim
void Dummy(void);
unsigned char FuncID;
unsigned int Task_Delay_Count[TASK_NUM];
unsigned int Task_Skip_Count[TASK_NUM];
void (*TaskFunc[TASK_NUM])(void);
void (*TaskResumeFunc[TASK_NUM])(void);

// O.S functions
void InitSystem(void)
{
    unsigned char i;

    FuncID = 0;
    for ( i= 0; i< TASK_NUM ; i++)
    {
        Task_Delay_Count[i] = Task_Skip_Count[i] = 0;
        TaskFunc[i] = TaskResumeFunc[i] = Dummy;
    }
    TaskFunc[1] = Task1;
    TaskFunc[2] = Task2;
}

void Dummy(void)
{
    return;
}

void Task_Set_Next(void (*p_func)(void))
{
    TaskFunc[FuncID]=p_func;
}

void Task_Set_Other_Task(unsigned char id, void (*p_func)(void))
{
    DIS_INTR();
    TaskFunc[id]=p_func;
    ENA_INTR();
}

void Task_Delay_Ms_Next(unsigned int n, void (*p_func)(void))
{
    if (! n ) n = 1;
    TaskResumeFunc[FuncID]=p_func;
    TaskFunc[FuncID] = Dummy; // to sleep
    DIS_INTR();
    Task_Delay_Count[FuncID] = n;
    ENA_INTR();
}

static void Dummy_Skip(void)
{
    if ( Task_Skip_Count[FuncID] )
    {
        Task_Skip_Count[FuncID]--;
        if (! Task_Skip_Count[FuncID]) TaskFunc[FuncID]=TaskResumeFunc[FuncID];
    }
}

void Task_Set_Skip_Next(unsigned int n, void (*p_func)(void))
{
    if (! n ) n = 1;
    TaskResumeFunc[FuncID]=p_func;
    TaskFunc[FuncID] = Dummy_Skip; // to sleep
    Task_Skip_Count[FuncID] = n;
}

// O.S functions end

// main loop
void main(void)
{
    INIT_DEVICE();
    InitSystem();
    while (1)
    {
        register void (*Current_Func)(void);
        DIS_INTR();
        Current_Func = TaskFunc[FuncID]; // may break with interrupt
        ENA_INTR();
        // Trace System Code maybe add here
        Current_Func();
        if( ++FuncID >= TASK_NUM ) FuncID = 0;
    }
}

void TimeTick(void)  // This is timer interrupt
{
    unsigned char i;

    for ( i=0; i< TASK_NUM; i++)
    {
        if ( Task_Delay_Count[i] )
        {
            Task_Delay_Count[i]--;
            if (! Task_Delay_Count[i]) TaskFunc[i]=TaskResumeFunc[i]; // wake up function
        }
    }
}


給其他C檔案用的引入檔mt.h
extern void Task_Set_Next(void (*p_func)(void));
extern void Task_Set_Other_Task(unsigned char id, void (*p_func)(void));
extern void Task_Delay_Ms_Next(unsigned int n, void (*p_func)(void));
extern void Task_Set_Skip_Next(unsigned int n, void (*p_func)(void));


Windows下模擬用程式
#include <windows.h>
#include <winbase.h>

extern void TimeTick(void);

HANDLE              OSSemaphore;
DWORD WINAPI OSTickW32 ( LPVOID lpParameter );
HANDLE              OSTick32Handle;

void SimInit(void)
{
    DWORD  dwID;
   
    OSSemaphore = CreateSemaphore( NULL, 1, 1, NULL );
    OSTick32Handle = CreateThread( NULL, 0, OSTickW32, 0, 0, &dwID );
    SetPriorityClass(OSTick32Handle,THREAD_PRIORITY_HIGHEST);
    SetThreadPriority(OSTick32Handle,THREAD_PRIORITY_HIGHEST);
}

DWORD WINAPI OSTickW32( LPVOID lpParameter )
{
    while(1)
    {
        TimeTick();
        Sleep(1);
    }
    return 0;
}

void OS_SLEEP(unsigned int count)
{
    Sleep(count);
}

void OS_INIT_CRITICAL(void)
{
    return;
}

void OS_ENTER_CRITICAL(void)
{
    WaitForSingleObject( OSSemaphore, INFINITE );
}

void OS_EXIT_CRITICAL(void)
{
    ReleaseSemaphore( OSSemaphore, 1, NULL );
}


測試用程式
#include <stdio.h>
#include "mt.h"
void Task1(void)
{
    printf("G");
    Task_Delay_Ms_Next(100,Task1);
}

void Task2(void)
{
    printf("z");
    Task_Delay_Ms_Next(33,Task2);
}


測試畫面

要記得請使用多核心下去跑,因為本來就不是給Windows用的,所以會用掉一個核心。

2011年8月20日 星期六

Flash Forth的運行原理

因為使用到PIC,於是看看有無Forth可用,於是找到Flash Forth。
另一個目標是找Cortex M3上的Forth,結果找到一個分散式的Forth,PC端是用TCL寫的,TCL很不熟,就先放著。

比較令我感興趣的是PIC18上的Flash Forth。
雖然丁陳老師的eForth有做在PIC17上。但無法做新加程式,因為無法將程式寫回Flash。
但Flash Forth在PIC18上可以將程式寫回Flash。
因為MCU上我比較少做這個動作,所以沒有想到其實可以寫回Flash,就可以Run完整的Forth。
看了一下,就把回寫的地方改為燒回Flash的函式。
雖然寫回Flash是很慢,但人的反應也不快,所以不會感覺到。
只要Run的夠快就好了。

有去比對其他Forth,MSP430上的CamelForth也有這樣的能力。
不過作者有發現一個問題,就是CREATE> 指令受到影響。
而改以另一個指令去取代。

還是比較希望有Flash Forth在Cortex M3上面。目前還沒有看到。
Bee是知道FIG Taiwan的Holi有ARM7 Forth。不過他的堆疊成長方向和Cortex M3相反,要移反而麻煩。
只能說是Holi選錯了方向,因為ARM7確實二個方向都可以。
不過Cortex M3因為要精簡,就變成單向。而且組合語言指令有16 bit及32 bit混合,不像ARM7固定在32 bit上。
這些改變都要去動Forth核心動作。重寫可能還比改寫快。


五週五顆MCU

新工作上先做一些支援性任務,結果上班五週,就遇到五顆MCU。
分別是PIC18、PIC33、STM32F103、STM32F207以及待寫的STM32F100。
後三個是ARM Cortex M3,理論上會比較好適應,不幸的是這三個是分開的在二個任務上。

現代MCU和以前的不同,I/O型態多,不好設定,花最多時間在讀規格及設定成需要的I/O。
以前是很喜歡去看不同的MCU及CPU資料,不過這五週還真的有點吃不消。
不過可以看到一個趨勢,Cortex M3真的很便宜,PIC完全不是對手。
所以新設計會採用ARM是無法避免的。
本來還想說不是還有Cortex M0,問題是M3不到1美元。那還用M0做什麼?

其實還有遇到第六個MCU,因為是全組合語言寫的,且是已停產的日系MCU。就說要適應要一個月。
所以就沒再去碰了。

在這裡可以見到使用C語言是必然的。
現在MCU便宜到不行,用組合語言會被特定MCU綁住,就無法升級。
程式也是一種資產,但沒有製造成本。
全組合語言的程式很難移植,最後只好用新產品去頂掉,程式就只好用C重寫一次。
變成不做也不行的事了。

另外還有一些PC上做出來的人工智慧的程式要移進MCU中。
在沒有作業系統的幫助下,在PC上開發程式的人吃了不少苦頭。
所以我又架了RTOS做為準備工作。
這次選了FreeRTOS。有去追一下核心,設計方式和uCOS-II並不同。
不過國內沒有什麼人在用FreeRTOS,所以也没有什麼中文資料。

充實的五週,不過還沒有結束。只能一件一件事解開來。


2011年7月24日 星期日

VS2010追蹤MCU的C程式

換公司後都是用未曾用過的MCU。
這時追程式能力很重要。

除了前公司用過Source Insight外。
Bee更喜歡用Visual Studio 2010。
追MCU程式碼只要用到Express版本就剛好,是免費的。

因為不只能靜態追碼,還可以做以下動作:
1. 使用X86的除錯器,可以做到動態追碼,就是模擬執行。
2. 在執行中,可以修改程式碼。

不過要使用,有二個問題要解決。
1. 組合語言對應程式碼。
   大部分是設定MCU,對X86沒有用。
   只是去補欠缺的函式。
2. 標頭檔不同
   函式庫位置。以及二個編譯器之間的不同。


都搞定後,就可以在VS2010上面編出exe檔。
這是在X86下可以執行的。

用Debuger去追,只要遇IO就會產生記憶體存取錯誤。
沒關係,直接修改。導向一個記憶體區。
然後就繼續執行。

都改好了,就可以發現while跑完了一圈。    
去修改假IO用的記憶體區,就可以進入程式內部再追下去。

這是Bee面對大型Firmware程式用的方法。