[通用8051核FLASH系列] 【芯圣电子HC89S105A测评报告】开箱开发环境搭建点灯外设控制

[复制链接]
3505|3
 楼主| yuyy1989 发表于 2023-5-15 23:02 | 显示全部楼层 |阅读模式
本帖最后由 芯圣电子官方QQ 于 2023-7-25 11:02 编辑

1.开箱
微信图片_20230515131347.jpg 微信图片_20230515131408.jpg
没有给焊好排针有点遗憾,找排针焊好后再拍一张,那两排双排的IO孔径比其它的小,我这没有合适的排针先这样插着用了,后面开发板如果有迭代计划**能统一孔径
微信图片_20230515131419.jpg
2.开发环境搭建
2.1到官网下载HC89S105AC8的资料包http://www.holychip.cn/download.php?class_id=102108101
微信截图_20230512112017.png
解压出来内容如图
QQ截图20230515080852.png
先看看1-使用前必读中的开发工具用户手册,里面介绍了开发板和需要的开发环境
参考例程里有一些写好的示例工程供参考
开发板资料文件夹里有开发板的原理图
模块使用手册里面有各模块的使用说明
数据手册,这个就不用多说了,开发中会经常翻
2.2下载KEIL C51并安装https://www.keil.com/download/product/
微信截图_20230512130937.png
2.3安装HC-LINK,前面的资料包里没有放入HC-LINK的安装包,在开发工具\仿真器目录里有个txt里面有链接http://www.holychip.cn/development.php?class_id=102104101 ,注意安装目录选择Keil所在目录,其它的一路next
QQ截图20230514233744.png
成功安装后keil里可以看到芯圣的单片机了
QQ截图20230514233949.png
3.编译例程烧录点灯
打开HC89S105AC8资料包中的参考例程文件夹,里面有很多例程,现在基于ExampleProject这个例程写个简单的程序点亮开发板上的LED
双击ExampleProject\Project\Pro.uvproj打开ExampleProject工程,可以看到这是一个每隔200ms翻转一次P00的程序
QQ截图20230515082129.png
查看原理图可以看到P00接在了LED4上,当P00为低电平时LED4会点亮,先不做改动直接编译并烧录,用示波器看一下
点击这个图标或按F7进行编译
QQ截图20230515133002.png
这个程序很小
QQ截图20230515133244.png
用microusb的数据线连接开发板和PC,win10下会识别出一个串口设备
QQ截图20230515132723.png
点击这个或按F8将程序下载到板子上
QQ截图20230515133128.png
烧录的时候这里会有个蓝条,等它走完就烧录成功了
QQ截图20230515133356.png
烧录后的效果
WeChat_20230515133620 00_00_00-00_00_30.gif
示波器查看
微信图片_20230515140255.jpg

这里有个问题,连接USB时默认不会给MCU开发电路供电,需要下载程序后才会有电,每次重新连接USB都要下载一次程序有点不方便,这个开发板如果有迭代计划的话**官方以后能改进一下
接下来修改一下例程,用按键控制LED的开关,开发板没有配置普通按键,这点比较可惜,后面开发板如果有迭代计划**能增加个普通按键测试用
外接一个按键到P35和GND
需要把P35设为上拉输入模式,查看数据手册
QQ截图20230515141216.png
QQ截图20230515141246.png

要配置P3M2
  1. void main()
  2. {
  3. /********************************系统初始化*******************************************/        
  4.         WDTCCR = 0x00;                           //关闭看门狗
  5.                                                            //本例程为方便测试关闭看门狗,实际使用中,建议客户打开看门狗,详见WDT复位例程
  6.         CLKCON = 0x02;                           //选择内部高频RC为系统时钟,Fosc=32MHz
  7.         CLKDIV = 0x02;                           //Fosc 2分频得到Fcpu,Fcpu=16MHz
  8. /**********************************IO配置初始化***************************************/        
  9.         P0M0 = P0M0 & 0xF0 | 0x08;   //P00设置为推挽输出
  10.         P3M2 = P3M2 & 0x0F | 0x20;   //P35上拉输入
  11.         while (1)
  12.         {
  13.                 if(P3_5 == 0)
  14.                 {
  15.                         Delay_ms(30); //去抖
  16.                         if(P3_5 == 0)
  17.                         {
  18.                                 while(P3_5 == 0); //等待按键松开
  19.                                 P0_0 = !P0_0;
  20.                         }
  21.                 }
  22.         }
  23. }
编译烧录效果如下
WeChat_20230515142258 00_00_00-00_00_30.gif
按键点灯就完成了

4.其它
之后来看看其它功能
4.1串口通讯实现printf输出到串口
参考例程中的代码,例程是用P00 P01进行通讯,这里改成P34 P35,通过串口对LED进行控制,输入1开灯输入0关灯
修改IO可以参考手册中的《7.5.1外设功能引脚映射控制寄存器》
QQ截图20230515191150.png
从中也可以看出外设都可以映射到任意IO上,这样进行布线时会简单得多

代码实现
  1. #define ALLOCATE_EXTERN
  2. #include "HC89S105AC8.h"
  3. #include <stdio.h>
  4. int indata = 0;  //输入的数据
  5. unsigned char rxflag = 0;  //0未收到数据 1已收到数据

  6. //printf输出到串口
  7. char putchar(char c)
  8. {
  9.         IE &=~ 0x10;         //失能UART1中断
  10.         SBUF = c;            //发送数据
  11.         while(!(SCON&0x02));
  12.         SCON &=~ 0x02;       //发送中断请求中断标志位清0
  13.         IE |= 0x10;          //UART1中断使能
  14.         return 0;
  15. }

  16. void main()
  17. {
  18. /********************************系统初始化*******************************************/        
  19.         WDTCCR = 0x00;                           //关闭看门狗
  20.                                                            //本例程为方便测试关闭看门狗,实际使用中,建议客户打开看门狗,详见WDT复位例程
  21.         CLKCON = 0x02;                           //选择内部高频RC为系统时钟,Fosc=32MHz
  22.         CLKDIV = 0x02;                           //Fosc 2分频得到Fcpu,Fcpu=16MHz

  23. /**********************************IO配置初始化***************************************/        
  24.         P0M0 = P0M0 & 0xF0 | 0x08;   //P00设置为推挽输出
  25.         P3M2 = 0x28;                                                   //P34推挽输出 P35上拉输入
  26.         TXD_MAP = 0x1C;                                                //TXD映射P34 00011100
  27.         RXD_MAP = 0x1D;                                                //RXD映射P35 00011101
  28.         
  29.         SBRTH = 0xFCBE/256;       //波特率1200
  30.         SBRTL = 0xFCBE%256;
  31.         SCON2 = 0x12;                                //8位UART 独立波特率,波特率可变
  32.         SCON  = 0x10;                                 //开串口接收

  33.         IE = 0x10;                                         //使能串口中断
  34.         EA = 1;                                                //使能总中断
  35.         while (1)
  36.         {
  37.                 if(rxflag)
  38.                 {
  39.                         printf("您输入的是%d LED已经",indata);
  40.                         if(P0_0)
  41.                         {
  42.                                 printf("关闭\r\n");
  43.                         }
  44.                         else
  45.                         {
  46.                                 printf("打开\r\n");
  47.                         }
  48.                         rxflag = 0;
  49.                         SCON |= 0x10;                                        //使能串口中断
  50.                 }
  51.         }
  52. }

  53. /***************************************************************************************
  54.   * @说明          UART1中断服务函数
  55.   *        @参数          无
  56.   * @返回值 无
  57.   * @注                  无
  58. ***************************************************************************************/
  59. void UART1_Rpt() interrupt UART1_VECTOR
  60. {
  61.         if(SCON&0x01)
  62.         {
  63.                 indata = SBUF;
  64.                 if(indata)
  65.                 {
  66.                         P0_0 = 0;
  67.                 }
  68.                 else
  69.                 {
  70.                         P0_0 = 1;
  71.                 }
  72.                 rxflag = 1;
  73.                 SCON &=~ 0x10;
  74.                 SCON &=~ 0x01;                                     //收到数据后,清除接收标志
  75.         }
  76. }
运行效果
WeChat_20230515214944 00_00_00-00_00_30.gif
4.2点亮幻彩灯带
这里使用的是内置SK6812的灯珠,通讯协议如下
QQ截图20230515154733.png
直接控制IO配合延时函数驱动
代码较多直接放到附件里 驱动SK6812幻彩_main.zip (1.56 KB, 下载次数: 8)
运行效果
WeChat_20230515172907 00_00_00-00_00_30.gif
4.3DHTC12IIC通讯
DHTC12使用IIC进行数据读取,官方提供的例程是软件模拟IIC,先按照这个例程移植一下,P33作为SDA P32作为SCK,P34作为串口TX,将读到的数据通过串口打印出来
QQ截图20230515190314.png QQ截图20230515190330.png
代码较多直接放到附件里 模拟IIC读DHTC12_main.zip (2.85 KB, 下载次数: 5)
运行截图
QQ截图20230515190220.png
HC89S105AC8是支持硬件IIC的,可以参考例程使用
移植代码

  1. void yuyy_dhtc12_iic_init(void)
  2. {
  3.     #if(YUYY_USE_SOFT_IIC)
  4.     yuyy_soft_iic_init();
  5.     #else
  6.         P3M1 = 0xA8;                           //P32推挽输出 P33带上拉开漏输出
  7.         SCL_MAP = 0x1A;                          //SCL映射P32口 00011010
  8.         SDA_MAP = 0x1B;                          //SDA映射P33口 00011011
  9.         IICCON = 0x40;                    //启动IIC模块,时钟位设置好别扭3个bit竟然不连着
  10.     #endif
  11. }

  12. uint8_t yuyy_dhtc12_iic_sendregaddr(uint8_t devaddr,uint16_t regaddr,uint8_t regaddrlen)
  13. {
  14.     #if(YUYY_USE_SOFT_IIC)
  15.     return 0;
  16.     #else
  17.     uint8_t err = 0,u8State,retry = 0,end = 0;
  18.         IICCON &= ~0x08;                //清除中断标志位
  19.         IICCON |= 0x20;                         //起始位发送起始信号
  20.     while(1)
  21.     {
  22.         while(!(IICCON&0x08));
  23.         u8State = IICSTA;
  24.         switch(u8State)
  25.         {
  26.             case 0x08:   //发送完START信号
  27.             case 0x10:   //发送完重复起始信号
  28.                 IICCON &= ~0x20;                //起始位停止起始信号
  29.                 IICDAT = ((devaddr<<1)&0xFE); //写入器件地址
  30.                 break;
  31.             case 0x18:   //发送完SLA+W信号
  32.             case 0x28:   //发送完1字节数据
  33.                 if(regaddrlen > 0)
  34.                 {
  35.                     regaddrlen--;
  36.                     IICDAT = (regaddr>>(regaddrlen*8))&0xFF; //写入数据;
  37.                 }
  38.                 else
  39.                 {
  40.                     end = 1;
  41.                 }
  42.                 break;
  43.             case 0x20:   //发送完SLA+W后从机返回NACK
  44.             case 0x38:   //主机在发送 SLA+W 阶段或者发送数据阶段丢失仲裁  或者  主机在发送 SLA+R 阶段或者回应 NACK 阶段丢失仲裁
  45.                 if(retry < 3)
  46.                     IICCON |= 0x20;
  47.                 else
  48.                     err = 1;
  49.                 retry++;
  50.                 break;
  51.             case 0x30:   //发送完一个数据字节后从机返回NACK
  52.                 err = 1;
  53.                 break;
  54.             default:
  55.                 err = 1;
  56.                 break;
  57.         }
  58.         if(err > 0 || end > 0)
  59.         {
  60.             if(err > 0)
  61.                 IICCON |= 0x10;                 //停止位发送停止信号
  62.             break;
  63.         }
  64.         IICCON &= ~0x08;        //清除中断标志位
  65.     }
  66.     return err;
  67.     #endif
  68. }

  69. uint8_t yuyy_dhtc12_iic_senddatas(uint8_t devaddr,uint16_t regaddr,uint8_t regaddrlen,uint8_t *datas,uint8_t datalen)
  70. {
  71.     #if(YUYY_USE_SOFT_IIC)
  72.     return yuyy_soft_iic_senddatas(devaddr,regaddr,regaddrlen,datas,datalen);
  73.     #else
  74.         uint8_t i = 0,err = 0,u8State;
  75.         err = yuyy_dhtc12_iic_sendregaddr(devaddr,regaddr,regaddrlen);
  76.     if(err > 0)
  77.     {
  78.         return err;
  79.     }
  80.     while(1)
  81.     {
  82.         u8State = IICSTA;
  83.         switch(u8State)
  84.         {
  85.             case 0x28:   //发送完1字节数据
  86.                 if(i < datalen)
  87.                 {
  88.                     IICDAT = datas[i]; //写入数据;
  89.                     i++;
  90.                 }
  91.                 break;
  92.             case 0x30:   //发送完一个数据字节后从机返回NACK
  93.                 err = 1;
  94.                 break;
  95.             default:
  96.                 err = 1;
  97.                 break;
  98.         }
  99.         if(err > 0 || i == datalen)
  100.         {
  101.             IICCON |= 0x10;                 //停止位发送停止信号
  102.             IICCON &= ~0x08;        //清除中断标志位
  103.             break;
  104.         }
  105.         IICCON &= ~0x08;        //清除中断标志位
  106.     }
  107.     return err;
  108.     #endif
  109. }
  110. uint8_t yuyy_dhtc12_iic_readdatas(uint8_t devaddr,uint16_t regaddr,uint8_t regaddrlen,uint8_t *datas,uint8_t datalen)
  111. {
  112.     #if(YUYY_USE_SOFT_IIC)
  113.     return yuyy_soft_iic_readdatas(devaddr,regaddr,regaddrlen,datas,datalen);
  114.     #else
  115.         uint8_t i=0,u8State,err = 0;

  116.     if(regaddrlen > 0)
  117.     {
  118.         err = yuyy_dhtc12_iic_sendregaddr(devaddr,regaddr,regaddrlen);
  119.         if(err > 0)
  120.             return err;
  121.     }
  122.     IICCON |= 0x20;                         //起始位发送起始信号

  123.     while(1)
  124.     {
  125.         while(!(IICCON&0x08));
  126.         u8State = IICSTA;
  127.         switch(u8State)
  128.         {
  129.             case 0x08:   //发送完START信号
  130.             case 0x10:   //发送完重复起始信号
  131.                 IICCON &= ~0x20;                //起始位停止起始信号
  132.                 IICDAT = ((devaddr<<1)|0x01);   //发送从机地址+R字节
  133.             case 0x40:   //发送完SLA+R信号,开始接收数据
  134.                 if(datalen>1)
  135.                 {
  136.                     IICCON |= 0x04;
  137.                 }
  138.                 break;
  139.             case 0x50:   //接收完一字节数据,在接收最后1字节数据之前设置AA=0;
  140.                 if(i==datalen-1)
  141.                 {
  142.                     IICCON |= 0x04;
  143.                 }
  144.                 datas[i++] = IICDAT;
  145.                 break;
  146.             case 0x58:   //接收到一个数据字节,且NACK已回复
  147.                 datas[i++] = IICDAT;
  148.                 break;
  149.             case 0x38:   //主机在发送 SLA+W 阶段或者发送数据阶段丢失仲裁  或者  主机在发送 SLA+R 阶段或者回应 NACK 阶段丢失仲裁
  150.                 err = 1;
  151.                 break;
  152.             case 0x48:   //发送完SLA+R后从机返回NACK
  153.                 err = 1;
  154.                 break;
  155.             default:
  156.                 err = 1;
  157.                 break;
  158.         }
  159.         if(i==datalen || err > 0)
  160.         {
  161.             IICCON |= 0x10;                 //停止位发送停止信号
  162.             IICCON &= ~0x08;        //清除中断标志位
  163.             break;
  164.         }
  165.         IICCON &= ~0x08;        //清除中断标志位
  166.     }
  167.     return err;
  168.     #endif
  169. }
实际运行发现了个问题,温度能正常读出来,湿度一直99.9%,换成软件IIC就没问题
4.4定时器
例程中有个定时器1ms翻转一次P00电平,基于它修改一下,封装一个可变定时的初始化方法,并将P00改成每500ms翻转一次
  1. #define ALLOCATE_EXTERN
  2. #include "HC89S105AC8.h"
  3. #include <stdio.h>
  4. #include <intrins.h>
  5. typedef unsigned char uint8_t;
  6. typedef unsigned short int uint16_t;

  7. uint16_t timerdelay = 0;

  8. void timer0_init(uint16_t us) //最大49152
  9. {
  10.         uint16_t t0hl = 0;
  11.         TCON1 = 0x00;                           //T0定时器时钟为Fper/12
  12.         TMOD = 0x00;                           //方式 0 :16位自动重装
  13.         t0hl = 65536 - us*16/12;//65536 - (us/1000000) *(16000000 / 12)
  14.         TH0 = (t0hl>>8)&0xFF;
  15.         TL0 = t0hl&0xFF;                                //T0定时时间
  16.         TCON = 0x10;                    //使能T0
  17.         IE = 0x02;                                //打开T0中断
  18. }

  19. void main()
  20. {
  21. /********************************系统初始化*******************************************/        
  22.         WDTCCR = 0x00;                           //关闭看门狗
  23.                                                            //本例程为方便测试关闭看门狗,实际使用中,建议客户打开看门狗,详见WDT复位例程
  24.         CLKCON = 0x02;                           //选择内部高频RC为系统时钟,Fosc=32MHz
  25.         CLKDIV = 0x02;                           //Fosc 2分频得到Fcpu,Fcpu=16MHz

  26. /**********************************IO配置初始化***************************************/        
  27.         P0M0 = P0M0 & 0xF0 | 0x08;   //P00设置为推挽输出
  28.         timer0_init(1000);
  29.         EA = 1;
  30.         while (1)
  31.         {
  32.         }
  33. }

  34. void TIMER0_Rpt(void) interrupt TIMER0_VECTOR
  35. {                        
  36.         if(timerdelay > 0)
  37.     timerdelay--;
  38.         else
  39.         {
  40.                 timerdelay = 500;
  41.                 P0_0 = !P0_0;
  42.         }
  43. }
运行效果
174a646d3169b49eb410e1706928f41f 00_00_00-00_00_30.gif
4.5PWM输出,参考例程中的8位PWM输出代码写个简单的呼吸灯

  1. #define ALLOCATE_EXTERN
  2. #include "HC89S105AC8.h"
  3. #include <stdio.h>
  4. #include <intrins.h>
  5. typedef unsigned char uint8_t;
  6. typedef unsigned short int uint16_t;
  7. void Delay_ms(uint16_t fui_i);
  8. void main()
  9. {
  10.         uint8_t toup = 1,pwm=0;
  11. /********************************系统初始化*******************************************/        
  12.         WDTCCR = 0x00;                           //关闭看门狗
  13.                                                            //本例程为方便测试关闭看门狗,实际使用中,建议客户打开看门狗,详见WDT复位例程
  14.         CLKCON = 0x02;                           //选择内部高频RC为系统时钟,Fosc=32MHz
  15.         CLKDIV = 0x02;                           //Fosc 2分频得到Fcpu,Fcpu=16MHz

  16. /**********************************IO配置初始化***************************************/        
  17.         P0M0 = P0M0 & 0xF0 | 0x08;   //P00设置为推挽输出
  18.         PWM3_MAP = 0x00;        //PWM3映射P00口        
  19.         PWM3P = 0xFF;            //PWM周期为0xFF
  20.         PWM3C = 0x9A;         //使能PWM3,关闭中断,允许输出,时钟4分频 PWM3有效期间为低电平
  21.         PWM3D = pwm;
  22.         while (1)
  23.         {
  24.                 if(toup)
  25.                 {
  26.                         pwm++;
  27.                         if(pwm == 0xFF)
  28.                         {
  29.                                 toup = 0;
  30.                         }
  31.                 }
  32.                 else
  33.                 {
  34.                         pwm--;
  35.                         if(pwm == 0)
  36.                         {
  37.                                 toup = 1;
  38.                         }
  39.                 }
  40.                 PWM3D = pwm;
  41.                 Delay_ms(10);
  42.         }
  43. }
  44. /*************************************************************************************
  45.   * @说明          延时函数
  46.   * @参数          fui_i : 延时时间
  47.   * @返回值 无
  48.   * @注         Fcpu = 16MHz,fui_i = 1时,延时时间约为1ms
  49. *************************************************************************************/
  50. void Delay_ms(uint16_t fui_i)
  51. {
  52.         uint16_t fui_j;
  53.         for (; fui_i > 0; fui_i--)
  54.                 for (fui_j = 1596; fui_j > 0; fui_j--);
  55. }
运行效果
WeChat_20230516105723 00_00_00-00_00_30.gif
4.6 SPI驱动12864LCD
移植修改的代码,软硬件SPI均可正常驱动
  1. #define HS12864G18B_CSPIN     P0_1
  2. #define HS12864G18B_RSTPIN    P0_2
  3. #define HS12864G18B_A0PIN     P0_3
  4. #define HS12864G18B_MOSIPIN   P1_1
  5. #define HS12864G18B_SCKPIN    P1_0
  6. #define USE_SOFT_SPI 0

  7. void yuyy_hs12864g18b_spiinit(void)
  8. {
  9.     P0M0 = 0x88;        //P00 P01推挽输出
  10.     P0M1 = 0x88;        //P02 P03推挽输出
  11.     P1M0 = 0x88;        //P10 P11推挽输出
  12.     #if(USE_SOFT_SPI == 0)
  13.     MOSI_MAP=0x09;                      //SPI_MOSI 映射到P11口 输出口
  14.     SCK_MAP=0x08;                      //SPI_SCK  映射到P10口 时钟端

  15.     SPDAT=0x00;                   //数据寄存器写0
  16.     SPSTAT=0x00;                  //状态寄存器清0     
  17.     SPCTL=0xDC;                   //MSB,主机模式,空闲SCK高电平,第2个边沿采样,时钟4分频        
  18.     #endif
  19.     HS12864G18B_CSPIN = 1;
  20. }

  21. void yuyy_hs12864g18b_cs(uint8_t cs)
  22. {
  23.     HS12864G18B_CSPIN = cs;
  24. }

  25. void yuyy_hs12864g18b_rst(uint8_t rst)
  26. {
  27.     HS12864G18B_RSTPIN = rst;
  28. }

  29. void yuyy_hs12864g18b_a0(uint8_t a0)
  30. {
  31.     HS12864G18B_A0PIN = a0;
  32. }


  33. void yuyy_hs12864g18b_delayus(uint16_t us)
  34. {
  35.     us*=2;
  36.     while(--us);
  37. }

  38. void yuyy_hs12864g18b_spiwritebyte(uint8_t dat)
  39. {
  40.     #if(USE_SOFT_SPI == 0)
  41.     SPSTAT = 0xC0;
  42.     SPDAT = dat;
  43.     while(!(SPSTAT&0x80));
  44.     SPSTAT = 0xC0;
  45.     #else
  46.     uint8_t i = 0;
  47.     while(i<8)
  48.     {
  49.         HS12864G18B_SCKPIN = 0;
  50.         if(dat & 0x80)
  51.             HS12864G18B_MOSIPIN = 1;
  52.         else
  53.             HS12864G18B_MOSIPIN = 0;
  54.         
  55.         HS12864G18B_SCKPIN = 1;
  56.         dat <<= 1;
  57.         i++;
  58.     }
  59.     #endif
  60. }
运行效果
微信图片_20230527225430.jpg


 楼主| yuyy1989 发表于 2023-5-16 11:24 | 显示全部楼层
目前所有外设操作都要直接操作寄存器,对于熟悉单片机喜欢手搓寄存器的大佬没什么难度,但是对于新手来说就没那么简单了,希望官方以后能提供库函数,一些小问题例如IO的孔径偏小和USB默认不对MCU供电如果后续开发板有迭代计划也希望改进一下
有何不可0365 发表于 2024-7-31 14:46 | 显示全部楼层
如果你发现开发板上有不同孔径的IO孔,请记下,以便在以后的开发板迭代中统一孔径,避免类似的问题。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:同飞软件研发工程师
简介:制冷系统单片机软件开发,使用PID控制温度

168

主题

826

帖子

10

粉丝
快速回复 在线客服 返回列表 返回顶部