CH559单片机 USB CDC虚拟串口调试信息输出案例

Posted perseverance52

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CH559单片机 USB CDC虚拟串口调试信息输出案例相关的知识,希望对你有一定的参考价值。

【CH559单片机 】USB CDC虚拟串口调试信息输出案例


⛳功能说明

🎯本示例功能基于上面这一篇的延伸,在原来的基础上,新增了调试信息输出。将串口接收数据的转发函数和调试信息输出函数做了分离。消除编译重载报警信息,重载函数为什么不用关键字reentrant来消除这个报警,如果使用了重载关键字,表面上消除了一个bug,其实掩埋了一个更大的bug在里面,将会导致USB CDC虚拟调试串口输出的信息变成乱码,这是在调试时发现的一个坑!

📝原本想从Arduino当中将CH55x系列的核心库函数当中移植过来的,相关函数移植过来没有实现出功能,转而从上面的示例进行改造。原本是想打造一个能支持像printf函数,那样给信息调试带来极大的方便和数据类型可操作性。当然如果你只是想利用此系列单片机简单的开发或验证一些项目,那么推荐使用Arduino框架去验证和开发。

程序目前发现的Bug问题

程序烧录后,通过串口调试软件选择USBCDC 虚拟串口打开后,需要发送任意一条字符串才能激活主动输出。

主程序代码

/********************************** (C) COPYRIGHT *******************************
* File Name          : CDC虚拟串口数据环路示例
* Author             : WCH
* Version            : V1.0
* Date               : 2015/05/20
* Description        : CH559模拟串口
*******************************************************************************/
#include <CH559.H>
#include <string.h>
#include "DEBUG.C"
//#include <stdio.h>
sfr P1_MOD_OC       = 0x92;         // port 1 output mode: 0=push-pull, 1=open-drain
sfr P1_DIR_PU       = 0x93;         // port 1 direction for push-pull or pullup enable for open-drain
sbit led =P1^2;
sbit led2 =P2^4;
sbit led3 =P2^5;
//volatile  UINT8 xdata USBByteCountEP2 = 0;      //Bytes of received data on USB endpoint
//volatile  UINT8 xdata USBBufOutPointEP2 = 0;    //Data pointer for fetching
//volatile bit UpPoint2BusyFlag  = 0;   //Flag of whether upload pointer is busy
//volatile  UINT8 xdata controlLineState = 0;
// UINT8 xdata usbWritePointer = 0;
#define THIS_ENDP0_SIZE         DEFAULT_ENDP0_SIZE

UINT8X	Ep0Buffer[THIS_ENDP0_SIZE] _at_ 0x0000;                                //端点0 OUT&IN缓冲区,必须是偶地址
UINT8X	Ep2Buffer[2*MAX_PACKET_SIZE] _at_ 0x0008;                              //端点2 IN&OUT缓冲区,必须是偶地址
UINT8X  Ep1Buffer[MAX_PACKET_SIZE] _at_ 0x00a0;

UINT8	  SetReqtp,SetupReq,SetupLen,UsbConfig,Flag=1;
PUINT8  pDescr;	                                                               
UINT8   num = 0;
UINT8   LEN = 0;
USB_SETUP_REQ	           SetupReqBuf;                                          //暂存Setup包
#define UsbSetupBuf     ((PUSB_SETUP_REQ)Ep0Buffer)

 
UINT8C DevDesc[18]=0x12,0x01,0x10,0x01,0xff,0x00,0x02,0x08,                   //设备描述符
                    0x86,0x1a,0x23,0x55,0x04,0x03,0x00,0x00,
                    0x00,0x01;

UINT8C CfgDesc[39]=0x09,0x02,0x27,0x00,0x01,0x01,0x00,0x80,0xf0,              //配置描述符,接口描述符,端点描述符
	                  0x09,0x04,0x00,0x00,0x03,0xff,0x01,0x02,0x00,           
                    0x07,0x05,0x82,0x02,0x20,0x00,0x00,                        //批量上传端点
		                0x07,0x05,0x02,0x02,0x20,0x00,0x00,                        //批量下传端点      
			              0x07,0x05,0x81,0x03,0x08,0x00,0x01;                       //中断上传端点

UINT8C DataBuf[26]=0x30,0x00,0xc3,0x00,0xff,0xec,0x9f,0xec,0xff,0xec,0xdf,0xec,
                    0xdf,0xec,0xdf,0xec,0x9f,0xec,0x9f,0xec,0x9f,0xec,0x9f,0xec,
                    0xff,0xec;
UINT8 RecBuf[64];
/*******************************************************************************
* Function Name  : USBDeviceCfg()
* Description    : USB设备模式配置
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void USBDeviceCfg()

    USB_CTRL = 0x00;                                                           //清空USB控制寄存器
    USB_CTRL &= ~bUC_HOST_MODE;                                                //该位为选择设备模式
    USB_CTRL |=  bUC_DEV_PU_EN | bUC_INT_BUSY | bUC_DMA_EN;					           //USB设备和内部上拉使能,在中断期间中断标志未清除前自动返回NAK
    USB_DEV_AD = 0x00;                                                         //设备地址初始化

    UDEV_CTRL &= ~bUD_RECV_DIS;                                                //使能接收器
//  USB_CTRL |= bUC_LOW_SPEED;    
//  UDEV_CTRL |= bUD_LOW_SPEED;                                                //选择低速1.5M模式

    USB_CTRL &= ~bUC_LOW_SPEED;
    UDEV_CTRL &= ~bUD_LOW_SPEED;                                               //选择全速12M模式,默认方式

    UDEV_CTRL |= bUD_DP_PD_DIS | bUD_DM_PD_DIS;                                //禁止DM、DP下拉电阻
    UDEV_CTRL |= bUD_PORT_EN;                                                  //使能物理端口



/*******************************************************************************
* Function Name  : USBDeviceIntCfg()
* Description    : USB设备模式中断初始化
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void USBDeviceIntCfg()

    USB_INT_EN |= bUIE_SUSPEND;                                                //使能设备挂起中断
    USB_INT_EN |= bUIE_TRANSFER;                                               //使能USB传输完成中断
    USB_INT_EN |= bUIE_BUS_RST;                                                //使能设备模式USB总线复位中断
    USB_INT_FG |= 0x1F;                                                        //清中断标志
    IE_USB = 1;                                                                //使能USB中断
    EA = 1; 																                                   //允许单片机中断



/*******************************************************************************
* Function Name  : USBDeviceEndPointCfg()
* Description    : USB设备模式端点配置
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void USBDeviceEndPointCfg()

    UEP2_DMA = Ep2Buffer;                                                      //端点2数据传输地址																			                                         
    UEP2_3_MOD |= bUEP2_TX_EN;                                                 //端点2发送使能
    UEP2_3_MOD |= bUEP2_RX_EN;                                                 //端点2接收使能
    UEP2_3_MOD &= ~bUEP2_BUF_MOD;                                              //端点2单64字节发送缓冲区、单64字节接收缓冲区,共128字节
    UEP2_CTRL = bUEP_AUTO_TOG | UEP_T_RES_NAK | UEP_R_RES_ACK;								 //端点2自动翻转同步标志位,IN事务返回NAK,OUT返回ACK
	
	
	  UEP1_DMA = Ep1Buffer;                                                      //端点1数据传输地址																			                                         
    UEP4_1_MOD |= bUEP1_TX_EN;                                                 //端点1发送使能
//  UEP4_1_MOD |= bUEP1_RX_EN;                                                 //端点1接收使能
    UEP4_1_MOD &= ~bUEP1_BUF_MOD;                                              //端点1单64字节发送缓冲区
    UEP1_CTRL = bUEP_AUTO_TOG | UEP_T_RES_NAK | UEP_R_RES_ACK;								 //端点1自动翻转同步标志位,IN事务返回NAK,OUT返回ACK
		
    UEP0_DMA = Ep0Buffer;                                                      //端点0数据传输地址
    UEP4_1_MOD &= ~(bUEP4_RX_EN | bUEP4_TX_EN);								                 //端点0单64字节收发缓冲区
    UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;                                 //OUT事务返回ACK,IN事务返回NAK



/*******************************************************************************
* Function Name  : SendData( PUINT8 SendBuf )
* Description    : 发送数据给主机串口
* Input          : PUINT8 SendBuf
* Output         : None
* Return         : None
*******************************************************************************/
void SendData( PUINT8 SendBuf ) // 不能用重载reentrant

	 if(Flag==1)                             
	 
     while(LEN > 32)		 
     memcpy(&Ep2Buffer[MAX_PACKET_SIZE],SendBuf,32);
	   UEP2_T_LEN = 32;
	   UEP2_CTRL &= ~(bUEP_T_RES1 | bUEP_T_RES0);	
     while(( UEP2_CTRL & MASK_UEP_T_RES ) == UEP_T_RES_ACK);                  //
     LEN -= 32;
     
     memcpy(&Ep2Buffer[MAX_PACKET_SIZE],SendBuf,LEN);
	   UEP2_T_LEN = LEN;
	   UEP2_CTRL &= ~(bUEP_T_RES1 | bUEP_T_RES0);		 		
     Flag = 0;		 
   

void CDC_Send( PUINT8 SendBuf )

     while(LEN > 32)		 
     memcpy(&Ep2Buffer[MAX_PACKET_SIZE],SendBuf,32);
	   UEP2_T_LEN = 32;
	   UEP2_CTRL &= ~(bUEP_T_RES1 | bUEP_T_RES0);	
     while(( UEP2_CTRL & MASK_UEP_T_RES ) == UEP_T_RES_ACK);                  //
     LEN -= 32;
     
     memcpy(&Ep2Buffer[MAX_PACKET_SIZE],SendBuf,LEN);
	   UEP2_T_LEN = LEN;
	   UEP2_CTRL &= ~(bUEP_T_RES1 | bUEP_T_RES0);		 		
		 


/*******************************************************************************
* Function Name  : RecieveData()
* Description    : USB设备模式端点配置
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void RecieveData()

	  memcpy(RecBuf,Ep2Buffer,USB_RX_LEN); 
	  UEP2_CTRL = UEP2_CTRL & ~ MASK_UEP_R_RES | UEP_R_RES_NAK;                    //默认应答ACK
	  Flag = 1;



/*******************************************************************************
* Function Name  : DeviceInterrupt()
* Description    : CH559USB中断处理函数
*******************************************************************************/
void	DeviceInterrupt( void ) interrupt INT_NO_USB using 1                       //USB中断服务程序,使用寄存器组1
   
	UINT8 len; 
	if(UIF_TRANSFER)                                                               //USB传输完成标志
  
    switch (USB_INT_ST & (MASK_UIS_TOKEN | MASK_UIS_ENDP))
    
			 case UIS_TOKEN_OUT | 2:                                                   //endpoint 2# 中断下传					 
						LEN = USB_RX_LEN; 
			      RecieveData();
            SendData(RecBuf);			 
						break;
		   case UIS_TOKEN_IN | 2:                                                    //endpoint 2# 中断上传
            UEP2_T_LEN = 0;	                                                     //预使用发送长度一定要清空						 
	          UEP2_CTRL = UEP2_CTRL & ~ MASK_UEP_R_RES | UEP_R_RES_ACK;                    //默认应答ACK					 
    			  UEP2_CTRL = UEP2_CTRL & ~ MASK_UEP_T_RES | UEP_T_RES_NAK;            //默认应答NAK
						break;
    	 case UIS_TOKEN_SETUP | 0:                                                  //SETUP事务
            len = USB_RX_LEN;
            if(len == (sizeof(USB_SETUP_REQ)))
               
							 SetReqtp = UsbSetupBuf->bRequestType;
               SetupLen = UsbSetupBuf->wLengthL;
               len = 0;                                                           //默认为成功并且上传0长度,标准请求                                                              
               SetupReq = UsbSetupBuf->bRequest;
               if(SetReqtp == 0xc0)
						   
								  Ep0Buffer[0] = DataBuf[num];
								  Ep0Buffer[1] = DataBuf[num+1];
								  len = 2;
								  if(num<24)
								  	
								    num += 2;
									
									else
									
										num = 24;
									
						   
					     else if(SetReqtp == 0x40)
						   
							    len = 9;                                                        //保证状态阶段,这里只要比8大,且不等于0xff即可
						   
						   else
						    
							    switch(SetupReq)                                                //请求码
							    
								     case USB_GET_DESCRIPTOR:
											    switch(UsbSetupBuf->wValueH)
											    
													   case 1:	                                            //设备描述符
																 pDescr = DevDesc;                                //把设备描述符送到要发送的缓冲区
																 len = sizeof(DevDesc);								       
													   break;	 
													   case 2:									                            //配置描述符
																 pDescr = CfgDesc;                                //把配置描述符送到要发送的缓冲区
																 len = sizeof(CfgDesc);
													   break;	
													   default:
																 len = 0xff;                                      //不支持的命令或者出错
													   break;
											     
									         if ( SetupLen > len ) SetupLen = len;                  //限制总长度
									         len = SetupLen >= 8 ? 8 : SetupLen;                    //本次传输长度
									         memcpy(Ep0Buffer,pDescr,len);                          //加载上传数据
									         SetupLen -= len;
									         pDescr += len;
										       break;						 
							        case USB_SET_ADDRESS:
										       SetupLen = UsbSetupBuf->wValueL;                       //暂存USB设备地址
										       break;
							        case USB_GET_CONFIGURATION:
									         Ep0Buffer[0] = UsbConfig;
									         if ( SetupLen >= 1 ) len = 1;
									         break;
							        case USB_SET_CONFIGURATION:
									         UsbConfig = UsbSetupBuf->wValueL;
									         break;
							        default:
										       len = 0xff;                                            //操作失败
										       break;    
							       
					        
				      
					    else
					    
							    len = 0xff;                                                     //包长度错误
					    

						  if(len == 0xff)
						  
								  SetupReq = 0xFF;
								  UEP0_CTRL = bUEP_R_TOG | bUEP_T_TOG | UEP_R_RES_STALL | UEP_T_RES_STALL;//STALL				     
						  
						  else if(len <= 8)                                                         //上传数据或者状态阶段返回0长度包
						  
								  UEP0_T_LEN = len;
								  UEP0_CTRL = bUEP_R_TOG | bUEP_T_TOG | UEP_R_RES_ACK | UEP_T_RES_ACK;  //默认数据包是DATA1,返回应答ACK
						  
						  else
						  
								  UEP0_T_LEN = 0;                                                       //虽然尚未到状态阶段,但是提前预置上传0长度数据包以防主机提前进入状态阶段
								  UEP0_CTRL = bUEP_R_TOG | bUEP_T_TOG | UEP_R_RES_ACK | UEP_T_RES_ACK;  //默认数据包是DATA1,返回应答ACK				     
						  
					    break;
				 case UIS_TOKEN_IN | 0:                                                         //endpoint0 IN
						  switch(SetupReq)
						  
							   case USB_GET_DESCRIPTOR:
								      len = SetupLen >= 8 ? 8 : SetupLen;                               //本次传输长度
											memcpy( Ep0Buffer, pDescr, len );                                 //加载上传数据
											SetupLen -= len;
											pDescr += len;
											UEP0_T_LEN = len;
											UEP0_CTRL ^= bUEP_T_TOG;                                          //同步标志位翻转
								      break;
							   case USB_SET_ADDRESS:
											USB_DEV_AD = USB_DEV_AD & bUDA_GP_BIT | SetupLen;
											UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
								      break;
							   default:
								      UEP0_T_LEN = 0;                                                    //状态阶段完成中断或者是强制上传0长度数据包结束控制传输
								      UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
								      break;
						  
						  break;
				 case UIS_TOKEN_OUT | 0:                                                 // endpoint0 OUT
							len = USB_RX_LEN;
							UEP0_T_LEN = 0;                                                    //虽然尚未到状态阶段,但是提前预置上传0长度数据包以防主机提前进入状态阶段
							UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_ACK;                         //默认数据包是DATA0,返回应答ACK									
						  break;
					default:
						  break;
				
				UIF_TRANSFER = 0;                                                        //写0清空中断  
    
    if(UIF_BUS_RST)                                                              //设备模式USB总线复位中断
    
			USB_DEV_AD = 0x00;
			UIF_SUSPEND = 0;
			UIF_TRANSFER = 0;
			UIF_BUS_RST = 0;                                                           //清中断标志
    
	  if (UIF_SUSPEND) 
		                                                                            //USB总线挂起/唤醒完成
			UIF_SUSPEND = 0;
			if ( USB_MIS_ST & bUMS_SUSPEND ) 
			                                                                          //挂起
				while ( XBUS_AUX & bUART0_TX );                                          //等待发送完成
				SAFE_MOD = 0x55;
				SAFE_MOD = 0xAA;
				WAKE_CTRL = bWAK_BY_USB | bWAK_RXD0_LO;                                  //USB或者RXD0有信号时可被唤醒
				PCON |= PD;                                                              //睡眠
				SAFE_MOD = 0x55;
				SAFE_MOD = 0xAA;
				WAKE_CTRL = 0x00;
			
     
	  else 
	                                                                               //意外的中断,不可能发生的情况
		  USB_INT_FG = 0x00;                                                          //清中断标志
	        

/*
void USBSerial_flush()
    if (!UpPoint2BusyFlag && usbWritePointer>0)
        UEP2_T_LEN = usbWritePointer;                                                   
        UEP2_CTRL = UEP2_CTRL & ~ MASK_UEP_T_RES | UEP_T_RES_ACK;            //Respond ACK
        UpPoint2BusyFlag = 1;
        usbWritePointer = 0;
    


UINT8 USBSerial_print_n(UINT8 * buf,  int len)   //3 bytes generic pointer, not using USBSerial_write for a bit efficiency
    UINT16 waitWriteCount;
    if (controlLineState > 0) 
        while (len>0)
            waitWriteCount = 0;
            while (UpPoint2BusyFlag)//wait for 250ms or give up, on my mac it takes about 256us
                waitWriteCount++;
                mDelayuS(5);   
                if (waitWriteCount>=50000) return 0;
            
            while (len>0)
                if (usbWritePointer<MAX_PACKET_SIZE)
                    Ep2Buffer[MAX_PACKET_SIZE+usbWritePointer] = *buf++;
                    usbWritePointer++;
                    len--;
                else
                    USBSerial_flush();  //go back to first while
                    break;
                
            
        
    
    return 0;

*/
void main()

	mDelaymS(30);                                                               //上电延时
  CfgFsys( );                                                                   //CH559时钟选择配置    
    mDelaymS(30);  
	mInitSTDIO( ); //串口0,可以用于调试
	  USBDeviceCfg(); //设备模式配置
    USBDeviceEndPointCfg();//端点配置
    USBDeviceIntCfg();//中断初始化
	  UEP0_T_LEN = 0;
    UEP1_T_LEN = 0;	                                                              //预使用发送长度一定要清空	
    UEP2_T_LEN = 0;	 

  PORT_CFG |= (bP0_DRV << 1); //P1端口驱动电流最大20mA
	PORT_CFG |= (bP0_DRV << 2); //P2端口驱动电流最大20mA
	P1_MOD_OC &= 0xfb;//P12设置推挽模式
  P1_DIR_PU |= 0x04;
	
//P24-P25设置推挽模式
	  PORT_CFG &= ~(bP0_OC << 2);
		P2_DIR |= 0x30;
		P2_PU = 0;
	
	led =0;
	led2=0;
	led3=1;	
    while(1)
    
		led = ~led;
    CDC_Send("Hello world! \\n");
 		mDelaymS( 1000 ); 
			led2= ~led2;
			led3 = ~led3;
		CDC_Send("perseverance51 \\n");	
		CH559L单片机基于Arduino框架下实现USB CDC虚拟串口打印ADC数据

CH552单片机 USB CDC虚拟串口调试信息输出案例

CH559L单片机CDC虚拟串口下数据环路测试案例

CH559l单片机入门篇通过串口打印内部ID信息

STC USB-CDC 虚拟串口使用

CH559L单片机常用外设功能-串口介绍