在做项目的过程中,经常发生UART口不够用的情况,可以用GPIO口来软件模拟UART。
硬件MCU型号:华芯微特SWM181(其它MCU大同小异)
实现功能:debug串口
原理:GPIO+定时器
DBG_RX_T Dbg_Rx = {
{0x00},
RX_BUF_LEN - 1,
0,
0,
0,
DBG_RX_IDLE
};
DBG_TX_T Dbg_Tx = {
NULL,
0,
0,
0,
0
};
#define DBG_TX_LOW() GPIO_ClrBit(GPIOA, PIN1)
#define DBG_TX_HIGH() GPIO_SetBit(GPIOA, PIN1)
#define DBG_RX_Value() GPIO_GetBit(GPIOA, PIN0)
/******************************************************************************************************************************************
* 函数名称: Dbg_Rx_Clear()
* 功能说明: 接收模块状态清除
* 输 入: 无
* 输 出: 无
* 注意事项: 无
******************************************************************************************************************************************/
static void Dbg_Rx_Clear(void)
{
Dbg_Rx.ChrIdx = 0;
Dbg_Rx.State = DBG_RX_IDLE;
}
/******************************************************************************************************************************************
* 函数名称: Dbg_RX_State()
* 功能说明: IOUART接收模块当前状态
* 输 入: 无
* 输 出: uint32_t IOUART_RX_IDLE、IOUART_RX_BUSY、IOUART_RX_SUCCESS、UOUART_RX_TIMEOUT、IOUART_RX_FRAMERR
* 注意事项: 无
******************************************************************************************************************************************/
uint32_t Dbg_RX_State(void)
{
return Dbg_Rx.State;
}
/******************************************************************************************************************************************
* 函数名称: Dbg_RX_Count()
* 功能说明: IOUART接收模块接收到的字符数
* 输 入: 无
* 输 出: uint32_t 接收到的字符个数
* 注意事项: 无
******************************************************************************************************************************************/
uint32_t Dbg_RX_Count(void)
{
return Dbg_Rx.ChrIdx;
}
#define DBG_RX_IDLE 0 //空闲中,接收到停止位进入
#define DBG_RX_BUSY 1 //接收中,接收到起始位进入
#define DBG_RX_BUFFULL 2 //接收满,停止接收
#define DBG_RX_TIMEOUT 3 //收超时,停止接收
#define DBG_RX_FRAMERR 4 //帧错误,停止接收
#define DBG_RX_PARITYERR 5 //奇偶校验错误,停止接收
#define DBG_STOP_1 0
#define DBG_STOP_2 1
#define DBG_STOP DBG_STOP_1
#define DBG_PARITY_NONE 0
#define DBG_PARITY_EVEN 1
#define DBG_PARITY_ODD 2
#define DBG_PARITY DBG_PARITY_NONE
#define RX_BUF_LEN 16
typedef struct
{
char Buff[RX_BUF_LEN]; //接收:Buf
uint32_t ChrCnt; //接收字符数
volatile uint32_t ChrIdx; //接收:字符数索引号
volatile uint32_t BitIdx; //接收:字符位索引号
volatile uint32_t TimeoutCnt; //接收:超时数量
volatile uint32_t State; //接收状态
}DBG_RX_T;
typedef struct
{
char* Buff; //发送:Buf
uint32_t ChrCnt; //发送字符数
volatile uint32_t ChrIdx; //发送:字符数索引号
volatile uint32_t BitIdx; //发送:字符位索引号
volatile uint32_t TxBusy; //发送状态
}DBG_TX_T;
1:硬件初始化端口
void Dbg_Init(void)
{
DBG_TIM_BAUD_9600 = SystemCoreClock/9600;
DBG_TIM_BAUD_9600_X_1_5 = SystemCoreClock/9600*1.5 * 0.85;
//tx init
GPIO_Init(GPIOA, PIN1, 1, 0, 0, 0); //GPIOA.1初始化为输出
DBG_TX_HIGH(); //UART空闲时输出高电平
TIMR_Init(TIMR0, TIMR_MODE_TIMER, SystemCoreClock/9600, 1);
IRQ_Connect(IRQ0_15_TIMR0, IRQ0_IRQ, 1); //定时器2中断链接到IRQ0中断线,高优先级
//rx init
TIMR_Init(TIMR1, TIMR_MODE_TIMER, SystemCoreClock/9600, 1);
IRQ_Connect(IRQ0_15_TIMR1, IRQ1_IRQ, 3);
GPIO_Init(GPIOA, PIN0, 0, 1, 0, 0); //GPIOA.0初始化为输入,上拉
EXTI_Init(GPIOA, PIN0, EXTI_FALL_EDGE); //下降沿触发中断
IRQ_Connect(IRQ0_15_GPIOA0, IRQ2_IRQ, 2); //A0引脚外部中断链接到IRQ1中断线,高优先级
EXTI_Open(GPIOA, PIN0);
}
2:接收GPIO引脚中断服务函数
void IRQ2_Handler(void)
{
if(EXTI_State(GPIOA, PIN0) == 0) //避免init,内核产生中断
{
return;
}
EXTI_Clear(GPIOA, PIN0); //清除中断标志
if(Dbg_Rx.State == DBG_RX_IDLE) //有必要检测是否真产生中断
{
Dbg_Rx.BitIdx = 0;
Dbg_Rx.State = DBG_RX_BUSY;
TIMR_Stop(TIMR1);
TIMR_SetPeriod(TIMR1, DBG_TIM_BAUD_9600_X_1_5);
TIMR_Start(TIMR1);
}
}
3:接收位定时器中断服务函数
void IRQ1_Handler(void)
{
static char ChrShf; //接收移位寄存器
//printf("[%s]: 22222 \r\n",__func__);
if(TIMR_INTStat(TIMR1))
{
TIMR_INTClr(TIMR1);
if(Dbg_Rx.State == DBG_RX_BUSY)
{
//1: 接收到第0位
if(Dbg_Rx.BitIdx == 0)
{
TIMR_Stop(TIMR1);
TIMR_SetPeriod(TIMR1, DBG_TIM_BAUD_9600);
TIMR_Start(TIMR1);
}
//2: 接收完8位数据
if(Dbg_Rx.BitIdx < 8)
{
if(DBG_RX_Value())
{
ChrShf |= (1 << Dbg_Rx.BitIdx);
}
else
{
ChrShf &= ~(1 << Dbg_Rx.BitIdx);
}
}
//3: 接收到停止位
else if(Dbg_Rx.BitIdx == 8)
{
Dbg_Rx.State = DBG_RX_IDLE;
Dbg_Rx.Buff[Dbg_Rx.ChrIdx] = ChrShf;
if(DBG_RX_Value() == 1)
{
Dbg_Rx.ChrIdx++;
if(Dbg_Rx.ChrIdx == Dbg_Rx.ChrCnt)
{
Dbg_Rx.State = DBG_RX_BUFFULL;
EXTI_Close(GPIOA, PIN0);
}
}
else
{
Dbg_Rx.State = DBG_RX_FRAMERR;
EXTI_Close(GPIOA, PIN0);
}
}
Dbg_Rx.BitIdx++;
Dbg_Rx.TimeoutCnt = 0;
}
//4: 接收超时完成
else if((Dbg_Rx.State == DBG_RX_IDLE) && (Dbg_Rx.ChrIdx != 0))
{
Dbg_Rx.TimeoutCnt++;
if(Dbg_Rx.TimeoutCnt == 20) //2个字符时间
{
Dbg_Rx.State = DBG_RX_TIMEOUT;
EXTI_Close(GPIOA, PIN0);
TIMR_Stop(TIMR1);
Dbg_Rx.TimeoutCnt = 0;
//解析字符
Dbg_Rx_Data_Parse(Dbg_Rx.Buff,Dbg_Rx.ChrCnt+1);
Dbg_Rx_Clear();
EXTI_Open(GPIOA, PIN0);
}
}
}
}
4:接收数据解析函数
通过接收特定数据,执行特定任务
static void Dbg_Rx_Data_Parse(char *buf , uint32_t len)
{
uint32_t i = 0;
uint8_t sw = 0x00;
//uint8_t p1 = 0, p2 = 0;
for(i=0;i<len;i++)
{
if(buf[i] == '\r')
{
if(buf[0] == 'V' && buf[1] == 'E' && buf[2] == 'R' && buf[3] == ':')
{
sw = 0x01;
}
else if(buf[0] == 'H' && buf[1] == 'W' && buf[2] == 'F' && buf[3] == ':')
{
sw = 0x02;
}
else if(buf[0] == '4' && buf[1] == 'G' && buf[2] == 'D' && buf[3] == ':')
{
sw = 0x03;
}
}
}
switch(sw)
{
case 0x01: //打印软硬件本号
hal_version_test();
break;
case 0x02: //打印软硬件本号
hw_fun();
break;
case 0x03: //打印软硬件本号
//Ml307a_Lauch_Init();
memset(Ml307a_Rx.Buff,0x00,M_4G_BUF_LEN);
//Ml307a_Mqtt_Connect();
Ml307a_Send_Cmd(AT_SET_MQTTSUB_CMD,AT_SET_MQTTSUB_RSP);
break;
default:
printf("[%s]: debug data parse is error!!! sw = %d buf = %s len = %d \r\n",__func__,sw,buf,len);
}
}
5:发送中断服务函数
void IRQ0_Handler(void)
{
if(TIMR_INTStat(TIMR0))
{
TIMR_INTClr(TIMR0);
if(Dbg_Tx.TxBusy == 1)
{
if(Dbg_Tx.BitIdx == 0)
{
DBG_TX_LOW(); //起始位
}
else if(Dbg_Tx.BitIdx < 9)
{
if(Dbg_Tx.Buff[Dbg_Tx.ChrIdx] & (0x01 << (Dbg_Tx.BitIdx-1)))
DBG_TX_HIGH();
else
DBG_TX_LOW();
}
else if(Dbg_Tx.BitIdx == 9)
{
DBG_TX_HIGH(); //停止位
Dbg_Tx.ChrIdx++;
if(Dbg_Tx.ChrIdx < Dbg_Tx.ChrCnt)
{
Dbg_Tx.BitIdx = 0;
goto T3Svr_End; //跳过“BitIdx++”,否则发不出起始位
}
else
{
TIMR_Stop(TIMR0);
Dbg_Tx.TxBusy = 0;
}
}
Dbg_Tx.BitIdx++;
}
T3Svr_End:
;
}
}
6:实现串口打印log信息,重写fputc
int fputc(int ch, FILE *f)
{
Dbg_Tx_Send((char *)&ch, 1);
while(Dbg_TX_IsBusy());
return ch;
}