在860里增加TL16C554多串口芯片驱动.docx
《在860里增加TL16C554多串口芯片驱动.docx》由会员分享,可在线阅读,更多相关《在860里增加TL16C554多串口芯片驱动.docx(25页珍藏版)》请在冰点文库上搜索。
在860里增加TL16C554多串口芯片驱动
在860里增加TL16C554多串口芯片驱动
(一)
一.设备概述
MPC860t有4个SCC(serialcommunicationscontroller串口通讯控制器)和2个SMC(serialmanagementcontroller串口管理控制器),一共可以扩展6个串口。
但笔者开发的通讯控制器项目中,由于考虑到需要扩展8个或更多串口,SCC和SMC扩展显然不能满足要求,需要需求一种新的可扩展的串口芯片。
我们采用两个TL16C554芯片来解决这个问题。
一个TL16C554芯片集成了四个ST16550ACE(异步通讯元件ASYNCHRONOUSCOMMUNICATIONSELEMENT)。
二.串口驱动简介
注意:
此处xxDrv,yyDrv表示一个特定的驱动程序,下同
.ttyDrv虚拟驱动,用以
.管理I/OSystem
.调用实际的驱动程序来管理硬件
.Model
.xxDrv支持:
标准I/OSystem接口
TargetAgent接口
两种模式(中断interrupt,轮巡poll)
.回调CallBacks
允许高层协议来设定驱动怎样收发数据
有两个回调(驱动调用)
getCallback
putCallBack
put回调:
传送一个从设备读到的字符到高层协议
get回调:
从高层协议取得一个字符以便写到设备
代码实现
usrConfig.c一般性代码
sysSerial.c特定执行代码
xxDrv.c设备特定代码
三.详细解说(调用和实现部分)
usrConfig.c,sysSerial.c
注意:
由于我们采用了多种设备实际的驱动(SMC和16C554),所以在sysSerial.c里需要整合,整合的函数包括sysSerialHwInit(),
sysSerialHwInit2(),sysSerialChanGet(),sysSerialReset()
解读usrConfig.c
先看usrConfig.c里与串口有关的部分(前面部分是早期版本的,不予理会),这里调用了ttyDrv(),然后用ttyDevCreate(),sysSerialChanGet()在sysSerials.c里定义,它把设备的编号同一个SIO_CHAN类型的结构关联起来,而这个结构在是至关重要的.(注意,如果系统采用串口调试,则该串口号被保留起来)
ttyDev()作的工作:
调用iosDrvInstall安装ttyDrv到设备表(使用ttyDrv和tyLib的入口)
ttyDevCreate()作的工作:
申请并初始化设备描述
调用tyDevinit()初始化tyLib
初始化selectLib
建立输入输出环行缓冲
创建信号量
调用iosDevAdd()增加设备到设备列表
安装tyLib的程序和输入输出回调
使设备开始中断模式
#ifdefINCLUDE_TYCODRV_5_2
#ifdefINCLUDE_TTY_DEV
if(NUM_TTY>0)
{
tyCoDrv();/*installconsoledriver*/
for(ix=0;ix{
sprintf(tyName,"%s%d","/tyCo/",ix);
(void)tyCoDevCreate(tyName,ix,512,512);
if(ix==CONSOLE_TTY)
strcpy(consoleName,tyName);/*storeconsolename*/
}
consoleFd=open(consoleName,O_RDWR,0);
/*setbaudrate*/
(void)ioctl(consoleFd,FIOBAUDRATE,CONSOLE_BAUD_RATE);
(void)ioctl(consoleFd,FIOSETOPTIONS,OPT_TERMINAL);
}
#endif/*INCLUDE_TTY_DEV*/
#else/*!
INCLUDE_TYCODRV_5_2*/
#ifdefINCLUDE_TTY_DEV
if(NUM_TTY>0)
{
ttyDrv();/*installconsoledriver*/
for(ix=0;ix{
#if(defined(INCLUDE_WDB)&&(WDB_COMM_TYPE==WDB_COMM_SERIAL))
if(ix==WDB_TTY_CHANNEL)/*don'tuseWDBschannel*/
continue;
#endif
sprintf(tyName,"%s%d","/tyCo/",ix);
(void)ttyDevCreate(tyName,sysSerialChanGet(ix),512,512);
if(ix==CONSOLE_TTY)/*initthettyconsole*/
{
strcpy(consoleName,tyName);
consoleFd=open(consoleName,O_RDWR,0);
(void)ioctl(consoleFd,FIOBAUDRATE,CONSOLE_BAUD_RATE);
(void)ioctl(consoleFd,FIOSETOPTIONS,OPT_TERMINAL);
}
}
}
#endif/*INCLUDE_TTY_DEV*/
#endif/*!
INCLUDE_TYCODRV_5_2*/
sysSerialChanGet():
以下是单独采用860smc时的情况
SIO_CHAN*sysSerialChanGet
(
intchannel/*serialchannel*/
)
{
if(channel>=chanNum)
return((SIO_CHAN*)ERROR);
return((SIO_CHAN*)&ppc860Chan[channel]);
}
单独采用16c554时为:
SIO_CHAN*sysSerialChanGet
(
intchannel/*serialchannel*/
)
{
if(channel<0||
channel>=(int)(NELEMENTS(sysSioChans)))
return(SIO_CHAN*)ERROR;
returnsysSioChans[channel];
}
我们必须把两种情况整合.
SIO_CHAN*sysSerialChanGet
(
intchannel
)
{
if(channel<0||
channel>=(int)(NELEMENTS(sysSioChans)))
return(SIO_CHAN*)ERROR;
ifchannel<=1
return(SIO_CHAN*)ppc860Chan[channel]
else
returnsysSioChans[channel-2];
}
sysSerialReset()
关于sysSerialResset,由于16c554和860的smc采用不同的原理,所以其重启也有不同,所以在sysSerialReset()里要考虑两中情况
voidsysSerialReset(void)
{
/*disableserialinterrupts*/
intDisable(devParas[0].intLevel);
}
voidsysSerialReset(void)
{
sysSerialHwInit();
}
voidsysSerialReset(void)
{
sysSerialHwInit();
intDisable(devParas[0].intLevel);
}
重中之重:
关于sysSerialHwInit2()
sysSerialHwInit2()由usrConfig.c里的usrRoot调用,用于连接中断
smc的如下:
voidsysSerialHwInit2(void)
{
inti;/*anindex*/
/*connectserialinterrupts*/
for(i=0;i{
switch(i)
{
case0:
(void)intConnect(IV_SMC1,(VOIDFUNCPTR)ppc860Int,
(int)&ppc860Chan[i]);
break;
case1:
(void)intConnect(IV_SMC2_PIP,(VOIDFUNCPTR)ppc860Int,
(int)&ppc860Chan[i]);
break;
default:
return;
}
*CIMR(vxImmrGet())|=(CIMR_SMC1>>i);
}
}
CIMR是一个叫CPIntmaskreg的宏,这里分别开启SMC1和SMC2的中断
#defineCIMR_SMC10x00000010/*SMC1*/
#defineCIMR_SMC2_PIP0x00000008/*SMC2*/
#defineCIMR(base)(CAST(VUINT32*)((base)+0x0948))
为什么在不在sysSerialHwinit()里调用连接呢?
因为在sysHwInit()执行的时刻内核内存分配算符还没有初始化!
16c554的:
voidsysSerialHwInit2(void)
{
(void)intConnect(INUM_TO_IVEC(devParas[0].vector),
st16554MuxInt,(int)&st16554Mux);
intEnable(devParas[0].intLevel);
}
各位必须注意,16c554中断服务程序是一个多重的,意思是:
所有使用同一中断的8个通道有一样的中断服务程序,这样在st16554MuxInt()处理的时候需要做出相应的判断(具体实现见后面)
经过整合后的sysSerialHwInit2应该是:
voidsysSerialHwInit2(void)
{
inti;/*anindex*/
/*connectserialinterrupts*/
for(i=0;i{
switch(i)
{
case0:
(void)intConnect(IV_SMC1,(VOIDFUNCPTR)ppc860Int,
(int)&ppc860Chan[i]);
break;
case1:
(void)intConnect(IV_SMC2_PIP,(VOIDFUNCPTR)ppc860Int,
(int)&ppc860Chan[i]);
break;
default:
return;
}
*CIMR(vxImmrGet())|=(CIMR_SMC1>>i);
}
}
voidsysSerialHwInit2(void)
{
inti;/*anindex*/
/*connectserialinterrupts*/
for(i=0;i<2;i++)
{
switch(i)
{
case0:
(void)intConnect(IV_SMC1,(VOIDFUNCPTR)ppc860Int,
(int)&ppc860Chan[i]);
break;
case1:
(void)intConnect(IV_SMC2_PIP,(VOIDFUNCPTR)ppc860Int,
(int)&ppc860Chan[i]);
break;
default:
return;
}
*CIMR(vxImmrGet())|=(CIMR_SMC1>>i);
}
(void)intConnect(INUM_TO_IVEC(devParas[0].vector),
st16554MuxInt,(int)&st16554Mux);
intEnable(devParas[0].intLevel);
}
重中之重:
关于sysSerialHwInit()
首先看16554的:
voidsysSerialHwInit(void)
{
inti;
for(i=0;i{
st16554Chan[i].regDelta=devParas[i].regSpace;
st16554Chan[i].regs=devParas[i].baseAdrs;
st16554Chan[i].baudRate=CONSOLE_BAUD_RATE;
st16554Chan[i].xtal=UART_XTAL_FREQ;
st16554Chan[i].level=devParas[i].intLevel;
/*
*Initialisedriverfunctions,getTxChar,putRcvCharand
*channelModeandinitUART.
*/
st16554DevInit(&st16554Chan[i]);
}
}
devParas结构用存放设备的一些参数
typedefstruct
{
UINTvector;
UINT8*baseAdrs;
UINTregSpace;
UINTintLevel;
}CMA_ST16554_CHAN_PARAS;
LOCALCMA_ST16554_CHAN_PARASdevParas[]=
{
{INT_VEC_COM,(UINT8*)SERIAL_A_BASE_ADR,UART_REG_ADDR_INTERVAL,
INT_LVL_COM},
{INT_VEC_COM,(UINT8*)SERIAL_B_BASE_ADR,UART_REG_ADDR_INTERVAL,
INT_LVL_COM},
{INT_VEC_COM,(UINT8*)SERIAL_C_BASE_ADR,UART_REG_ADDR_INTERVAL,
INT_LVL_COM},
{INT_VEC_COM,(UINT8*)SERIAL_D_BASE_ADR,UART_REG_ADDR_INTERVAL,
INT_LVL_COM},
{INT_VEC_COM,(UINT8*)SERIAL_E_BASE_ADR,UART_REG_ADDR_INTERVAL,
INT_LVL_COM},
{INT_VEC_COM,(UINT8*)SERIAL_F_BASE_ADR,UART_REG_ADDR_INTERVAL,
INT_LVL_COM},
{INT_VEC_COM,(UINT8*)SERIAL_G_BASE_ADR,UART_REG_ADDR_INTERVAL,
INT_LVL_COM},
{INT_VEC_COM,(UINT8*)SERIAL_H_BASE_ADR,UART_REG_ADDR_INTERVAL,
INT_LVL_COM}
};
从SERIAL_A_BASE_ADR到SERIAL_H_BASE_ADR表示两个16c554的8个通道的基地址。
INT_VEC_COM和INT_LVL_COM是通道所用中断的矢量和中断号,各位可以看到8个通道(4X2)所用的通道一样.当然可以在设计电路的时候选择不同的通道,但这对有限的中断是浪费.
UART_REG_ADDR_INTERVAL是寄存器的步长,或者叫间隔,等于8.
SysSerialHwInit先对st16554Chan结构做一些初始化(st16554Chan(i)对应相应的通道),设置寄存器步长regDelta,基地址regs,波特率baudRate,时钟晶振频率xtal,中断号intLevel
然后调用xxDrv里的st16554DevInit,关于xxDevInit在xxDrv.c详解里详细介绍.
使用smc的SysSerialHwInit要复杂许多
voidsysSerialHwInit(void)
{
inti;/*anindex*/
/*enableserialI/Oontheboard*/
*BCSR1&=~(BCSR1_RS232_EN_L);
/*Ifrunningan823oran850,useonlySMC1*/
if(((*BCSR3&BCSR3_DBID_MASK)==BCSR3_823DB_MASK)||
((*BCSR3&BCSR3_DBID_MASK)==BCSR3_850DB_MASK))
{
chanNum=1;
}
if(chanNum==2)
*BCSR1&=~(BCSR1_RS232_2_EN_L);
/*intializethechipsdevicedescriptors*/
for(i=0;i{
UINT32regBase;
/*BRGCLKfreq(Hz)*/
ppc860Chan[i].clockRate=BRGCLK_FREQ;
/*IMMRreghasbaseadr*/
ppc860Chan[i].regBase=vxImmrGet();
regBase=ppc860Chan[i].regBase;
/*useBRG1forchannel1andBRG2forchannel2*/
ppc860Chan[i].bgrNum=(i+1);
/*SMCwiredforrs232*/
ppc860Chan[i].uart.smcNum=(i+1);
/*initthenumberofTBDs*/
ppc860Chan[i].uart.txBdNum=ppc860SmcParms[i].smcTbdNum;
0
/*initthenumberofRBDs*/
ppc860Chan[i].uart.rxBdNum=ppc860SmcParms[i].smcRbdNum;
/*transmitBDbaseadrs*/
ppc860Chan[i].uart.txBdBase=(SMC_BUF*)
(MPC860_REGB_OFFSET+
ppc860SmcParms[i].smcTbdOff);
/*receiveBDbaseadrs*/
ppc860Chan[i].uart.rxBdBase=(SMC_BUF*)
(MPC860_REGB_OFFSET+
ppc860SmcParms[i].smcRbdOff);
/*txbufbase*/
ppc860Chan[i].uart.txBufBase=(u_char*)
(MPC860_DPRAM_BASE(regBase)
+ppc860SmcParms[i].smcTxBufOff);
/*rxbufbase*/
ppc860Chan[i].uart.rxBufBase=(u_char*)
(MPC860_DPRAM_BASE(regBase)
+ppc860SmcParms[i].smcRxBufOff);
/*transmitbuffersize*/
ppc860Chan[i].uart.txBufSize=ppc860SmcParms[i].smcTxBufSz;
/*DPRAMaddrofSMC1params*/
ppc860Chan[i].uart.pSmc=(SMC*)((UINT32)PPC860_DPR_SMC1
(MPC860_DPRAM_BASE(regBase))
+(i*0x100));
/*SMCMR1forSMC1*/
ppc860Chan[i].uart.pSmcReg=(SMC_REG*)
((UINT32)MPC860_SMCMR1(regBase)
+(i*0x10));
/*Maskinterrupts*/
ppc860Chan[i].uart.pSmcReg->smcm=0;
ppc860Chan[i].pBaud=(UINT32*)((UINT32)MPC860_BRGC1(regBase)
+(i*4));
ppc860Chan[i].channelMode=0;
/*selectRS232pins*/
*MPC860_PBPAR(regBase)|=0xC0<<(i*4);
/*setittonormaloperations*/
*MPC860_SDCR(regBase)=SDCR_RAID_BR5;
/*resetthechip*/
ppc860DevInit(&(ppc860Chan[i]));
}
}
经过整合,应该是