//-------------------------------------------------------------------------*
//                   AW60 功能演示程序底端设计                             *
//                          2010/09/10                                     *
//                         详见设计文档                                    *
//                苏州大学飞思卡尔嵌入式仿真研究所                         *
//-------------------------------------------------------------------------*

#include "Includes.h"

                                                           //具体非全局类的宏

/* 函数原型声明 */
                                                           
uint8 OperateCodeRecognise();                              //用于识别PC机发来的命令，形如0x00 0x00 0x00 code 0xFF 0xFF 0xFF.返回code，否则返回0xFF                                                          

void LedShowNumCount( uint16 num );                        //用于4段LED的亮灯显示，从0000->num开始计数.从零开始，num为计数值，最大为9999

void ScreenShowStr(uint8 data[]);                          //用于在LCD屏幕上显示从PC获得的字符串，为32个字符

void LightChangONandOFF();                                 //小灯不停地闪烁

void PwmControlLight();                                    //从PC接受占空比，利用pwm波形控制小灯

void SavePictoFlash( uint16 addr);                         //保存图片到flash的指定控件

void ReadPicfromFlash( uint16 addr );                      //从指定地址开始读取图片文件


/* end 函数原型声明结束 */
                                                           //全局变量定义
uint8 cmd_from_pc;                                         //用于存放解析完的正确指令
uint8 StopFlag = CODE_FAILED;                              //停止标志位，如果StopFlag为1，停止。主要用于在需要的时候，跳出执行的程序。
       
uint8 ResultCommand[7];                                    //用于存放构造的命令字段,用于向PC机放松的字段
uint8 receive_data_screen[32];                             //用于接收液晶屏显示的32个字符

void main(void)
{ 
    DisableInterrupt();                                    //禁止总中断
    
    /* 模块初始化 */                                       //建议中断类初始化可以在这边，其他直接用直接初始化的设备到使用的地方初始化，避免干扰
    
    MCUInit();                                             //芯片初始化
    
    SCIInit( SCI_NUM,  SYSTEM_CLOCK, SCI_BAULD_RATE );     //串口初始化,宏参数定义于sci.h中 
                                                  
    Light_Init( Light_Run_PORT, Light_Run, Light_OFF );    //小灯初始化,宏参数定义于light.h中
    
    LEDinit();                                             //LED初始化            
                                            
    IRQ_Init();                                            //IRQ初始化
       
    TPMChInit(TIMER_NUM_2,TIMER2_CHANNEL_BZ_NUM);          //定时器2通道X初始化,用于开始演示软件界面    * * *
    
    KBInit();                                              //键盘初始化
            
    /* end 模块初始化结束 */
      
    EnableTPM2ChInInt(TIMER2_CHANNEL_BZ_NUM);              //允许定时器2通道x的输入捕捉中断
    
    EnableInterrupt();                                     //开放总中断    
   
    while(1)                                               
    {     
        cmd_from_pc = OperateCodeRecognise();              //获取操作码，实现不同操作
        
        if( cmd_from_pc != CODE_FAILED )                   //0xFF说明未能正确解析操作指令
        {            
            switch( cmd_from_pc )
            {
                /* 小灯控制块 */ 
                case CODE_LIGHT_OPEN:                      //小灯控制，打开
                    Light_Control(Light_Run_PORT,Light_Run,Light_ON);
                                                           //组成命令字
                    MakeCommand(CODE_FAILED,CODE_SUCCESSED,
                                CODE_LIGHT_OPEN,           //开灯码
                                ResultCommand);
                    SCISendN(SCI_NUM,7,ResultCommand);     //发送确认信息 
                    break;
                    
                case CODE_LIGHT_SHUTDOWN:                  //小灯控制，关闭
                    Light_Control(Light_Run_PORT,Light_Run,Light_OFF);
                                                           //组成命令字
                    MakeCommand(CODE_FAILED,CODE_SUCCESSED,
                                CODE_LIGHT_SHUTDOWN,       //关灯码
                                ResultCommand);
                    SCISendN(SCI_NUM,7,ResultCommand);     //发送确认信息
                    break;
                    
                case CODE_LIGHT_CHANGE:                    //小灯控制，闪烁
                    EnableSCIReInt();                      //开放串口接受中断,用于接收来自PC机的停机指令
                    LightChangONandOFF();                  //不停闪烁，直到停止命令的到来.并发送确认字段
                    DisableSCIReInt();                     //关闭串口接收中断，
                    break;
                /* end 小灯控制块 */        
                    
   
                case CODE_FLASH_CONTROL:
                    break;
                    
                /* LED控制块 */    
                case CODE_LED_COUNT_TO_NUM:                //控制LED进行数字显示
                    EnableSCIReInt();                      //开放串口接受中断
                    LedShowNumCount( NUM_FOR_LED_COUNT );
                    DisableSCIReInt();                     //关闭串口接收中断，千万不能忘记，调试的时候曾出问题
                    break; 
                /* end LED控制块 */
                
                
                    
                /* LCD液晶屏幕操作 */
                case CODE_LCD_SHOW_STR:                    //控制LCD显示用户输入的字符
                    /*EnableSCIReInt(); */                 //开放串口接受中断,不可以加，否则会干扰！不加已经可以满足需求了
                                                           //组成确认字段
                    MakeCommand(CODE_FAILED,CODE_SUCCESSED,CODE_LCD_SHOW_STR,ResultCommand);
                    SCISendN(SCI_NUM,7,ResultCommand);     //发送确认信息
                    ScreenShowStr(receive_data_screen);     
                    /*DisableSCIReInt(); */                //关闭串口接收中断
                    break; 
                case CODE_LCD_CLEAR:
                    MakeCommand(CODE_FAILED,CODE_SUCCESSED,
                                CODE_LCD_CLEAR,            //清屏命令
                                ResultCommand);
                    SCISendN(SCI_NUM,7,ResultCommand);     //发送确认信息 
                    Delay(1);                              //延时1s，有干扰
                    LCDinit();                             //LCD重新初始化，清屏
                    break;
                /* end LCD液晶屏幕操作 */   
                
                
                
                /* 键盘操作块 */  
                case CODE_KEYBOARD_RECOGNIZE:
                    EnableKBint();                         //打开键盘中断，等待键盘操作
                                                           //组成命令字
                    MakeCommand(CODE_FAILED,CODE_SUCCESSED,
                                CODE_KEYBOARD_RECOGNIZE,   //开键盘中断
                                ResultCommand);
                    SCISendN(SCI_NUM,7,ResultCommand);     //发送确认信息 
                    break;
                case CODE_KEYBOARD_RECOGNIZE_CLOSE:
                    DisableKBint();                        //关闭键盘中断
                                                           //组成命令字
                    MakeCommand(CODE_FAILED,CODE_SUCCESSED,
                                                           //关键盘中断
                                CODE_KEYBOARD_RECOGNIZE_CLOSE,   
                                ResultCommand);
                    SCISendN(SCI_NUM,7,ResultCommand);     //发送确认信息
                    break;
                /* end 键盘操作块 */   
                
                   
                
                /* 定时器操作块 */
                case CODE_TIMER_COUNT:
                    TPMinit(TIMER_NUM_1);                  //初始化定时器1，准备用于 1s 计数. 注意，不要在最上面先初始化，调试的时候，发现初始化完没用，运行中的其他程序影响到它的相关标识。建议再用到的地方初始化
                                                           //这里先发送确认，因为如果先开定时器，则会有两条确认，调试后这样决定。不过这样也不能完全保证定时器打开，只是权衡方法
                    MakeCommand(CODE_FAILED,CODE_SUCCESSED,
                    CODE_TIMER_COUNT,                      //开定时器计数码
                    ResultCommand);
                    
                    SCISendN(SCI_NUM,7,ResultCommand);     //发送确认信息
                     
                    EnableTimer(TIMER_NUM_1);              //启动定时器溢出中断，开始计数
                                                           //组成命令字
                    break; 
                case CODE_TIMER_COUNT_CLOSE:
                    DisableTimer(TIMER_NUM_1);             //关闭定时器溢出中断，停止计数
                                                           //组成命令字
                    MakeCommand(CODE_FAILED,CODE_SUCCESSED,
                                CODE_TIMER_COUNT_CLOSE,    //关定时器计数码
                                ResultCommand);
                    SCISendN(SCI_NUM,7,ResultCommand);     //发送确认信息 
                    break;
                /* end 定时器操作块 */  
                
                
                /* ad采集 */
                case CODE_AD_MCU_TEMPERATURE:
                    ADCInit();
                                                           //组成确认字段
                    MakeCommand(CODE_FAILED,CODE_SUCCESSED,CODE_AD_MCU_TEMPERATURE,ResultCommand);
                    SCISendN(SCI_NUM,7,ResultCommand);     //发送确认信息   
                                                           //在通道26做A/D采集,200次均值滤波,串口发送均值滤波结果
                    SCISend1(SCI_NUM,ADCAve(AD_CHANNEL_26, 200));
                    break;     
                /* end ad采集 */
                
                
                /* pwm 模块 */
                case CODE_PWM_CONTROL_LIGHT_INIT:          //首先初始化半亮 50%
                    PWM( TIMER_NUM_2,                      //定时器号
                         TIMER2_CHANNEL_PWM_NUM,           //定时器2的用于pwm的通道号           
                         0x0138,                           //设定的周期 10ms
                         50 );                             //占空比 50%
                    break;
                case CODE_PWM_CONTROL_LIGHT:                   
                    PwmControlLight();                     //小灯实时控制，来自PC得占空比
                    break;
                case CODE_PWM_CONTROL_LIGHT_CLOSE:         //关闭小灯
                    PWM( TIMER_NUM_2,                      //定时器号
                         TIMER2_CHANNEL_PWM_NUM,           //定时器2的用于pwm的通道号           
                         0x0138,                           //设定的周期 10ms
                         0 );                              //占空比为0
                    break;
                /* end pwm 模块 */
                
                
                
                /* flash 控制块 */
                
                case CODE_FLASH_PIC_SAVE:
                    FlashInit();                           //falsh初始化，频率
                                                           //组成确认字段
                    MakeCommand(CODE_FAILED,CODE_SUCCESSED,CODE_FLASH_PIC_SAVE,ResultCommand);
                    SCISendN(SCI_NUM,7,ResultCommand);     //发送确认信息                  
                    SavePictoFlash( 0xA000 );                              
                    break;
                    
                case CODE_FLASH_PIC_READ:
                    FlashInit();
                    ReadPicfromFlash( 0xA000 );
                    break;
                
                /* end flash 控制块 */
                     
                default:                                   //如果命令为非法，或者为保留
                    SCISend1(SCI_NUM, CODE_FAILED);        //返回错误码0xFF
                    break;
            }  
        }       
    }
}

/*******************FUNCTION DEFINE ONLY FOR THIS FILE************************/



//-----------------------------------------------------------------------*
//函数名: OperateCodeRecognise                                           *
//功  能: 识别PC发来的命令帧，00 00 00 code FF FF FF( HEX)               * 
//参  数: 无                                                             *
//返  回: 如果识别正确返回识别的操作码CODE,否则返回0xFF                  *
//说  明: 暂时code为一位，如果需要识别多位的code码，修改result的大小     *
//        并修改与result相关的部分代码进行识别                           *
//-----------------------------------------------------------------------*
uint8 OperateCodeRecognise()
{
    uint8 m = 0,n = 0;                                     //USERD FOR COUNT.
    uint8 fp;                                              //RECEIVE FLAG
    uint8 temp;                                            //USERD FOR TEMP DATA FROM PC BY SCI
                                                           //uint8 codeflag = 0;// if codeflag = 1,program starts to receive operate code
    uint8 result[4];                                       //USERD FOR CODE + 0xFF+0xFF+0xFF    * * *

    while(1)
    {
        temp = SCIRe1( SCI_NUM, &fp );  
        ++m;
        
        if( fp == 1 )                                      //此处的1为模块返回值，如果要统一标准的话，最好用宏    * * *
        {
                                                           //receive failed and GO OUT
            m = 0;
            n = 0;
            return CODE_FAILED;
            goto OUT;
        }              
                                                           //SCISend1( SCI_NUM, 0xFF ); //test and return 6 0xFF 
        if( fp == 0 && temp == CODE_SUCCESSED )            //此处的0为模块返回值    * * *
        {
                                                           //SCISend1( SCI_NUM, OxFF ); //test and return 3 0xFF,because i must recognize 3 0x00
            ++n;  
            if( n == 3 && m == n )                         //if m == n, 3 0x00 is followed closely.Otherwise it is not fit.
            {     
                                                           //SCISend1( SCI_NUM, OxFF ); //test and return 1 0xFF
                m = 0;
                n = 0;  
                                                           //HERE,RECOGNIZE 3 0x00,THEN RECOGNIZE 3 0xFF。
                                                           //now it only can recognize 1 operate code.
                if( SCIReN( SCI_NUM, 4, result ) == 0 )    //    * * *
                {
                    if( result[1] == CODE_FAILED &&        //judged whether is it 3 0xFF.if true,Successed
                        result[2] == CODE_FAILED &&
                        result[3] == CODE_FAILED   )
                    {
                        return result[0];                  //true operete code.recognize successed   
                    }
                    else                                   //if failed,restart and reinit the m,n for count
                    {
                        m = 0;
                        n = 0;
                        return CODE_FAILED;
                        goto OUT;                          //goto next loop.
                    }
                }
            }
            /*
            else
            {
                m = 0;
                n = 0;
                return 0xFF;
                goto OUT;   
            }
            */
        }
                
    OUT:                                                   //LABEL,out dead while
     ;
    }
    
}

//-----------------------------------------------------------------------*
//函数名: LedShowNumCount                                                *
//功  能: 在LED上显示数值。从0000到用户提供的NUM，不停地增加             * 
//参  数: num,需要计数的上限。num最大为9999。                            *
//返  回: 无返回值。                                                     *
//说  明: 由于LED得显示是扫描的方式进行的，是需要一个死循环的，因而执行  *
//        到这个程序里面无法自己跳出来，除非执行完毕，而且普通程序也无法 *
//        跳出，只能通过系统中断处理机制跳出。设计时要注意。             *
//-----------------------------------------------------------------------*
void LedShowNumCount( uint16 num )
{
    uint8  LEDbuf[4];                                      //用于存储LED的四段数值
    
    uint16 j = 0, i = 0;                                   //用于计数
    uint16 temp;  
    
    LEDbuf[0]='0';                                         //初始化，led首先显示的数值
    LEDbuf[1]='0';
    LEDbuf[2]='0';
    LEDbuf[3]='0';      
                                                           //计数功能，1~num
    for( i = 0; i < num; i++ )
    {
        LEDbuf[0] =  i / 1000 + 48;                        //加48转换为char装换位ascii码
        temp = i % 1000;                                   //取四位数高位
        
        LEDbuf[1] =  temp / 100 + 48;                       
        temp = temp % 100;                                 //取次高位
        
        LEDbuf[2] =  temp / 10 + 48;
        temp = temp % 10;                                  //取次低位
        
        LEDbuf[3] = temp +48;                              //取低位
                     
        while(1)
        {
            if( StopFlag == CODE_SUCCESSED )               //如果检测到停止标志为允许停止，则跳出当前程序
            {
                StopFlag = CODE_FAILED;                    //重新置初始状态  
                goto STOP;
            }
                
            //cmd_from_pc = 0;                             //本来用于循环中识别外面的中断命令，但是现在设计为通过系统中断的方法跳出来。
            //cmd_from_pc = OperateCodeRecognise();        //获取操作码，实现不同操作
            //if( cmd_from_pc != 0xFF && cmd_from_pc == CODE_STOP )
            //{
                //goto STOP;
            //}
            //else
            //{     
                LEDshow( LEDbuf );   
                j++;                                       //j用于计数，自增
                if( j == 100 )                             //延时显示，要不然就看不到了，一闪而过
                {
                    j = 0;                                 //j置0，为下次计数准备
                    goto OUT;                              //跳出循环显示下一个数
                }
            //}
        }
        
        OUT:                                               //用于一个数的显示跳出，准备显示下一个数
        ;   
    }     
    STOP:                                                  //用于停止跳出
    ;
}

//-----------------------------------------------------------------------*
//函数名: ScreenShowStr                                                  *
//功  能: 在LCD上显示从PC机上获取到的信息。都是32个字符。                * 
//参  数: data                                                           *
//返  回: 无返回值。                                                     *
//说  明: 该函数首先显示初始化字符，然后等待电脑的输入，一次输入完成需要 *
//        重新发送命令。因为已经从该处理过程出来了。                     *
//-----------------------------------------------------------------------*
void ScreenShowStr(uint8 data[])
{  
    uint8 firstStr[] = "Waiting...Please enter the words"; //默认字符串

    LCDshow(firstStr);                                     //首先初始化LCD屏幕上的值                                      
                                                                                                         
    while (1)                                              //使用死循环等待用户的数据输入
    { 
        if( StopFlag == CODE_SUCCESSED )                   //如果检测到停止标志为允许停止，则跳出当前程序
        {
            StopFlag = CODE_FAILED;
            goto OUT;                                      //重新置初始状态   
        }          
                                                           //接收PC发送的32个数据,并存放到receive_data数组中
        if (0 == SCIReN(SCI_NUM,32,data))                  //接收成功
        {   
                                                           //LCD显示MCU从串口接收到的32个数据并返回给PC
            LCDshow(data);                                 //显示的数据变量，这里必须用静态变量，否则异常，问题待查  *** ? ***
            goto OUT;                                      //显示一次跳出
        }     
    }
    
    OUT:                                                   //出来标志位
    ;             
}

//-----------------------------------------------------------------------*
//函数名: LightChangONandOFF                                             *
//功  能: 小灯不停的闪烁，明暗交替。                                     * 
//参  数: 无                                                             *
//返  回: 无返回值。                                                     *
//说  明:                                                                *
//-----------------------------------------------------------------------*
void LightChangONandOFF()
{                                                          //组成确认字段
    MakeCommand(CODE_FAILED,CODE_SUCCESSED,CODE_LIGHT_CHANGE,ResultCommand);
    SCISendN(SCI_NUM,7,ResultCommand);                     //发送确认信息
    
    while(1)
    {
        if( StopFlag == CODE_SUCCESSED )                   //如果检测到停止标志为允许停止，则跳出当前程序
        {
            StopFlag = CODE_FAILED;                        //重新置初始状态  
            goto STOP;
        }
        
        Light_Change(Light_Run_PORT,Light_Run);            //小灯交替
        
        Delay(1);                                          //延时   
    }
    
    STOP:                                                  //停止标号
    ;
}


//-----------------------------------------------------------------------*
//函数名: PwmControlLight                                                *
//功  能: 从PC接受占空比，利用pwm波形控制小灯。                          * 
//参  数: 无                                                             *
//返  回: 无返回值。                                                     *
//说  明: 100 - 64(HEX) 传十六进制进来                                   *
//-----------------------------------------------------------------------*
void PwmControlLight()
{
    uint8 receive_data[1];                                             

                                                           //组成确认字段
    MakeCommand(CODE_FAILED,CODE_SUCCESSED,CODE_PWM_CONTROL_LIGHT,ResultCommand);
    SCISendN(SCI_NUM,7,ResultCommand);                     //发送确认信息

                                                           //接收PC发送的1个数据,并存放到receive_data数组中
    if (0 == SCIReN(SCI_NUM,1,receive_data))               //接收成功
    {   
        PWM( TIMER_NUM_2,                                  //定时器号
             TIMER2_CHANNEL_PWM_NUM,                       //定时器2的用于pwm的通道号           
             0x0138,                                       //设定的周期 10ms
             receive_data[0] );                            //占空比
    }  
}
        
//-----------------------------------------------------------------------*
//函数名: SavePictoFlash                                                 *
//功  能: 保存图片到flash的指定空间                                      * 
//参  数: 无                                                             *
//返  回: 无返回值。                                                     *
//说  明: 默认写入512字节                                                *
//-----------------------------------------------------------------------*
void SavePictoFlash( uint16 addr )
{
    uint8 receive_data[1];
    uint8 temp[16];
    uint8 i = 0, j = 0;
  
    if( 0 == ErasePage( addr ) )
    {
        for( i = 0; i < 16; i++ )
        {
            for( j = 0; j < 16; j++ )
            {
                while(1)
                {
                    if( 0 == SCIReN(SCI_NUM,1,receive_data) )
                    {
                        goto OUT;  
                    }
                }
                OUT:
                ;
                temp[j] = receive_data[0];
            }
            WriteFlash(temp,(addr+i*16),16);
        }
    
    }       
}

//-----------------------------------------------------------------------*
//函数名: ReadPicfromFlash                                               *
//功  能: 从指定地址开始读取图片文件                                     * 
//参  数: addr，从这里设定的地址出读取512字节数据                        *
//返  回: 无返回值。                                                     *
//说  明:                                                                *
//-----------------------------------------------------------------------*
void ReadPicfromFlash( uint16 addr )
{ 
  
    uint8 re[16];
    uint16 i = 0,j = 0;

    for( j = 0; j < 16; j++ )
    {   
        for( i = 0; i < 16; i++ )
        {
            re[i] = *( (uint8 *)(void *)(addr+i+j*16) );   //取数方法 
        }
        SCISendN(SCI_NUM,16,re); 
    }

}


/******************************END FUNCTION***********************************/

