一、安装 Keil C51
此步直接参考网上资料,我这里安装的是 Keil3。
二、安装 WCHISPStudio
去沁恒官网(https://www.wch.cn/downloads/WCHISPTool_Setup_exe.html)下载安装包,按照提示默认安装即可。
三、安装WCH器件库到 Keil
打开 WCHISPStudio软件,点击菜单栏:“功能” -> “添加 WCH MCU 到 KEIL 器件库” 。
我按此操作是失败的,提示:
点确定后,弹出目录选择对话框。我的 Keil 安装在以下位置:
所以选择 “C:\Program Files (x86)\Keil” 目录,但是又提示:
估计是沁恒默认支持的是 Keil4,而我安装的是 Keil3,没办法只能手动添加了。首先需要获得器件库文件,这得去沁恒的技术论坛搜索资源。下面是我找到的链接:
解决方案:https://www.wch.cn/bbs/thread-94733-1.html
文件资源:https://www.wch.cn/uploads/file/20220921/1663759687119278.zip
将下载到的器件库(wch.cdb)放到 Keil 安装目录的 “UV3” 文件夹下,然后编辑安装目录下的 “TOOLS.INI”,在 “[UV2]” 下面添加一行:CDB0=UV3\WCH.CDB ("WCH MCU Database")
至此器件库安装完成。
四、建工程、写代码、编译
1、建工程
2、写代码
官方只提供了 CH552 的头文件资料包 CH552EVT.ZIP(https://www.wch.cn/downloads/CH552EVT_ZIP.html)
代码参考需要使用 CH554 的资料包 CH554EVT.ZIP(https://www.wch.cn/downloads/CH554EVT_ZIP.html),毕竟 CH552 只是比 CH554 少了 USB HOST 支持。
将 CH552EVT.ZIP 里的 “CH552.H” 复制到项目目录下。
再将 CH554EVT.ZIP 里 EXAM/Public 目录下的 “Debug.C”、“Debug.H” 复制到项目目录下。
另外再新建一个 “Main.C” 的空文件。
然后将*.C文件添加到 Keil 项目(注意,一定不要落掉 “Debug.C”,我开始就是没添加Debug.C,编写下载都成功了,就是运行无效果。)
修正Debug.C文件中的头文件引用:
编辑Main.C文件,添加如下代码:(main函数里调用的前三个函数是在Debug.C中实现的。printf内容会输出到串口,是Keil对printf底层调用函数putchar进行了重写,重写位置在C:\Program Files (x86)\Keil\C51\LIB\PUTCHAR.C)
#include "CH552.H"
#include "Debug.H"
#include "stdio.h"
#include <string.h>
#pragma NOAREGS
void main( )
{
CfgFsys(); //时钟选择配置
mDelaymS(20);
mInitSTDIO(); //串口0初始化
printf("start ...\n");
while(1){
}
}
3、编译
编译前先设置:
代码保存后编译:
如果要编译CH554EVT.ZIP中的其他示例,比如
只需将CompositeKM.C内容复制到Main.C中,修正头文件引用路径,然后编译即可。
Keil C51编译器会将所有添加到项目的C文件编译(未添加的,即使在项目文件夹下也不编译),头文件无需添加到项目。
Keil C51对文件名如何取并无要求,方便识别管理即可,但要求添加到项目的C文件中,有且仅有一个main函数。
五、连接下载
打开下载软件,按如下设置:
断电状态下按住开发板的下载键,然后连接USB上电,连接成功后就会自动下载了。(下载过程中可以松开下载键)
六、验证
前面的代码效果是:开发板上电后,会在串口0输出 “start ...” 字符串,下面验证效果。
我这里用到一个USB转串口模块,方便将串口的输出显示到电脑串口调试软件上。
先将USB转串口模块连接到电脑,打开串口调试软件,并打开USB转串口模块对应的串口端口,波特率使用57600。(如果USB转串口模块是第一次使用记得先装驱动)
CH552开发板 与 USB转串口模块 按下图所示连接。
然后连接开发板的USB接口,给开发板供电,观看串口调试软件有无收到信息。
【2025/05/03 补充说明】
最近玩 STC8G1K08A 这颗单片机,也是51内核、用 Keil 开发,入门过程与本文类似,顺带也复盘了下本文,发现有以下几个点需要补充下:
1)器件库装不上,事实上不一定非要去沁恒论坛下,完全可以手动添加。
首先我们来查看一下 Keil 自带的设备数据库(这里说的设备数据库,跟前面说的器件库,以及后面可能会提到的CPU数据库,都是同一个/同一类东西)。
可以看到右上角有一个 “添加” 按钮。没错!就是可以通过它来添加我们需要的 CH552 芯片。
信息怎么填?对比自动安装成功的器件库信息,照着填即可,这里直接给出:
CPU=IRAM(0-0xFF) XRAM(0-0x3FF) IROM(0-0x3FFF) CLOCK(24000000)
MON=S8051.DLL TP51.DLL("-p51")
SIM=S8051.DLL DP51.DLL("-pDR8051")
SFILE="LIB\STARTUP.A51" ("Standard 8051 Startup Code")
REGFILE=CH552.H("WCH")
下次建工程时,就可以选择 “Generic CPU Data Base” > “WCH” > “Ch552” 了。
注意,芯片数据只能添加/更改,不能删除。为防止搞乱默认数据库,手动添加时最好备份下默认数据库。默认位置在:C:\Keil\UV3\UV3.cdb,其中的数字3将根据你安装的版本不同而不同。
查看数据库时,双击芯片名称,进入芯片参数设置模式,只能 “更新”,不能 “添加”,单击厂商名称即可退出设置模式,此时可 “添加” 芯片。
2)头文件管理
细心的读者可能会发现,前面添加芯片信息时,最后一行有 “CH552.H” 字样,是的!Keil有专门管理芯片头文件的地方,而不用每次都把头文件放到工程当中。
我们只需把 CH552.H 文件放到 C:\Keil\C51\INC\WCH 下
WCH文件夹名称对应: REGFILE=CH552.H("WCH") 中括号内的名称。
放好后,工程中即可通过 “#include <CH552.H> ” 将头文件包含到工程中。
3)评论区有网友提到以前的方法在编译时有警告,经验证,确实是因为 Debug.c 中有定义了而未调用的代码,可以只拷出用到的代码到 main.c,而不再包含 Debug.c、Debug.h 文件。
#include <CH552.H>
#include "stdio.h"
#define FREQ_SYS 12000000 //系统主频12MHz
#ifndef UART0_BUAD
#define UART0_BUAD 57600
#define UART1_BUAD 57600
#endif
/*******************************************************************************
* Function Name : CfgFsys( )
* Description : CH554时钟选择和配置函数,默认使用Fsys 6MHz,FREQ_SYS可以通过
CLOCK_CFG配置得到,公式如下:
Fsys = (Fosc * 4/(CLOCK_CFG & MASK_SYS_CK_SEL);具体时钟需要自己配置
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void CfgFsys( )
{
// SAFE_MOD = 0x55;
// SAFE_MOD = 0xAA;
// CLOCK_CFG |= bOSC_EN_XT; //使能外部晶振
// CLOCK_CFG &= ~bOSC_EN_INT; //关闭内部晶振
SAFE_MOD = 0x55;
SAFE_MOD = 0xAA;
// CLOCK_CFG = CLOCK_CFG & ~ MASK_SYS_CK_SEL | 0x07; // 32MHz
// CLOCK_CFG = CLOCK_CFG & ~ MASK_SYS_CK_SEL | 0x06; // 24MHz
// CLOCK_CFG = CLOCK_CFG & ~ MASK_SYS_CK_SEL | 0x05; // 16MHz
CLOCK_CFG = CLOCK_CFG & ~ MASK_SYS_CK_SEL | 0x04; // 12MHz
// CLOCK_CFG = CLOCK_CFG & ~ MASK_SYS_CK_SEL | 0x03; // 6MHz
// CLOCK_CFG = CLOCK_CFG & ~ MASK_SYS_CK_SEL | 0x02; // 3MHz
// CLOCK_CFG = CLOCK_CFG & ~ MASK_SYS_CK_SEL | 0x01; // 750KHz
// CLOCK_CFG = CLOCK_CFG & ~ MASK_SYS_CK_SEL | 0x00; // 187.5MHz
SAFE_MOD = 0x00;
}
/*******************************************************************************
* Function Name : mDelayus(UNIT16 n)
* Description : us延时函数
* Input : UNIT16 n
* Output : None
* Return : None
*******************************************************************************/
void mDelayuS( UINT16 n ) // 以uS为单位延时
{
#ifdef FREQ_SYS
#if FREQ_SYS <= 6000000
n >>= 2;
#endif
#if FREQ_SYS <= 3000000
n >>= 2;
#endif
#if FREQ_SYS <= 750000
n >>= 4;
#endif
#endif
while ( n ) { // total = 12~13 Fsys cycles, 1uS @Fsys=12MHz
++ SAFE_MOD; // 2 Fsys cycles, for higher Fsys, add operation here
#ifdef FREQ_SYS
#if FREQ_SYS >= 14000000
++ SAFE_MOD;
#endif
#if FREQ_SYS >= 16000000
++ SAFE_MOD;
#endif
#if FREQ_SYS >= 18000000
++ SAFE_MOD;
#endif
#if FREQ_SYS >= 20000000
++ SAFE_MOD;
#endif
#if FREQ_SYS >= 22000000
++ SAFE_MOD;
#endif
#if FREQ_SYS >= 24000000
++ SAFE_MOD;
#endif
#if FREQ_SYS >= 26000000
++ SAFE_MOD;
#endif
#if FREQ_SYS >= 28000000
++ SAFE_MOD;
#endif
#if FREQ_SYS >= 30000000
++ SAFE_MOD;
#endif
#if FREQ_SYS >= 32000000
++ SAFE_MOD;
#endif
#endif
-- n;
}
}
/*******************************************************************************
* Function Name : mDelayms(UNIT16 n)
* Description : ms延时函数
* Input : UNIT16 n
* Output : None
* Return : None
*******************************************************************************/
void mDelaymS( UINT16 n ) // 以mS为单位延时
{
while ( n ) {
#ifdef DELAY_MS_HW
while ( ( TKEY_CTRL & bTKC_IF ) == 0 );
while ( TKEY_CTRL & bTKC_IF );
#else
mDelayuS( 1000 );
#endif
-- n;
}
}
/*******************************************************************************
* Function Name : mInitSTDIO()
* Description : CH554串口0初始化,默认使用T1作UART0的波特率发生器,也可以使用T2
作为波特率发生器
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void mInitSTDIO( )
{
UINT32 x;
UINT8 x2;
SM0 = 0;
SM1 = 1;
SM2 = 0; //串口0使用模式1
//使用Timer1作为波特率发生器
RCLK = 0; //UART0接收时钟
TCLK = 0; //UART0发送时钟
PCON |= SMOD;
x = 10 * FREQ_SYS / UART0_BUAD / 16; //如果更改主频,注意x的值不要溢出
x2 = x % 10;
x /= 10;
if ( x2 >= 5 ) x ++; //四舍五入
TMOD = TMOD & ~ bT1_GATE & ~ bT1_CT & ~ MASK_T1_MOD | bT1_M1; //0X20,Timer1作为8位自动重载定时器
T2MOD = T2MOD | bTMR_CLK | bT1_CLK; //Timer1时钟选择
TH1 = 0-x; //12MHz晶振,buad/12为实际需设置波特率
TR1 = 1; //启动定时器1
TI = 1;
REN = 1; //串口0接收使能
}
void main( )
{
CfgFsys(); //时钟选择配置
mDelaymS(20);
mInitSTDIO(); //串口0初始化
printf("test start ...\n");
while(1){
}
}