很久没有更新博客了,最近基于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 = *(unsignedshort *)LocalBusAddr;
TL16c550cSio.putChar(TL16c550cSio.putCharArg,Value);
}
}