本帖最后由 芯圣电子官方QQ 于 2023-7-25 11:02 编辑
1.开箱
没有给焊好排针有点遗憾,找排针焊好后再拍一张,那两排双排的IO孔径比其它的小,我这没有合适的排针先这样插着用了,后面开发板如果有迭代计划**能统一孔径
2.开发环境搭建
2.1到官网下载HC89S105AC8的资料包http://www.holychip.cn/download.php?class_id=102108101
解压出来内容如图
先看看1-使用前必读中的开发工具用户手册,里面介绍了开发板和需要的开发环境
参考例程里有一些写好的示例工程供参考
开发板资料文件夹里有开发板的原理图
模块使用手册里面有各模块的使用说明
数据手册,这个就不用多说了,开发中会经常翻
2.2下载KEIL C51并安装https://www.keil.com/download/product/
2.3安装HC-LINK,前面的资料包里没有放入HC-LINK的安装包,在开发工具\仿真器目录里有个txt里面有链接http://www.holychip.cn/development.php?class_id=102104101 ,注意安装目录选择Keil所在目录,其它的一路next
成功安装后keil里可以看到芯圣的单片机了
3.编译例程烧录点灯
打开HC89S105AC8资料包中的参考例程文件夹,里面有很多例程,现在基于ExampleProject这个例程写个简单的程序点亮开发板上的LED
双击ExampleProject\Project\Pro.uvproj打开ExampleProject工程,可以看到这是一个每隔200ms翻转一次P00的程序
查看原理图可以看到P00接在了LED4上,当P00为低电平时LED4会点亮,先不做改动直接编译并烧录,用示波器看一下
点击这个图标或按F7进行编译
这个程序很小
用microusb的数据线连接开发板和PC,win10下会识别出一个串口设备
点击这个或按F8将程序下载到板子上
烧录的时候这里会有个蓝条,等它走完就烧录成功了
烧录后的效果
示波器查看
这里有个问题,连接USB时默认不会给MCU开发电路供电,需要下载程序后才会有电,每次重新连接USB都要下载一次程序有点不方便,这个开发板如果有迭代计划的话**官方以后能改进一下
接下来修改一下例程,用按键控制LED的开关,开发板没有配置普通按键,这点比较可惜,后面开发板如果有迭代计划**能增加个普通按键测试用
外接一个按键到P35和GND
需要把P35设为上拉输入模式,查看数据手册
要配置P3M2
void main()
{
/********************************系统初始化*******************************************/
WDTCCR = 0x00; //关闭看门狗
//本例程为方便测试关闭看门狗,实际使用中,建议客户打开看门狗,详见WDT复位例程
CLKCON = 0x02; //选择内部高频RC为系统时钟,Fosc=32MHz
CLKDIV = 0x02; //Fosc 2分频得到Fcpu,Fcpu=16MHz
/**********************************IO配置初始化***************************************/
P0M0 = P0M0 & 0xF0 | 0x08; //P00设置为推挽输出
P3M2 = P3M2 & 0x0F | 0x20; //P35上拉输入
while (1)
{
if(P3_5 == 0)
{
Delay_ms(30); //去抖
if(P3_5 == 0)
{
while(P3_5 == 0); //等待按键松开
P0_0 = !P0_0;
}
}
}
}
编译烧录效果如下
按键点灯就完成了
4.其它
之后来看看其它功能
4.1串口通讯实现printf输出到串口
参考例程中的代码,例程是用P00 P01进行通讯,这里改成P34 P35,通过串口对LED进行控制,输入1开灯输入0关灯
修改IO可以参考手册中的《7.5.1外设功能引脚映射控制寄存器》
从中也可以看出外设都可以映射到任意IO上,这样进行布线时会简单得多
代码实现
#define ALLOCATE_EXTERN
#include "HC89S105AC8.h"
#include <stdio.h>
int indata = 0; //输入的数据
unsigned char rxflag = 0; //0未收到数据 1已收到数据
//printf输出到串口
char putchar(char c)
{
IE &=~ 0x10; //失能UART1中断
SBUF = c; //发送数据
while(!(SCON&0x02));
SCON &=~ 0x02; //发送中断请求中断标志位清0
IE |= 0x10; //UART1中断使能
return 0;
}
void main()
{
/********************************系统初始化*******************************************/
WDTCCR = 0x00; //关闭看门狗
//本例程为方便测试关闭看门狗,实际使用中,建议客户打开看门狗,详见WDT复位例程
CLKCON = 0x02; //选择内部高频RC为系统时钟,Fosc=32MHz
CLKDIV = 0x02; //Fosc 2分频得到Fcpu,Fcpu=16MHz
/**********************************IO配置初始化***************************************/
P0M0 = P0M0 & 0xF0 | 0x08; //P00设置为推挽输出
P3M2 = 0x28; //P34推挽输出 P35上拉输入
TXD_MAP = 0x1C; //TXD映射P34 00011100
RXD_MAP = 0x1D; //RXD映射P35 00011101
SBRTH = 0xFCBE/256; //波特率1200
SBRTL = 0xFCBE%256;
SCON2 = 0x12; //8位UART 独立波特率,波特率可变
SCON = 0x10; //开串口接收
IE = 0x10; //使能串口中断
EA = 1; //使能总中断
while (1)
{
if(rxflag)
{
printf("您输入的是%d LED已经",indata);
if(P0_0)
{
printf("关闭\r\n");
}
else
{
printf("打开\r\n");
}
rxflag = 0;
SCON |= 0x10; //使能串口中断
}
}
}
/***************************************************************************************
* @说明 UART1中断服务函数
* @参数 无
* @返回值 无
* @注 无
***************************************************************************************/
void UART1_Rpt() interrupt UART1_VECTOR
{
if(SCON&0x01)
{
indata = SBUF;
if(indata)
{
P0_0 = 0;
}
else
{
P0_0 = 1;
}
rxflag = 1;
SCON &=~ 0x10;
SCON &=~ 0x01; //收到数据后,清除接收标志
}
}
运行效果
4.2点亮幻彩灯带
这里使用的是内置SK6812的灯珠,通讯协议如下
直接控制IO配合延时函数驱动
代码较多直接放到附件里
驱动SK6812幻彩_main.zip
(1.56 KB)
运行效果
4.3DHTC12IIC通讯
DHTC12使用IIC进行数据读取,官方提供的例程是软件模拟IIC,先按照这个例程移植一下,P33作为SDA P32作为SCK,P34作为串口TX,将读到的数据通过串口打印出来
代码较多直接放到附件里
模拟IIC读DHTC12_main.zip
(2.85 KB)
运行截图
HC89S105AC8是支持硬件IIC的,可以参考例程使用
移植代码
void yuyy_dhtc12_iic_init(void)
{
#if(YUYY_USE_SOFT_IIC)
yuyy_soft_iic_init();
#else
P3M1 = 0xA8; //P32推挽输出 P33带上拉开漏输出
SCL_MAP = 0x1A; //SCL映射P32口 00011010
SDA_MAP = 0x1B; //SDA映射P33口 00011011
IICCON = 0x40; //启动IIC模块,时钟位设置好别扭3个bit竟然不连着
#endif
}
uint8_t yuyy_dhtc12_iic_sendregaddr(uint8_t devaddr,uint16_t regaddr,uint8_t regaddrlen)
{
#if(YUYY_USE_SOFT_IIC)
return 0;
#else
uint8_t err = 0,u8State,retry = 0,end = 0;
IICCON &= ~0x08; //清除中断标志位
IICCON |= 0x20; //起始位发送起始信号
while(1)
{
while(!(IICCON&0x08));
u8State = IICSTA;
switch(u8State)
{
case 0x08: //发送完START信号
case 0x10: //发送完重复起始信号
IICCON &= ~0x20; //起始位停止起始信号
IICDAT = ((devaddr<<1)&0xFE); //写入器件地址
break;
case 0x18: //发送完SLA+W信号
case 0x28: //发送完1字节数据
if(regaddrlen > 0)
{
regaddrlen--;
IICDAT = (regaddr>>(regaddrlen*8))&0xFF; //写入数据;
}
else
{
end = 1;
}
break;
case 0x20: //发送完SLA+W后从机返回NACK
case 0x38: //主机在发送 SLA+W 阶段或者发送数据阶段丢失仲裁 或者 主机在发送 SLA+R 阶段或者回应 NACK 阶段丢失仲裁
if(retry < 3)
IICCON |= 0x20;
else
err = 1;
retry++;
break;
case 0x30: //发送完一个数据字节后从机返回NACK
err = 1;
break;
default:
err = 1;
break;
}
if(err > 0 || end > 0)
{
if(err > 0)
IICCON |= 0x10; //停止位发送停止信号
break;
}
IICCON &= ~0x08; //清除中断标志位
}
return err;
#endif
}
uint8_t yuyy_dhtc12_iic_senddatas(uint8_t devaddr,uint16_t regaddr,uint8_t regaddrlen,uint8_t *datas,uint8_t datalen)
{
#if(YUYY_USE_SOFT_IIC)
return yuyy_soft_iic_senddatas(devaddr,regaddr,regaddrlen,datas,datalen);
#else
uint8_t i = 0,err = 0,u8State;
err = yuyy_dhtc12_iic_sendregaddr(devaddr,regaddr,regaddrlen);
if(err > 0)
{
return err;
}
while(1)
{
u8State = IICSTA;
switch(u8State)
{
case 0x28: //发送完1字节数据
if(i < datalen)
{
IICDAT = datas[i]; //写入数据;
i++;
}
break;
case 0x30: //发送完一个数据字节后从机返回NACK
err = 1;
break;
default:
err = 1;
break;
}
if(err > 0 || i == datalen)
{
IICCON |= 0x10; //停止位发送停止信号
IICCON &= ~0x08; //清除中断标志位
break;
}
IICCON &= ~0x08; //清除中断标志位
}
return err;
#endif
}
uint8_t yuyy_dhtc12_iic_readdatas(uint8_t devaddr,uint16_t regaddr,uint8_t regaddrlen,uint8_t *datas,uint8_t datalen)
{
#if(YUYY_USE_SOFT_IIC)
return yuyy_soft_iic_readdatas(devaddr,regaddr,regaddrlen,datas,datalen);
#else
uint8_t i=0,u8State,err = 0;
if(regaddrlen > 0)
{
err = yuyy_dhtc12_iic_sendregaddr(devaddr,regaddr,regaddrlen);
if(err > 0)
return err;
}
IICCON |= 0x20; //起始位发送起始信号
while(1)
{
while(!(IICCON&0x08));
u8State = IICSTA;
switch(u8State)
{
case 0x08: //发送完START信号
case 0x10: //发送完重复起始信号
IICCON &= ~0x20; //起始位停止起始信号
IICDAT = ((devaddr<<1)|0x01); //发送从机地址+R字节
case 0x40: //发送完SLA+R信号,开始接收数据
if(datalen>1)
{
IICCON |= 0x04;
}
break;
case 0x50: //接收完一字节数据,在接收最后1字节数据之前设置AA=0;
if(i==datalen-1)
{
IICCON |= 0x04;
}
datas[i++] = IICDAT;
break;
case 0x58: //接收到一个数据字节,且NACK已回复
datas[i++] = IICDAT;
break;
case 0x38: //主机在发送 SLA+W 阶段或者发送数据阶段丢失仲裁 或者 主机在发送 SLA+R 阶段或者回应 NACK 阶段丢失仲裁
err = 1;
break;
case 0x48: //发送完SLA+R后从机返回NACK
err = 1;
break;
default:
err = 1;
break;
}
if(i==datalen || err > 0)
{
IICCON |= 0x10; //停止位发送停止信号
IICCON &= ~0x08; //清除中断标志位
break;
}
IICCON &= ~0x08; //清除中断标志位
}
return err;
#endif
}
实际运行发现了个问题,温度能正常读出来,湿度一直99.9%,换成软件IIC就没问题
4.4定时器
例程中有个定时器1ms翻转一次P00电平,基于它修改一下,封装一个可变定时的初始化方法,并将P00改成每500ms翻转一次
#define ALLOCATE_EXTERN
#include "HC89S105AC8.h"
#include <stdio.h>
#include <intrins.h>
typedef unsigned char uint8_t;
typedef unsigned short int uint16_t;
uint16_t timerdelay = 0;
void timer0_init(uint16_t us) //最大49152
{
uint16_t t0hl = 0;
TCON1 = 0x00; //T0定时器时钟为Fper/12
TMOD = 0x00; //方式 0 :16位自动重装
t0hl = 65536 - us*16/12;//65536 - (us/1000000) *(16000000 / 12)
TH0 = (t0hl>>8)&0xFF;
TL0 = t0hl&0xFF; //T0定时时间
TCON = 0x10; //使能T0
IE = 0x02; //打开T0中断
}
void main()
{
/********************************系统初始化*******************************************/
WDTCCR = 0x00; //关闭看门狗
//本例程为方便测试关闭看门狗,实际使用中,建议客户打开看门狗,详见WDT复位例程
CLKCON = 0x02; //选择内部高频RC为系统时钟,Fosc=32MHz
CLKDIV = 0x02; //Fosc 2分频得到Fcpu,Fcpu=16MHz
/**********************************IO配置初始化***************************************/
P0M0 = P0M0 & 0xF0 | 0x08; //P00设置为推挽输出
timer0_init(1000);
EA = 1;
while (1)
{
}
}
void TIMER0_Rpt(void) interrupt TIMER0_VECTOR
{
if(timerdelay > 0)
timerdelay--;
else
{
timerdelay = 500;
P0_0 = !P0_0;
}
}
运行效果
4.5PWM输出,参考例程中的8位PWM输出代码写个简单的呼吸灯
#define ALLOCATE_EXTERN
#include "HC89S105AC8.h"
#include <stdio.h>
#include <intrins.h>
typedef unsigned char uint8_t;
typedef unsigned short int uint16_t;
void Delay_ms(uint16_t fui_i);
void main()
{
uint8_t toup = 1,pwm=0;
/********************************系统初始化*******************************************/
WDTCCR = 0x00; //关闭看门狗
//本例程为方便测试关闭看门狗,实际使用中,建议客户打开看门狗,详见WDT复位例程
CLKCON = 0x02; //选择内部高频RC为系统时钟,Fosc=32MHz
CLKDIV = 0x02; //Fosc 2分频得到Fcpu,Fcpu=16MHz
/**********************************IO配置初始化***************************************/
P0M0 = P0M0 & 0xF0 | 0x08; //P00设置为推挽输出
PWM3_MAP = 0x00; //PWM3映射P00口
PWM3P = 0xFF; //PWM周期为0xFF
PWM3C = 0x9A; //使能PWM3,关闭中断,允许输出,时钟4分频 PWM3有效期间为低电平
PWM3D = pwm;
while (1)
{
if(toup)
{
pwm++;
if(pwm == 0xFF)
{
toup = 0;
}
}
else
{
pwm--;
if(pwm == 0)
{
toup = 1;
}
}
PWM3D = pwm;
Delay_ms(10);
}
}
/*************************************************************************************
* @说明 延时函数
* @参数 fui_i : 延时时间
* @返回值 无
* @注 Fcpu = 16MHz,fui_i = 1时,延时时间约为1ms
*************************************************************************************/
void Delay_ms(uint16_t fui_i)
{
uint16_t fui_j;
for (; fui_i > 0; fui_i--)
for (fui_j = 1596; fui_j > 0; fui_j--);
}
运行效果
4.6 SPI驱动12864LCD
移植修改的代码,软硬件SPI均可正常驱动
#define HS12864G18B_CSPIN P0_1
#define HS12864G18B_RSTPIN P0_2
#define HS12864G18B_A0PIN P0_3
#define HS12864G18B_MOSIPIN P1_1
#define HS12864G18B_SCKPIN P1_0
#define USE_SOFT_SPI 0
void yuyy_hs12864g18b_spiinit(void)
{
P0M0 = 0x88; //P00 P01推挽输出
P0M1 = 0x88; //P02 P03推挽输出
P1M0 = 0x88; //P10 P11推挽输出
#if(USE_SOFT_SPI == 0)
MOSI_MAP=0x09; //SPI_MOSI 映射到P11口 输出口
SCK_MAP=0x08; //SPI_SCK 映射到P10口 时钟端
SPDAT=0x00; //数据寄存器写0
SPSTAT=0x00; //状态寄存器清0
SPCTL=0xDC; //MSB,主机模式,空闲SCK高电平,第2个边沿采样,时钟4分频
#endif
HS12864G18B_CSPIN = 1;
}
void yuyy_hs12864g18b_cs(uint8_t cs)
{
HS12864G18B_CSPIN = cs;
}
void yuyy_hs12864g18b_rst(uint8_t rst)
{
HS12864G18B_RSTPIN = rst;
}
void yuyy_hs12864g18b_a0(uint8_t a0)
{
HS12864G18B_A0PIN = a0;
}
void yuyy_hs12864g18b_delayus(uint16_t us)
{
us*=2;
while(--us);
}
void yuyy_hs12864g18b_spiwritebyte(uint8_t dat)
{
#if(USE_SOFT_SPI == 0)
SPSTAT = 0xC0;
SPDAT = dat;
while(!(SPSTAT&0x80));
SPSTAT = 0xC0;
#else
uint8_t i = 0;
while(i<8)
{
HS12864G18B_SCKPIN = 0;
if(dat & 0x80)
HS12864G18B_MOSIPIN = 1;
else
HS12864G18B_MOSIPIN = 0;
HS12864G18B_SCKPIN = 1;
dat <<= 1;
i++;
}
#endif
}
运行效果
|