回答

收藏

MPC5604B 采用循环队列发送接收数据

其他 其他 3710 人阅读 | 0 人回复 | 2016-11-07

本帖最后由 桅杆吟风 于 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
// ============================================================================







8c0cd14373948dbf0a56d19eb86f8792.jpg (136.34 KB, 下载次数: 67)

图1 队列

图1 队列

2016-11-07_195437.png (44.17 KB, 下载次数: 29)

图3 调试界面

图3 调试界面

2016-11-07_195446.png (168.55 KB, 下载次数: 24)

图4 调试界面

图4 调试界面
分享到:
回复

使用道具 举报

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

本版积分规则

关闭

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