TA的每日心情 | 奋斗 2016-12-6 18:19 |
---|
签到天数: 7 天 连续签到: 1 天 [LV.3]偶尔看看II
|
本帖最后由 桅杆吟风 于 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 串口助手显示结果
|
|