Detailed explanation of Qinheng eight serial port expansion chip CH438
[Copy link]
Many MCUs, such as the 51 MCU, have only one or two serial ports. Therefore, sometimes you may encounter a situation where the serial ports are not enough. At this time, you can change to a MCU with more serial ports. Sometimes you don't want to change to a MCU, so you can use the eight-expansion serial port chip CH438 introduced in this article. The chip is from Qinheng. Qinheng's chips are used a lot, very distinctive, and of great quality. Without further ado, here is a brief introduction.
The feature of CH438 chip is that it has 8 extended serial ports in a single chip, each serial port has a 128-byte FIFO, which can save your MCU's RAM and prevent the buffer from overflowing due to failure to read the data in the receive buffer. The access interface of CH438 is a parallel port, which uses more pins, and the data pin and address pin can be shared. The periphery is simple and only requires one crystal oscillator. The circuit diagram is shown above.
现在来上代码
int main(void)
{
uint8_t UARTx;
InitSysClk(); //Initial system clock
InitSysTick(); //Initial sysTick timer, interrupt once per millisecond
PortConfig(); //IO Port configure, please modify this function to configure the IO port
CH438_PinInit();
for (UARTx=0; UARTx<16; UARTx++)
{
CH438_UARTxInit(UARTx);
}
while (1)
{
CH438SerialsProcess();
}
}
/*********************************************************************************************************
CH438 串口处理
*********************************************************************************************************/
void CH438SerialsProcess(void)
{
uint8_t UARTx;
uint8_t res;
for (UARTx=0; UARTx<16; UARTx++)
{
res = CH438_UARTxRcv(UARTx, CH438UARTRcvBuf, &CH438UARTRcvLen);
if (res == 0) //Hardware error
{
break;
}
if (CH438UARTRcvLen)
{
CH438_UARTxSend(UARTx, CH438UARTRcvBuf, CH438UARTRcvLen);
}
}
}
During initialization, first call CH438_PinInit() to configure the parallel port IO port, then call CH438_UARTxInit(UARTx) to configure each serial port, then you can use CH438_UARTxRcv to read the data in the receive buffer, and use CH438_UARTxSendByte and CH438_UARTxSend to send data.
The driver of CH438 is as follows
/*********************************************************************************************************
** 功能: CH438引脚初始化
** 输入: 无
** 返回: 无
** 说明: CH438引脚初始化
*********************************************************************************************************/
void CH438_PinInit()
{
CH438_CSHigh();
CH438_ALELow();
CH438_WRHigh();
CH438_RDHigh();
}
/*********************************************************************************************************
** 功能: CH438写寄存器
** 输入: Addr:寄存器地址
** 输入: RegValue:寄存器值
** 返回: 无
** 说明: CH438写寄存器
*********************************************************************************************************/
void CH438_WriteReg(uint8_t Addr, uint8_t RegValue)
{
CH438_SetDPort(Addr);
CH438_CSLow();
CH438_ALEHigh();
CH438_ALELow();
CH438_SetDPort(RegValue);
CH438_WRLow();
CH438_WRHigh();
CH438_CSHigh();
}
/*********************************************************************************************************
** 功能: CH438读寄存器
** 输入: Addr:寄存器地址
** 返回: 寄存器值
** 说明: CH438读寄存器
*********************************************************************************************************/
uint8_t CH438_ReadReg(uint8_t Addr)
{
uint8_t Data;
CH438_SetDPort(Addr);
CH438_CSLow();
CH438_ALEHigh();
CH438_ALELow();
CH438_SetDPortIn();
CH438_RDLow();
Data = CH438_ReadDPort();
CH438_RDHigh();
CH438_CSHigh();
return (Data);
}
/*********************************************************************************************************
** 功能: 检测访问CH438芯片是否正常
** 输入: 无
** 返回: 0 异常; 1 正常
** 说明: 通过往一个寄存器写入值,然后读出判断是否一致,来检测访问是否正常
*********************************************************************************************************/
uint8_t CH438_CheckRW()
{
static uint8_t CheckData=0;
uint8_t Temp;
CH438_WriteReg(REG_SCR_ADDR, CheckData);
Temp = CH438_ReadReg(REG_SCR_ADDR);
if (CheckData != Temp)
{
CheckData ++;
CH438RWErrorFlag = 1;
return 0;
}
CheckData ++;
CH438RWErrorFlag = 0;
return 1;
}
/*********************************************************************************************************
** 功能: 初始化CH438串口UARTx
** 输入: UARTx: UARTx, 0~7
** 返回: 0 成功;1 失败,访问出错
** 说明: 初始化CH438串口UARTx
*********************************************************************************************************/
uint8_t CH438_UARTxInit(uint8_t UARTx)
{
uint16_t DL = CH438_CLK/12/16/UARTsBaud[UARTx];
uint8_t UARTxBase = UARTsBase[UARTx];
uint8_t LCR;
if (!CH438_CheckRW())
{
return (0);
}
//先复位UARTx
CH438_WriteReg(UARTxBase+REG_IER_ADDR, 1<<7);
//设置UARTx的波特率
LCR = CH438_ReadReg(UARTxBase+REG_LCR_ADDR);
LCR |= (1<<7);
CH438_WriteReg(UARTxBase+REG_LCR_ADDR, LCR); //DLAB置1
CH438_WriteReg(UARTxBase+REG_DLL_ADDR, DL);
CH438_WriteReg(UARTxBase+REG_DLM_ADDR, DL >> 8);
LCR &= ~(1<<7);
CH438_WriteReg(UARTxBase+REG_LCR_ADDR, LCR); //DLA清0
//设置数据格式: 无校验; 1个停止位; 8个数据位
CH438_WriteReg(UARTxBase+REG_LCR_ADDR,
(0 << 4) //校验模式 0:奇校验; 1:偶检验; 2:1检验; 3:0检验
| (0 << 3) //校验使能 0:禁能; 1:使能
| (0 << 2) //停止位 0:1个停止位; 1:2个停止位
| (3 << 0)); //字长. 0:5 bits; 1:six bits; 2:7 bits; 3:8 bits
//使能FIFO模式,清空FIFO
CH438_WriteReg(UARTxBase+REG_FCR_ADDR, 0x07);
return (1);
}
/*********************************************************************************************************
** 功能: CH438 UARTx发送一个字节
** 输入: UARTx: UARTx, 0~7
** 输入: Data: 要发送的字节
** 返回: 0 成功;1 失败,访问出错
** 说明: 同步模式,发送完毕,函数才返回
*********************************************************************************************************/
uint8_t CH438_UARTxSendByte(uint8_t UARTx, uint8_t Data)
{
uint8_t UARTxBase = UARTsBase[UARTx];
if (!CH438_CheckRW())
{
return (0);
}
while((CH438_ReadReg(UARTxBase+REG_LSR_ADDR) & (1<<5)) == 0); //等待发送保持寄存器THR空
CH438_WriteReg(UARTxBase+REG_THR_ADDR, Data);
while((CH438_ReadReg(UARTxBase+REG_LSR_ADDR) & (1<<6)) == 0); //等待发送完毕
return (1);
}
/*********************************************************************************************************
** 功能: CH438 UARTx发送
** 输入: UARTx: UARTx, 0~7
** 输入: pData: 发送数据指针
** 输入: Num: 发送字节数
** 返回: 0 成功;1 失败,访问出错
** 说明: 同步模式,发送完毕,函数才返回
*********************************************************************************************************/
uint8_t CH438_UARTxSend(uint8_t UARTx, uint8_t *pData, uint16_t Num)
{
uint8_t UARTxBase = UARTsBase[UARTx];
if (!CH438_CheckRW())
{
return (0);
}
while (Num--)
{
while((CH438_ReadReg(UARTxBase+REG_LSR_ADDR) & (1<<5)) == 0); //等待发送保持寄存器THR空
CH438_WriteReg(UARTxBase+REG_THR_ADDR, *(pData++));
}
while((CH438_ReadReg(UARTxBase+REG_LSR_ADDR) & (1<<6)) == 0); //等待发送完毕
return (1);
}
/*********************************************************************************************************
** 功能: CH438 UARTx发送字符串
** 输入: UARTx: UARTx, 0~7
** 输入: str: 要发送的字符串
** 返回: 0 成功;1 失败,访问出错
** 说明: 同步模式,发送完毕,函数才返回
*********************************************************************************************************/
uint8_t CH438_UARTxSendString(uint8_t UARTx, char *str)
{
uint8_t UARTxBase = UARTsBase[UARTx];
if (!CH438_CheckRW())
{
return (0);
}
while (*str != '\0')
{
while((CH438_ReadReg(UARTxBase+REG_LSR_ADDR) & (1<<5)) == 0); //等待发送保持寄存器THR空
CH438_WriteReg(UARTxBase+REG_THR_ADDR, *(str++));
}
while((CH438_ReadReg(UARTxBase+REG_LSR_ADDR) & (1<<6)) == 0); //等待发送完毕
return (1);
}
/*********************************************************************************************************
** 功能: CH438 UARTx读取数据
** 输入: UARTx: UARTx, 0~7
** 输出: pData: 存放读取数据的指针
** 输出: pRcvNum: 读取到的字节数
** 返回: 0 成功;1 失败,访问出错
** 说明: 同步模式,发送完毕,函数才返回
*********************************************************************************************************/
uint8_t CH438_UARTxRcv(uint8_t UARTx, uint8_t *pData, uint8_t *pRcvNum)
{
uint8_t RcvNum = 0;
uint8_t LSR;
uint8_t UARTxBase = UARTsBase[UARTx];
if (!CH438_CheckRW())
{
return (0);
}
while (1)
{
LSR = CH438_ReadReg(UARTxBase+REG_LSR_ADDR);
if (LSR & (BIT_LSR_BREAKINT | BIT_LSR_FRAMEERR | BIT_LSR_PARERR | BIT_LSR_OVERR)) //接收错误
{
CH438_ReadReg(UARTxBase+REG_RBR_ADDR);
}
else if (LSR & BIT_LSR_DATARDY) //接收缓冲有数据
{
*pData++ = CH438_ReadReg(UARTxBase+REG_RBR_ADDR);
RcvNum ++;
}
else
{
break;
}
if (RcvNum == 128)
{
break;
}
}
*pRcvNum = RcvNum;
return (1);
}
|