打印
[通用8051核FLASH系列]

【芯圣电子HC89S105A测评报告】开箱开发环境搭建点灯外设控制

[复制链接]
2611|3
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 芯圣电子官方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
}
运行效果



使用特权

评论回复
沙发
yuyy1989|  楼主 | 2023-5-16 11:24 | 只看该作者
目前所有外设操作都要直接操作寄存器,对于熟悉单片机喜欢手搓寄存器的大佬没什么难度,但是对于新手来说就没那么简单了,希望官方以后能提供库函数,一些小问题例如IO的孔径偏小和USB默认不对MCU供电如果后续开发板有迭代计划也希望改进一下

使用特权

评论回复
板凳
有何不可0365| | 2024-7-31 14:46 | 只看该作者
如果你发现开发板上有不同孔径的IO孔,请记下,以便在以后的开发板迭代中统一孔径,避免类似的问题。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

145

主题

695

帖子

6

粉丝