久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 6334|回復: 0
打印 上一主題 下一主題
收起左側(cè)

STM8應用,如何寫出強健的程序

[復制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:71922 發(fā)表于 2015-1-10 02:28 | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式
        下文純屬個人見解,如有錯誤的地方請及時指正,也希望提出好的建議,在這里拋磚引玉了。
                實現(xiàn) 需求很簡單。做好卻很難,沒有最好的程序。在滿足需求的情況下,如何讓程序健壯、良好可擴展性是一個程序員要考慮的事情。好的辦法是反復實驗、修改得來的。廢話不多說,上硬菜。
            這里以我家臺燈為例,功能需求很簡單,可以通過旋鈕,及紅外遙控控制燈光強弱及開關(guān)狀態(tài),遙控優(yōu)先級高于旋鈕。我是利用  AIN0 采集旋鈕的電壓值,TIM2_CH1 捕獲紅外信號 ,TIM1_CH1 輸出PWM 控制燈光  ,TLI 監(jiān)控電壓狀態(tài)   ,內(nèi)部FLASH存儲數(shù)據(jù)。單片機 105K4   內(nèi)部16M晶振。
            程序的主函數(shù)部分   :

#include<stm8s105k4.h>#include"ad105.h"        //旋鈕電壓采集頭文件
#include"deng.h"           //燈光控制頭文件
#include"irdate.h"        //紅外采集頭文件
#include"eeprom.h"    //數(shù)據(jù)存儲頭文件,包含TLI電壓檢測
#include"keys.h"        //紅外解碼值轉(zhuǎn)換為按鍵頭文件


////////////////////////////////////////////////////
void  TIM3_INIT(void)  //定時器初始化 程序基本運行定時器,所有工作指示都靠這個定時器指定
{  
        
TIM3_EGR=0X01;               //產(chǎn)生更新事件
        TIM3_PSCR=0X07;            //128分頻
        TIM3_ARRH=0X04;            //1250    重裝載值
        TIM3_ARRL=0Xe2;            //1250   重裝載值
        TIM3_CR1=0X01;              //開定時器
        TIM3_IER=0X01;                //使能更新中斷
}
void  DOG_Init(void)
{
        IWDG_KR=0XCC;    //使能看門狗
        IWDG_KR=0X55;                        //解鎖,PR  RLR 賦值
        IWDG_PR=0X06;                        //分頻  賦值
        IWDG_RLR=0XFF;                //重裝值
        IWDG_KR=0XAA;                        //重新加載
}
void Feed_DOG(void)
{
        IWDG_KR=0XAA;                        //重新加載 ,喂狗
}
void  clock_config(void)
{
        CLK_CKDIVR=0X00;//CPU0分頻
}
main()
{
        clock_config();            //時鐘初始化
       
        TIM1_CH1PWM();    //燈管控制初始化
        TIM3_INIT();                //基本運行定時器初始化
        TIM2_CH3_Init();        //紅外捕獲初始化
        adinit(0);                       //旋鈕電壓采集初始化
        DOG_Init();                //看門狗初始化
        _asm("rim");                //開總中斷
        while (1)
        {
                adzread();            //旋鈕電壓值讀取
                deng_pwm();                //燈光控制
                getzudate();            //紅外解碼
                keysirrun();            //按鍵處理
        }
}
@far @interrupt void TIM3_UP (void)            //基本運行定時器
{
        static  unsigned  char  t200ms=0,t1s=0;
        TIM3_SR1=0X00;                                    //清標志位
        /////////////////////////add   10ms   flag
        /////////////////////////
        t200ms++;
        if(t200ms>9)
        {
                t200ms=0;
                /////////////////////////add   200ms   flag
                adstartflag=1;                                    //電壓采集標志位
                /////////////////////////
                t1s++;
                if(t1s>4)
                {
                        t1s=0;
                        /////////////////////////add   1s   flag
                        Feed_DOG();                            //喂看門狗
                        /////////////////////////
                }
        }
}


       你可能說有啥問題,這個小項目,對如何提高單片機的執(zhí)行效率方面的代碼不是很多。首先我說一下我的代碼風格。
我的程序都有一個程序運行基本定時器,保證程序的基本運行,它決定程序的運行方向。就是上面的定時器3。這個定時器的作用是,合理的管理要執(zhí)行的任務,避免CPU 的浪費,提高吞吐率.我大致分為三個時間段,10ms  級別 ,200ms級別,1s級別。對應的下邊是待處理函數(shù)的標志位,中斷是把雙刃劍,時效性高,過度利用中斷會加重CPU的工作量,很大一部分時間浪費在進入中斷及跳出中斷的路上。這樣一來,在頻繁、多個中斷同時到達時,中斷的的時效性反而不如不用中斷了。我習慣在中斷里邊只處理標志位的狀態(tài),在大循環(huán)里面運行內(nèi)嵌標志位的實時函數(shù)。這樣,在多中斷,大工作量的情況下,程序也跑的很輕松。我會在講解完本歷程后貼出一個按鍵檢測小程序,你就會感覺這樣做的優(yōu)點。
不難發(fā)現(xiàn),在While  里面有一下幾個函數(shù)
               
adzread();            //旋鈕電壓值讀取                deng_pwm();                //燈光控制
                getzudate();            //紅外解碼
                keysirrun();            //按鍵處理
旋鈕電壓采集函數(shù),電壓采集函數(shù)內(nèi)部有個允許電壓采集標志位,
adstartflag  只有這個標志位被至位后,才運行函數(shù)內(nèi)部程序,且運行完后將標志為復位,等待下次標志位置位。在定時器200MS下將這個標志位每200MS 置位一次
if(t200ms>9)
        {
                t200ms=0;
                /////////////////////////add   200ms   flag
                adstartflag=1;                                    //電壓采集標志位
        }
你可能又會說,為啥只有這么一個標志位,這樣有啥用,上邊說了本歷程相關(guān)代碼較少,是因為其他函數(shù)不需要有時間的感念,它們都是基于,旋鈕電壓的變化、及紅外捕獲值存在,而變化,不需要時間決定它們的運行狀態(tài)。當程序龐大,關(guān)系繁瑣復雜時你會體會到這種寫法的優(yōu)點。
看門狗每1s喂一次,這里不多說。
        下面分別說每個頭文件下的函數(shù)
#include<stm8s105k4.h>
#include"eeprom.h"
#include"ad105.h"
_Bool adstartflag=0;        //標志位


unsigned  short  adz1, adz2;
unsigned   char   adi=0;
void   adgpioinit(void)
{
        PB_ODR&=0XFE;
        PB_DDR&=0XFE;
        PB_CR1&=0XFE;
        PB_CR2&0XFE;
}
void   adinit(unsigned   char  ch)
{
        adgpioinit();
        ADC_CSR &=0xf0;
        ADC_CSR|= ch;   //通道         
        ADC_CR1=0X01;
        ADC_CR2=0X30;
}
unsigned   short  readadz(void)
{
        unsigned   short   adz=0;
        ADC_CR1|=0x01;
        while(!(ADC_CSR & 0x80)); // 等待ADC結(jié)束
        adz=ADC_DRH;adz<<=2;adz+=ADC_DRL;
   return (adz);   
}
void   adzread(void)
{
        if(adstartflag)                        //是否可以采集旋鈕電壓
        {
//                if(adi>4)
                if(adendlag==0){            //沒有處于紅外控制狀態(tài)下
                adz=readadz();
//                adi=0;       
                pwmstartflag=1;}                //燈光控制允許變化。這里也可以對電壓值比較有變化時允許燈光變化
//                if(((adz1+10)>adz)&&((adz1-10)<adz)) adi++; //判斷當前狀態(tài)
//                        else  adi=5;//狀態(tài)不穩(wěn)定時
//                adz1=adz;
//被注釋掉的地方是為在穩(wěn)定狀態(tài)下降低旋鈕電壓檢測次數(shù),在不講求功耗的時候沒有太大必要。

                adstartflag=0;
        }
}
每個頭文件下總有那么1-2個標志位。號召它們旗下的小兵們。這個就這么簡單,速度過。

#include<stm8s105k4.h>
#include"deng.h"
#include"eeprom.h"
void   TIM1_CH1PWM(void)
{
        TIM1_CR1=0X00;
        TIM1_CCMR1=0X68;
        TIM1_CCER1=0X01;
        TIM1_ARRH=0X04;        //旋鈕電壓采集滿量程值,
        TIM1_ARRL=0X00;
       
        TIM1_PSCRH=0X00;
        TIM1_PSCRL=0X00;    /0分頻,讓PWM頻率最大
       
        TIM1_BKR=0X80;                    //注意這個別忘了   
        TIM1_CR1=0X81;
}
void  T1_CH1PWMT(unsigned  short date)
{
        TIM1_CCR1H=date/256;
        TIM1_CCR1L=date%256;
}
void   deng_pwm(void)
{
        if(pwmstartflag)                        //是否可以控制更改燈光輸出
        {
                T1_CH1PWMT(adz);        //賦值
                pwmstartflag=0;            //    清除標志位
        }
}
也很簡單   過   。。。。。。。。。。。。。。

#include<stm8s105k4.h>
#include"irdate.h"
#include"eeprom.h"
//////////14 03 27  張小強
#define   tim2_ccr3 (TIM2_CCR1H*256+TIM2_CCR1L)   //捕獲值
#define   tim2_ovfz  (0xffff)   //預設(shè)值
//#define   tim2_ovfz  (0x7a10)   //預設(shè)值
//更換遙控器需要修改的的數(shù)據(jù)
#define   learnadd   0     //是否學習地址
#define   learndate  0     //是否學習數(shù)據(jù)
//////////////////////////////////////////////////
#define   irstartH 255         //引導嗎最大值
#define   irstartL 95        //引導嗎最小值
#define   irstopH 95        //停止碼最大值
#define   irstopL 75        //停止碼最小值
#define   irhH                 21        //高電平最大值
#define   irhL                 15        //高電平最小值
#define   irlH                 12        //低電平最大值
#define   irlL                 6        //低電平最小值
///////////////////////////////////////////////
//所用變量
unsigned  char  ovfflag=0;//溢出標志  實際捕獲值 輔助計算值
unsigned  char  zu[33]={0};    //捕獲臨時存放的捕獲時間
unsigned   long irzu=0;        //解出得遙控碼包含地址 數(shù)據(jù) 及各自的反碼

unsigned   char   irflag=0x01;                        //捕獲值進入
unsigned   char  irz=0,tempfalg=0;//得到的鍵值與次數(shù)
_Bool IRled @PD_ODR:1;
/////////////////////////////////////////////
#if learndate ==1
        unsigned  char   irzu1[50]={0},irzu1i=0;
#endif
#if  learnadd==1
        unsigned   char  iradd=0 , iraddf=0;

unsigned  char *iraddr; //= (unsigned char *)0x4000;   //
void  miyao(void)
{
        do
        {
                FLASH_DUKR = 0xae;             // 寫入第一個密鑰
                FLASH_DUKR = 0x56;             // 寫入第二個密鑰
        }
        while((FLASH_IAPSR & 0x08) == 0);  
}
void   irwrite(unsigned short add,unsigned  char  date  )
{
                miyao();
        iraddr=(unsigned char *)add;
        *iraddr = date;
        while((FLASH_IAPSR & 0x04) == 0);
        }
     // 等待寫操作成功
unsigned  char  irreadadd(unsigned short add)
        {
                iraddr=(unsigned char *)add;
                return (*iraddr);
               
        }
#else
        #define   iradd    0x38                                        //遙控器地址
        #define   iraddf   0xc7                                        //遙控器地址反碼       
#endif

////////////////////////////////////////////

void   TIM2_CH3_Init(void)
{
        TIM2_CR1=0X00;
        TIM2_PSCR=0X0b;
        TIM2_EGR=0X03;
       
        TIM2_ARRH=0Xff;
        TIM2_ARRL=0Xff;       
        TIM2_CCMR1=0Xa1;       
        TIM2_CCER1=0X03;
        TIM2_IER|=0X03;
        TIM2_CR1|=0X01;

                PD_CR1|=0X10;
        PD_DDR&=0XEF;
        PD_CR2&=0XEF;
        PD_ODR&=0XEF;

        irled_config();
       
        #if  learnadd==1
        iradd=irreadadd(0x40a0);
        iraddf=irreadadd(0x40a1);
        #endif
}
void   getirdate(void)
{
        static  unsigned char  tim2_ch3date=0,tim2_ccr3c=0,iri=0;
        if(ovfflag==0)
        {
                tim2_ch3date=tim2_ccr3-tim2_ccr3c;
                tim2_ccr3c=tim2_ccr3;
        }
        else
        {
               
                tim2_ch3date=((tim2_ovfz -tim2_ccr3c)+tim2_ccr3);
                ovfflag=0;
        }
        if((tim2_ch3date>irstartL)&&(tim2_ch3date<irstartH)){iri=0;irflag=0x05;}
       
        if((tim2_ch3date>irlL)&&(tim2_ch3date<irhH)&&(irflag==0x05))
        {
                zu[iri++]=tim2_ch3date;
                if(iri>31)irflag=0x0a;
        }
        if((tim2_ch3date>irstopL)&&(tim2_ch3date<irstopH)){iri=0;irflag=0x0a;}
        if(iri>32)iri=0;
        #if learndate ==1
                irzu1[irzu1i++]=tim2_ch3date;
                if(irzu1i>49)irzu1i=0;
        #endif
}
void    restzu(void)
{
        static  unsigned  char  rzi=0;
        for(rzi=0;rzi<32;rzi++)
        {
                zu[rzi]=0;
        }
}
void   irtimeupflag(void)
{
        ovfflag=1;
}
void   getzudate(void)
{
        static   unsigned  char iri1=0,z1=0,z2=0,z3=0,z4=0,z5=0;
#if  learnadd==1       
static  unsigned  char add1=0,add2=0,add3=0,add4=0,add5=0,add6=0;
#endif
        if(irflag==0x0a)
        {
                for(iri1=32;iri1>0;iri1--)
                {
                        if((zu[iri1]>irhL)&&(zu[iri1]<irhH)){irzu|=0x01;}
                        irzu<<=1;
                }
                z1=irzu;z2=(irzu>>8);z3=(~(irzu>>24));z4=(irzu>>16);
               
                if(((z1==iradd )&&(z2==iraddf)))
                {
                        if(z3==z4)
                        {
                        switch(z4)
                                {
                                        case 3: irz=10;  irokflag=1;                 break;       //有效按鍵  進入按鍵待處理狀態(tài)
                                        case 9: irz=11;  irokflag=1;                 break;
                                        case 14:irz=12;  irokflag=1;          break;
                                        case 26:irz=13;  irokflag=1;                break;
                                }
                                if((z5==z4)&&(irokflag)){tempfalg++;}            //連按情況下      
                                else  tempfalg=0;
                                z5=z4;

                                restzu();
                                


                        //        z1=0;z2=0;z3=0;z4=0;
                        }
                }
                #if learnadd==1                       
                        else
                {
                        add5=add3;add6=add4;add3=add1;add4=add2;add1=z1;add2=z2;
                                if((add5==add1)&&(add6==add2))
                                {iradd=add1;iraddf=add2;irwrite(0x40a0,add1);irwrite(0x40a1,add2);}
                }//地址學習功能
        #endif
                irflag=0;
        }
}
@far @interrupt void TIM2_CC (void)
{
        TIM2_SR1&=0XFd;
        TIM2_SR2&=0xfd;
        getirdate();
}
@far @interrupt void TIM2_UP (void)
{
        TIM2_SR1&=0XFE;
        irtimeupflag();
                #if learndate ==1
        irzu1i=0;
        #endif
}

上面是紅外解碼部分,原理是,捕獲的一組時間段,放到數(shù)組中,接收完畢后判斷數(shù)組的內(nèi)容,解出紅外值,對應有,地址學習  功能,數(shù)據(jù)分析功能。方便在更換遙控器調(diào)試使用。這些代碼需要耐心看,首先要成功捕獲數(shù)據(jù),以后就順理成章了。功能可以做的跟好。這里只是粗略的寫寫,有興趣的可以慢慢研究。


#include<stm8s105k4.h>
#include"keys.h"
#include"eeprom.h"
#include"irdate.h"
_Bool pwmen@TIM1_CCER1:0;
void   keysirrun(void)
{
        if(irokflag)                                        //是否有有效遙控解碼值
        {
                if(irz==11)                                    //進入遙控控制模式,關(guān)閉旋鈕電壓采集功能
                {
                        adendlag=!adendlag;
                        adz=(adz-(adz%10));
                }
                if(adendlag)                                //在遙控模式下
                {
                        if(irz==12)
                        {
                                if(adz<700)adz+=10;pwmstartflag=1;        //更改燈光強度   ,使能燈光變化
                        }
                        if(irz==13)
                        {
                                if(adz>0)adz-=10;pwmstartflag=1;
                        }
                }
                if(irz==10)
                {
                        pwmen=!pwmen;                        //開關(guān)燈光      
                }       
                irokflag=0;
        }
}

//////////內(nèi)容很少有效指令后處理對應的動作。

#include<stm8s105k4.h>
#include"eeprom.h"
_Bool ledevent @PD_ODR:1;//事件指示燈
_Bool  eventledflag=0;
unsigned  char ledi=0;
/////////////////////////////自身變量
unsigned  char  eeflag=0;
////////////////////////////////////外部變量
unsigned   short   shiji=0;

unsigned  char  shanflag=0,shflag=0,disflag=0;
//////////////////////////////////
void   eepromEN(void)
{
        eeflag=1;
}
unsigned  char *addr; //= (unsigned char *)0x4000;   //
void  miyao(void)
{
        do
        {
                FLASH_DUKR = 0xae;             // 寫入第一個密鑰
                FLASH_DUKR = 0x56;             // 寫入第二個密鑰
        }
        while((FLASH_IAPSR & 0x08) == 0);  
}
void   write(unsigned short add,unsigned  char  date  )
{
                miyao();
        addr=(unsigned char *)add;
        *addr = date;
        while((FLASH_IAPSR & 0x04) == 0);
        }
     // 等待寫操作成功
unsigned  char  readadd(unsigned short add)
        {
                addr=(unsigned char *)add;
                return (*addr);
               
        }
       
        void   eventledgpio(void)
{
        ledevent=1;
        PD_DDR|=0X02;
        PD_CR1|=0Xfd;
       
}
void   eventleden(void)
{
        eventledflag=1;//   set  ventlede  flag
        ledevent=0;                        //     open  led
}
void    eventledable(void)
{
                if(eventledflag)
                {
                        ledi++;
                        if(ledi>1)                // 30ms  
                        {
                                ledi=0;
                                eventledflag=0;  //reset   ventlede  flag
                                ledevent=1;                                //close led
                        }
                }
}
void   TLI_init(void)
{
        PD_DDR&=0X7F;
        PD_CR2|=0X80;
        EXTI_CR2|=0X20;
        eventledgpio();
        eepromEN();
}
void   readeeprom(void)
{

        adendlag=readadd(0x4000);
}
@far @interrupt void TLI_PD7(void)
{
        if(eeflag==1)
        {

                write(0x4000, adendlag);
       
        }
}
//




在電壓下降到認為發(fā)生掉電時,觸發(fā)TLI中斷,將需要保存的數(shù)據(jù)保存到FLASH內(nèi)部,等待下次運行用。這里就一個數(shù)據(jù)需要存儲。






按鍵小程序駕到
#include<stm8s105k4.h>
#include"eeprom.h"
#include"key.h"


_Bool keystartflag=0;


#define keyc02  0x02
#define keyc04  0x04
#define keyc08  0x08
#define keyc10  0x10
#define   keygc  PC_IDR&0x1e  //宏定義控制


#define keyd01  0x01
#define keyd04  0x04
#define keyd08  0x08
#define   keygd  PD_IDR&0x0d  //宏定義控制
//端口c
unsigned  char  c10g=0,c20g=0,c40g=0,c80g=0;
_Bool c10kflag=0,c20kflag=0,c40kflag=0,c80kflag=0;
unsigned  char  c20cg=0;   //長按
_Bool  c20cflag=0;       


//端口d
unsigned  char d20g=0,d40g=0,d80g=0;
_Bool  d40kflag=0,d20kflag=0,d80kflag=0;


void   keyinit(void)
{
        PC_DDR&=0Xe1;
        PC_CR1|=0X1e;
        PD_DDR&=0Xf2;
        PD_CR1|=0X0d;
}
void  keysjian(unsigned char k1t,unsigned char k2t,unsigned char k3t,unsigned char k4t,unsigned char k5t,unsigned char  k6t,unsigned char k7t)
{


        if(keystartflag)
        {
                eventledable();   //進過30ms關(guān)閉led  事件指示燈
                //端口c
                if((keygc&keyc10)==0x00){if(c10kflag==0){c10g++;if(c10g>k1t){c10kflag=1;eventleden();    動作    }}}
                else {c10kflag=0;c10g=0;}
                if((keygc&keyc08)==0x00){if(c20kflag==0){c20g++;if(c20g>k2t){c20kflag=1;eventleden();動作    }}}
                else {c20kflag=0;c20g=0;}
                if((keygc&keyc04)==0x00){if(c40kflag==0){c40g++;if(c40g>k3t){c40kflag=1;eventleden();動作    }}}
                else {c40kflag=0;c40g=0;}
                if((keygc&keyc02)==0x00){if(c80kflag==0){c80g++;if(c80g>k4t){c80kflag=1;eventleden();動作    }}}
                else {c80kflag=0;c80g=0;}
               
                if((keygd&keyd01)==0) {if(d20kflag==0){d20g++;if(d20g>k5t){d20kflag=1;eventleden();動作    }}}
                else {d20kflag=0;d20g=0;}
               
                if((keygd&keyd04)==0) {if(d40kflag==0){d40g++;if(d40g>k6t){d40kflag=1;eventleden();動作    }}}
                else {d40kflag=0;d40g=0;}


                if((keygd&keyd08)==0x00){if(d80kflag==0){d80g++;if(d80g>k7t){d80kflag=1;eventleden();動作    }}}
                else {d80kflag=0;d80g=0;}
               
                keystartflag=0;
        }
       
}

將標志位放到10MS下,調(diào)用函數(shù)   keysjian(1,2,10,50,100,200,250);   參數(shù)是承認該按鍵的時間,可更換有關(guān)變量類型加大選擇時間的寬度,這里只寫了按下的檢測函數(shù),可以很容易的更改 為松開后   、長按  、短按   、組合 、等檢測方式,很有趣,這個需要讀者理解,才能為己所用,我多說無益。重要是領(lǐng)悟編程方法    。








程序員萬萬不能懶。



下次再會      張小強于2014 06 10 23 02

分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏 分享淘帖 頂 踩
回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規(guī)則

手機版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機教程網(wǎng)

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 日日干天天操 | 青草青草久热精品视频在线观看 | 国产在线精品一区二区 | 在线国产中文字幕 | 日韩一区二区三区av | 在线观看av网站永久 | 中文一区二区 | 99精品视频在线观看免费播放 | 黄色免费在线网址 | 91在线影院 | 国产亚洲一区二区三区 | 欧洲毛片| 99精品电影| 日本特黄a级高清免费大片 成年人黄色小视频 | 国产成人免费 | 成人免费淫片aa视频免费 | 精品国产一区二区三区久久久久久 | 久久精品亚洲精品国产欧美 | 国产日韩欧美一区二区在线播放 | 一区二区三区在线播放 | 国产欧美精品区一区二区三区 | 午夜精品91 | 亚洲国产精品91 | 91精品国产91久久久久久不卞 | 国产91精品久久久久久久网曝门 | 中文字幕国产一区 | 欧美黄色一级毛片 | av黄色国产 | 日韩a | 在线观看免费av片 | 国产精品久久久久久久7电影 | 九色www | 欧美视频一区 | 国产精品视频不卡 | 午夜伦理影院 | 天堂网avav | 久久噜噜噜精品国产亚洲综合 | 日本黄色大片免费 | 日韩一区二区福利 | 国产日韩欧美 | 综合色站导航 |