好久没更新博客了,最近调了个基于TTY中间件的串口驱动,在此分享一下经验,也做笔记。
首先,定义结构体
typedef struct m8313_Uart_Chan
{
SIO_CHAN sio;
STATUS (*getChar)();
STATUS (*putChar)();
void * getCharArg;
void * putCharArg;
unsigned int intLevel;
unsigned int baund;
unsigned int errorCount;
int intrMode;
}M8313_UART_ChAN;
第二步,实现驱动函数的挂接
static SIO_DRV_FUNCS TL16C550C_DrvFuncs = {
TL16C550C_Ioctrl,
TL16C550C_StartUp,
TL16C550C_CallbackInstall,
TL16C550C_PollInput,
TL16C550C_PollOutput
};
第三步,实现TTY中间层到驱动层接口函数的挂接
static int TL16C550C_CallbackInstall(SIO_CHAN * pSioChan,
int callbackType,
STATUS (*callback)(void *, ...),
void * callbackArg)
{
M8313_UART_ChAN *pChan = (M8313_UART_ChAN*)pSioChan;
switch(callbackType)
{
case SIO_CALLBACK_GET_TX_CHAR:
pChan->getChar = callback;
pChan->getCharArg = callbackArg;
return OK;
case SIO_CALLBACK_PUT_RCV_CHAR:
pChan->putChar = callback;
pChan->putCharArg = callbackArg;
return OK;
case SIO_CALLBACK_ERROR:
default:
return ENOSYS;
}
}
此函数主要是实现接收数据后,如何把数据交给TTY中间件接口putChar,还有如何从tty中间件接口拿到需要发送的数据接口getChar
第四步,实现发送函数TL16C550C_StartUp,这个函数是驱动层的数据发送函数
static void TL16C550C_SendByte(char byte)
{
int LocalBusAddr = 0;
char value = 0;
int errorCount = 0;
do
{
LocalBusAddr = Get_LocalBus_Addr(LSR_REGISTER);
value = *(unsigned short *)LocalBusAddr;
errorCount++;
if(errorCount > 100)
{
TL16C550C_Config();
return;
}
}while(!(value&LSR_THRE_MASK));
LocalBusAddr = Get_LocalBus_Addr(LCR_REGISTER);
value = *(unsigned short *)LocalBusAddr;
value &= ~LCR_DLAB_MASK;
*(unsigned short *)LocalBusAddr = value;
LocalBusAddr = Get_LocalBus_Addr(THR_REGISTER);
*(unsigned short *)LocalBusAddr = byte;
}
第五步,实现配置函数TL16C550C_Ioctrl,此函数主要是设置串口波特率,模式,请缓冲区,校验位,数据位,停止位,根据不同的芯片进行配置
static int TL16C550C_Ioctrl(SIO_CHAN *pSioChan,int cmd,void *arg)
{
M8313_UART_ChAN* pChan = (M8313_UART_ChAN*)pSioChan;
int setArg = (int)arg;
switch (cmd)
{
case SIO_BAUD_SET:
if(setArg != 0)
{
if(setArg < TL16C550C_MIN_BAUD || setArg > TL16C550C_MAX_BAUD)
{
return ERROR;
}
switch(setArg)
{
case 600:
case 1200:
case 2400:
case 4800:
case 9600:
case 19200:
case 38400:
case 56000:
pChan->baund = setArg;
TL16C550C_SetBaund(setArg);
break;
default:
break;
}
}
break;
case SIO_BAUD_GET:
*(int *)arg = pChan->baund;
break;
case SIO_MODE_SET:
if(setArg == SIO_MODE_INT) //中断模式
{
TL16c550cSio.intrMode = TRUE;
TL16C550C_ModeSet(SIO_MODE_INT);
}
else if(setArg == SIO_MODE_POLL)//查询模式
{
TL16c550cSio.intrMode = FALSE;
TL16C550C_ModeSet(SIO_MODE_POLL);
}
break;
case SIO_MODE_GET:
if(TL16c550cSio.intrMode)
{
*(int *)arg = SIO_MODE_INT;
}
else
{
*(int *)arg = SIO_MODE_POLL;
}
break;
case SIO_AVAIL_MODES_GET:
*(int *)arg = SIO_MODE_INT | SIO_MODE_POLL;
break;
case SIO_HW_OPTS_SET:
TL16C550C_LCR(setArg);
break;
default:
break;
}
return OK;
}
因为查询模式的效率较低,所以很少使用查询模式,在此不再叙述
第六步,挂接驱动程序
static void TL16C550C_Init()
{
TL16c550cSio.sio.pDrvFuncs = &TL16C550C_DrvFuncs;
TL16c550cSio.errorCount = 0;
TL16c550cSio.getChar = dummyCallBack;
TL16c550cSio.putChar = dummyCallBack;
TL16c550cSio.intLevel = INUM_IRQ2;
//配置串口芯片
TL16C550C_Config();
TL16c550cSio.intrMode = FALSE;
}
第七步,创建tty设备使能接收中断
void TL16C550C_Open(void)
{
char devName[16] = {0};
TL16C550C_Init();
sprintf(devName, "%s", SERIAL_NAME);
/*将设备驱动注册到tty驱动*/
ttyDevCreate(devName, &TL16c550cSio.sio,RECV_BUF_SIZE, SEND_BUF_SIZE);
//使能串口中断
intConnect ((VOIDFUNCPTR *)INUM_TO_IVEC(TL16c550cSio.intLevel), (VOIDFUNCPTR)TL16C550C_IntIsr, (int)0);
intEnable(TL16c550cSio.intLevel);
}
第八步,实现中断接收数据到tty中间层
static void TL16C550C_IntIsr(void)
{
int LocalBusAddr = 0;
char status= 0;
char Value = 0;
LocalBusAddr = Get_LocalBus_Addr(LSR_REGISTER);
status = *(unsigned short *)LocalBusAddr;
if(status & (LSR_OE_MASK | LSR_PE_MASK |LSR_FE_MASK))//错误
{
//复位串口
TL16c550cSio.errorCount++;
TL16C550C_Config();
}
else if(status & LSR_DR_MASK)
{
LocalBusAddr = Get_LocalBus_Addr(LCR_REGISTER);
Value = *(unsigned short *)LocalBusAddr;
Value &= ~LCR_DLAB_MASK;
*(unsigned short *)LocalBusAddr = Value;
LocalBusAddr = Get_LocalBus_Addr(RBR_REGISTER);
Value = *(unsigned short *)LocalBusAddr;
TL16c550cSio.putChar(TL16c550cSio.putCharArg,Value);
}
}