查看: 2843|回复: 0

[原创] MPC5604B 采用循环队列发送接收数据

[复制链接]
  • TA的每日心情
    奋斗
    2016-12-6 18:19
  • 签到天数: 7 天

    连续签到: 1 天

    [LV.3]偶尔看看II

    发表于 2016-11-7 20:14:16 | 显示全部楼层 |阅读模式
    分享到:
    本帖最后由 桅杆吟风 于 2016-11-7 20:15 编辑

           一般在UART(SCI)模块下采用缓冲区,因为串口发送相对于程序的运行是在太慢,CPU不可能在那里等着它完成。因此往往要采用中断来发送。采用在中断还没完,要加上缓冲区会更好。如果采用单向的队列,数据满了,就得等着,效率不高,因此需要将队列的首尾相接,如图1所示。
           采用飞思卡尔32位单片机,构造循环队列来缓冲数据,是一个不错的例子。采用循环队列的串口发送移植性很强,很容易应用在其他单片机中。
          本例将创建两个缓冲区,缓冲区长度为128字节,也可以小于它,不过需要是2的倍数的增长,以便于掩码运算的正确性。上一次写的例子中用了软硬件中断,硬件中断虽然快,但是写起来太复杂。这里我们采用MPC5604的软件中断,即第四号内核中断。
          网上一搜就有很多关于FIFO队列的介绍,我这里就不做详细介绍了,直接应用FIFO来事项通信功能。对于MPC5604来讲,UART功能整合到了LINFlex中,具有4个UART,每个UART具有4个字节的输入缓冲区和输出缓冲区,但对我们来说还不够,因此建立了队列缓冲区。
         程序中包含了采用轮询的发送和中断的发送方式,波特率采用9600,使用串口0发送数据,通过串口调试助手来监控,以及通过调试器在线监控。用CW2.1版本的调试界面时,总是无法实现像BDM那样实时监控全局变量的变化,不知道怎么设置,查了资料也不知道,我用的是NXP官网上下载的IDE,没破解的。
        下面是程序全部部分,共参考学习。

    //***********************************************************
    // 程序功能:MPC5604 串口测试
    // 编    写:ChenYanan
    // 日    期: 2016.11.02
    // 芯    片:MPC5604BML06
    // 晶    振:8Mhz
    //***********************************************************


    #include "MPC5604B_M07N.h"
    #include "IntcInterrupts.h"
    #include "main.h"
    #include "comm.h"



    Tvar tvar;
    Tadc tadc;
    Uart uart;



    uint8_t putstring[]="123hello world! Let‘s begin!\r\n";


    //==================================中断服务程序=================================
    //RTC中断程序
    void RTC_Iner(void)
    {
      tvar.u32RTC++;
      RTC.RTCS.B.APIF=1;             //清中断
    }

    // ***************************
    void Pit1_ISR(void)
    {
      tvar.u32debug++;              

      PIT.CH[1].TFLG.B.TIF = 1;     //清中断标志位
    }
    // *******************************************
    void SwIrq4_ISR(void)
    {
      INTC.SSCIR[4].R = 1;                //清除中断标志
    }
    // *******************************************
    // 中断发送函数
    void UART_TX_interrupt(void)
    {   
            uint08 u08_Out;                                         // 临时出指针
        LINFLEX_0.UARTSR.B.DTF = 1;                             // 清除发送完成标志位

            if ( uart.tuart_send.u08_Out ==uart.tuart_send.u08_Inp)  return;//检查数据是否空

             
        u08_Out = (uart.tuart_send.u08_Out+ 1) & SENDMASK;                    //计算当前指针,指针加1
        uart.tuart_send.u08_Out  = u08_Out;                                   //更新指针
            LINFLEX_0.BDRL.B.DATA0 = uart.tuart_send.pu08send[u08_Out];           //写入寄存器发送                              //数据发送
                     
    }

    // *******************************************
    // 中断接收函数
    void UART_RX_interrupt(void)
    {
      uint08 u08_Inp;
      LINFLEX_0.UARTSR.B.DRF = 1;                                   // 清除接收完成标志位

      u08_Inp=(uart.tuart_rece.u08_Inp+1)&RECEMASK;                // 计算当前指针,指针加1
      if(u08_Inp==uart.tuart_rece.u08_Out)   
      {
       uart.ReceFull=1;
              return;               // 缓冲区满了
      }


      uart.tuart_rece.u08_Inp=u08_Inp;
      uart.tuart_rece.pu08Rece[u08_Inp-1]=(uint08)LINFLEX_0.BDRM.B.DATA4;  // 读取接收到的数据
             
    }


    //=================================中断程序结束===================================
    //初始化中断,采用IntcInterrupts.c文件中的函数进行初始化
    void Init_INTC(void)
    {  

       INTC.MCR.B.HVEN = 0;        // 软件向量模式
       INTC.MCR.B.VTES = 0;               // 4字节

       EXCEP_InitExceptionHandlers();                 //初始化异常处理函数
       INTC_InitINTCInterrupts();                     //清中断


       INTC_InstallINTCInterruptHandler(RTC_Iner,39,5);         //API中断初始化程序,优先级5

       INTC_InstallINTCInterruptHandler(UART_RX_interrupt,79,2);  //UART 接收中断,优先级2
       INTC_InstallINTCInterruptHandler(UART_TX_interrupt,80,1);  //UART 发送完成中断,优先级2
       INTC_InstallINTCInterruptHandler(SwIrq4_ISR,4,2);         //中断初始化程序,优先级1


    }

    //--------------------------------------------------------------
    //ADC采样函数
    void Adc_Sample(void)
    {
       ADC.MCR.B.NSTART = 1;                                //启动转换

       while(ADC.CDR[0].B.VALID != 1)  ;                    //等待ADC0转换结果有效
       tadc.u16adcval[0] = (uint16_t)ADC.CDR[0].B.CDATA;    //读取ADC0的转换结果

       while(ADC.CDR[1].B.VALID != 1)  ;                    //等待ADC1转换结果有效
       tadc.u16adcval[1] = (uint16_t)ADC.CDR[1].B.CDATA;    //读取ADC1的转换结果        
    }

    //-------------------------------------------------------------------------------
    //延时函数
    void Delay(uint32_t deftime)
    {
    tvar.u32count=tvar.u32RTC;
    while((uint32)(tvar.u32RTC-tvar.u32count)<deftime) ;
    }


    //=============================================================================
    //主函数
    void main(void)
    {

    DisableIrq();

    Init_Watchdog();
    Init_MC_ME();
    Init_MCGM();
    Init_INTC();          //初始化中断

    Init_SIU();
    Init_RTC();           //初始化RTC中断

    //Init_ADC();
    Init_Uart();

    Init_Fifo();

    EnableIrq();
            



    while (1)
      {

                
                //Send_String(putstring);
                Fifo_Send_Str(putstring);
            Delay(1000);
            SIU.GPDO[70].B.PDO  ^=1 ;         //亮灭


      }
    }


    //====================================================================================
    //END


    // --------------------------------------------------------------------------
    #include "MPC5604B_M07N.h"
    #include "main.h"


    //---------------------------------------------------------------------------
    //初始化运行模式
    void Init_MC_ME(void)
    {
    ME.MER.R = 0x0000001D;            //打开RUN0  DRUN SAFE RESET模式
    ME.RUNPC[0].R=0x000000FE;         // 外设在所有模式均打开

    ME.RUN[0].B.MVRON =1;          // 电压规整器开
    ME.RUN[0].B.DFLAON=3;          // FLASH处于正常模式
    ME.RUN[0].B.CFLAON=3;          // 代码寄存器处于正常模式
    ME.RUN[0].B.FXOSC0ON=1;        // 外部晶振打开
    ME.RUN[0].B.FMPLLON=1;         // RUN0模式锁相环开
    ME.RUN[0].R|=0x04;             // 选择PLL时钟

    ME.PCTL[91].R = 0x00;           //RTC在RUN0模式开启
    ME.PCTL[92].R = 0x00;           //PIT在RUN0模式开启

    ME.MCTL.R =   0x40005AF0;         // 设置RUN0模式,控制KEY p147
    ME.MCTL.R =   0x4000A50F;         // 设置RUN0模式反转KEY p147
    while(ME.GS.B.S_MTRANS==1) ;       // 等待进入响应模式
    while(ME.GS.B.S_CURRENTMODE != 4) ; //RUN0处于电流模式

             
    }
    //--------------------------------------------------------------------------
    //初始化时钟PLL=64Mhz fPLL=fosc*NDIV/IDF/ODF
    //FVCO: 256MHz-512MHz
    void Init_MCGM(void)
    {

    //CGM.FMPLL_CR.R = 0x02400100;
    CGM.FMPLL_CR.B.EN_PLL_SW=1;          //渐进式时钟切换
    CGM.FMPLL_CR.B.IDF=0;
    CGM.FMPLL_CR.B.NDIV=64;
    CGM.FMPLL_CR.B.ODF=2;                 //设置PLL0 64 MHz

    while(!CGM.FMPLL_CR.B.S_LOCK) ;       //等待PLL锁定

    // if(ME.GS.S_SYSCLK==0b0100);        //如果是PLL时钟
    while(!ME.GS.B.S_FMPLL) ;                   //等待PLL稳定


    CGM.CMU_CSR.B.CME_A=1;                  //FMPLL 检测器开
    CGM.CMU_HFREFR_A.R=258;                 //上限64.5MHz
    CGM.CMU_LFREFR_A.R=254;                 //上限63.5MHz
    RGM.FBRE.B.BE_FMPLL=1;                  // FMPLL失败复位使能

    }

    //----------------------------------------------------------------------
    void Init_Watchdog(void)        //初始化看门狗
    {                             
       //禁用看门狗
      SWT.SR.R = 0x0000c520;     // Write keys to clear soft lock bit
      SWT.SR.R = 0x0000d928;
      SWT.CR.R = 0x8000010A;     // Clear watchdog enable (WEN)
    }
    //------------------------------------------------------------------------------------
    //时钟输出
    void InitOutputClock(void)
    {
      CGM.OC_EN.B.EN = 1;                  // 时钟输出使能
      CGM.OCDS_SC.B.SELDIV = 3;      // 输出分频 8
      CGM.OCDS_SC.B.SELCTL = 2;      // 选择 FMPLL时钟
      SIU.PCR[0].B.PA= 2;            // PA0作为时钟输出 64MHz/8=8MHz

    }
    //--------------------------------------------------------------------------------]
    //初始化端口
    void Init_SIU(void)
    {
                
                
    SIU.PCR[70].B.IBE=0;      //端口PE6 对应70
    SIU.PCR[70].B.PA=0;
    SIU.PCR[70].B.OBE=1;
    //SIU.PCR[70].B.WPE=1;
    SIU.GPDO[70].B.PDO  = 1;   //

    SIU.PCR[18].R = 0x0600;               // 将PB[2]设置为LINFlex_0.TxDA
    SIU.PCR[19].R = 0x0100;               // 将PB[3]设置为LINFlex_0.RxDA
    SIU.PCR[20].R = 0x2000;               // 设置PB[4]为ADC0模数转换输入
    SIU.PCR[21].R = 0x2000;               // 设置PB[5]为ADC1模数转换输入


    }
    //--------------------------------------------------------------------------
    //初始化ADC
    void Init_ADC(void)
    {
      ADC.MCR.R        = 0x80000000;        //初始化ADC为单次转换模式,右对齐,使能覆盖,时钟为64/2=32MHz。
      ADC.NCMR[0].R    = 0x00000003;        //选择转换ADC0和ADC1
      ADC.CTR[0].R     = 0x00008606;        //设置转换时间 通道0-15
      ADC.MCR.B.NSTART = 1;                 //启动AD转换
    }
    // --------------------------------------------------------------------------------------
    // 初始化实时时钟,采用API模式
    void Init_RTC(void)                       
    {

    CGM.SIRC_CTL.B.RCDIV=0;                    // RC振荡器不分频
    CGM.SIRC_CTL.B.SIRCON_STDBY=1;             // StandBy模式开SIRC

    RTC.RTCSUPV.R=0x80000000;                  //仅在管理员模式可访问其余寄存器
    //RTC.RTCC.R = 0;                          //重置
    RTC.RTCC.B.CLKSEL=1;                       //选择128KHz SIRC
    RTC.RTCC.B.DIV32EN=1;                      //32分频使能 4kHz
    RTC.RTCC.B.CNTEN=1;                        //计数使能
    RTC.RTCC.B.APIVAL=4;                       //1ms中断一次,在APIEN之前定义,最小值4
    RTC.RTCC.B.APIEN=1;                        //API使能
    RTC.RTCS.B.APIF=1;                         //清中断
    RTC.RTCC.B.APIIE=1;                        //API中断使能


    }   

    //---------------------------------------------------------------------------
    //初始化周期中断
    void Init_PIT(void)
    {
      PIT.PITMCR.R = 0x00000001;       // 使能PIT 在DEBUG模式中停止
      PIT.CH[1].LDVAL.R = 64000;       // 溢出时间1ms = 64000 clk x 1sec/64M
      PIT.CH[1].TCTRL.R = 0x000000003; // 使能PIT中断

    }
    //------------------------------------------------------------------------------
    //END

    //***********************************************************
    // 程序功能:MPC5604 SCI 串口
    // 编    写:ChenYanan
    // 日    期: 2016.11.02
    // 芯    片:MPC5604BML06
    // 晶    振:8Mhz
    //***********************************************************


    #include "MPC5604B_M07N.h"
    #include "main.h"
    #include "comm.h"


    extern Uart uart;

    #define   DISABLE             0         // 禁用
    #define   ENABLE              1         // 使能

    //====================================================================
    //初始化UART,即 LINFlex
    void Init_Uart(void)
    {  
    //配置LINFlex
      LINFLEX_0.LINCR1.B.INIT   = ENABLE;   // 请求初始化
      LINFLEX_0.LINCR1.B.SLEEP  = DISABLE;  // 禁止睡眠模式
      LINFLEX_0.LINCR1.B.BF     = DISABLE;  // 如果ID不匹配不产生中断

      LINFLEX_0.UARTCR.B.UART   = 1;        // 进入UART模式
      LINFLEX_0.UARTCR.B.RXEN   = ENABLE;   // 禁止接收
      LINFLEX_0.UARTCR.B.TXEN   = ENABLE;   // 允许发送
      LINFLEX_0.UARTCR.B.WL     = 1;        // 8位数据位
    //  LINFLEX_0.UARTCR.B.OP     = 1;      // 偶校验
      LINFLEX_0.UARTCR.B.PCE    = DISABLE;  // 禁止奇偶校验
      LINFLEX_0.UARTCR.B.TDFL   = 0;        // 发送缓冲区为1个字节
      LINFLEX_0.UARTCR.B.RDFL   = 0;        // 接收缓冲区为1个字节

    //设置波特率为9600
    //波特率=外设时钟1/(16*LFDIV)
    //DIV_M定义LFDIV的整数部分,(DIV_F/16)定义LFDIV的小数部分。

    LINFLEX_0.LINIBRR.B.DIV_M = 416;      // 波特率 = 9600,    总线 64 MHz
    LINFLEX_0.LINFBRR.B.DIV_F = 11;      
    //LINFLEX_0.LINIBRR.B.DIV_M = 208;      // 波特率 = 19200,   总线 64 MHz
    //LINFLEX_0.LINFBRR.B.DIV_F = 5;        
    //LINFLEX_0.LINIBRR.B.DIV_M = 104;      // 波特率 = 38400,   总线 64 MHz
    //LINFLEX_0.LINFBRR.B.DIV_F = 3;        
    //LINFLEX_0.LINIBRR.B.DIV_M = 69;       // 波特率 = 57600,   总线 64 MHz
    //LINFLEX_0.LINFBRR.B.DIV_F = 7;        
    //LINFLEX_0.LINIBRR.B.DIV_M = 34;       // 波特率 = 115200,  总线 64 MHz
    //LINFLEX_0.LINFBRR.B.DIV_F = 12;      

    //配置中断,使能中断功能
    LINFLEX_0.LINIER.B.DRIE   = ENABLE;   // 数据接收完成中断
    //LINFLEX_0.LINIER.B.DTIE   = ENABLE;   // 数据发送完成中断
    //LINFLEX_0.LINIER.B.DBFIE  = ENABLE;   // 数据缓冲器满中断
    LINFLEX_0.LINIER.B.DTIE   = ENABLE;  // 数据发送完成中断
    //LINFLEX_0.LINIER.B.DBEIE  = ENABLE;  // 数据缓冲器空中断
    //LINFLEX_0.LINIER.B.DBEIE  = ENABLE;   // 数据缓冲器空中断

    //配置中断,禁止中断功能
      //LINFLEX_0.LINIER.R        = 0;        // 禁止所有中断
    //LINFLEX_0.LINIER.B.DRIE   = DISABLE;  // 数据接收完成中断
    //LINFLEX_0.LINIER.B.DTIE   = DISABLE;  // 数据发送完成中断
    //LINFLEX_0.LINIER.B.DBFIE  = DISABLE;  // 数据缓冲器满中断
    //LINFLEX_0.LINIER.B.DBEIE  = DISABLE;  // 数据缓冲器空中断

      LINFLEX_0.UARTSR.B.DRF    = 1;        // 清除接收完成标志
      LINFLEX_0.UARTSR.B.DTF    = 1;        // 清除发送完成标志

      LINFLEX_0.LINCR1.B.INIT   = DISABLE;  // 变为正常模式


    }

    //--------------------------------------------------------------
    // (查询) UART0发送一字节               
    void UART0_TX(uint08 data)
    {
      LINFLEX_0.BDRL.B.DATA0 = data;        // 写入需发送的数据
      while(LINFLEX_0.UARTSR.B.DTF == 0) ; // 等待发送完成
      LINFLEX_0.UARTSR.B.DTF = 1;           // 清除发送完成标志位
    }

    //--------------------------------------------------------------
    //(查询)串口发送字符串函数                     
    void Send_String(uint08 *putchar)
    {
      while(*putchar)         //判断字符串是否发送完毕

       UART0_TX(*putchar++);  

    }


    //----------------------------------------------------------------
    //队列初始化
    void Init_Fifo(void)
    {
    uart.tuart_send.u08_Inp =0;                  //
    uart.tuart_send.u08_Out =0;
    uart.tuart_rece.u08_Inp =0;                  //
    uart.tuart_rece.u08_Out =0;        
    uart.ReceFull=0;                                    // 接收满标志清零
    }
    //--------------------------------------------------------------------------
    //入队操作,发送
    void Uart_FifoIn(uint08 data)
    {
    uint08 u08_Inp;
    u08_Inp= (uart.tuart_send.u08_Inp+1)&SENDMASK;      // 计算当前指针,指针加1

    while(u08_Inp==uart.tuart_send.u08_Out) ;           // 判断是否满了 等待缓冲区空

    uart.tuart_send.pu08send[u08_Inp]=data;             // 数据存入缓冲区

    uart.tuart_send.u08_Inp=u08_Inp;                    // 更新指针



    }

    //--------------------------------------------------------------
    //(中断)串口发送字符串函数                     
    void Fifo_Send_Str(uint08 *putchar)
    {
      LINFLEX_0.BDRL.B.DATA0 = *(putchar++);       //发送第一个数,触发中断

      while(*putchar)                              //判断字符串是否发送完毕

       Uart_FifoIn(*putchar++);   

    }
    //--------------------------------------------------------------
    //出队操作,接收(未调用)
    uint08 Uart_FifoOut(void)
    {
    uint08 u08_Out;

    while(uart.ReceFull) ;                                             // 判断是否满了 等待缓冲区空

    u08_Out=(uart.tuart_rece.u08_Out+1)&RECEMASK;                      // 计算当前指针,指针加1
    uart.tuart_rece.u08_Out=u08_Out;                                   // 更新指针

    return uart.tuart_rece.pu08Rece[u08_Out];                  

    }

    //====================================================================================
    //END


    #include "MPC5604B_M07N.h"


    #ifndef MAIN_H

    #define MAIN_H
    // volatile应该解释为:直接存取原始内存地址

    //中断服务程序中修改的供其它程序检测的变量需要加volatile;

    //多任务环境下各任务间共享的标志应该加volatile

    //存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义;


    //
    typedef   signed char       sint08;
    typedef unsigned char       uint08;
    typedef const unsigned char cuint08;
    typedef   signed int        sint16;
    typedef unsigned int        uint16;
    typedef const unsigned int  cuint16;
    typedef   signed long       sint32;
    typedef unsigned long       uint32;


    #define EnableIrq()   {INTC.CPR.B.PRI = 0;asm(" wrteei 1");}
    #define DisableIrq()   asm(" wrteei 0")
    typedef struct
    {
    volatile uint32 u32RTC;   //中断中的变量
    uint32 u32count;
    uint32 u32debug;         
    } Tvar;

    typedef struct
    {
    uint16 u16adcval[16];
             
    } Tadc;


    void Init_MC_ME(void);
    void Init_MCGM(void);
    void Init_Watchdog(void);        //初始化看门狗
    void Init_SIU(void);
    void InitOutputClock(void);
    void Init_ADC(void);
    void Init_RTC(void);
    void Init_PIT(void);


    extern Tvar tvar;
    extern Tadc tadc;

    #endif
    // ============================================================================



    //***********************************************************
    // 程序功能:MPC5604 通信头文件
    // 编    写:ChenYanan
    // 日    期: 2016.11.02
    // 芯    片:MPC5604BML06
    // 晶    振:8Mhz
    //***********************************************************

    #include "main.h"


    #ifndef COMM_H
    #define COMM_H


    // UART
    #define DefUartLenSend    128U    // 发送缓冲区长度1,2,4,8,16,32
    #define SENDMASK          (DefUartLenSend-1)
    #define DefUartLenRece    128U    // 接收缓冲区长度1,2,4,8,16,32
    #define RECEMASK          (DefUartLenRece-1)


    typedef struct
    {
      uint08  u08_Inp;                                // 进指针,如果进出指针完全相同,表示FIFO为空,此时禁止读
      uint08  u08_Out;                                // 出指针 进出指针相等时为空
      uint08  pu08send[DefUartLenSend];               // 发送缓冲区        
    } Tuart_send;

    typedef struct
    {
      uint08    u08_Inp;                                // 进指针,如果进出指针完全相同,表示FIFO为空,此时禁止读
      uint08    u08_Out;                                // 出指针 进出指针相等时为空  
      uint08    pu08Rece[DefUartLenRece];               // 通讯接收缓冲区  
             
    } Tuart_rece;

    typedef struct
    {
    Tuart_rece tuart_rece;        
    Tuart_send tuart_send;
    uint08  ReceFull;

    }Uart;


    //函数声明
    void Init_Uart(void);
    void Send_String(uint08 *putchar);
    void Init_Fifo(void);
    void Fifo_Send_Str(uint08 *putchar);

    extern Uart uart;

    #endif
    // ============================================================================

    图2 串口助手显示结果

    图2 串口助手显示结果







    图1 队列

    图1 队列

    图3 调试界面

    图3 调试界面

    图4 调试界面

    图4 调试界面
    回复

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /2 下一条



    手机版|小黑屋|与非网

    GMT+8, 2024-4-20 13:25 , Processed in 0.119523 second(s), 16 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2020, Tencent Cloud.