未元星系
发表于 2024-8-1 18:35:23
本帖最后由 未元星系 于 2024-8-3 15:32 编辑
自学开天斧第23课:
学习内容:小项目(1):计时器(队列存储计次值)
学习简介:通过两个月左右的学习,对这款单片机和开发板有了一定了解,近几天也初步学习了数据结构-队列,本次计划通过完成一个小项目巩固学习过的内容。项目所含知识点:定时器、中断、独立按键、OLED、数据结构-队列项目内容:
①、按P3.5按键 开启/停止计时②、按P3.4按键 清零计时区域数据③、按P3.3按键 计次
④、按P3.2按键 查看计次值(先进先出)实验进步空间:①、EEPROM保存计次数据(尝试了几次都没成功,暂时搁置)②、按P3.5按键 清空计次存储数据+计时区域数据(有bug,需要读取全部计次值后才能正常使用,否则计次数量会错乱)
程序代码:(因代码量太大,这里不展示代码了,需要的直接下载文件)
未元星系
发表于 2024-8-3 15:31:42
自学开天斧第24课:
学习内容:小项目(2):计时器(EEPROM存储计数值)
学习简介:之前对STC8H8K64U的EEPROM的学习只是单纯地存入数据和读出数据,没有结合项目实战,理解不够深入,这次使用EEPROM的库函数制作一个断电存储计次数据的定时器项目所含知识点:GPIO、定时器、中断、独立按键、OLED、EEPROM项目内容:
①、按P3.5按键 开启/停止计时②、按P3.4按键 清零计时区域数据③、按P3.3按键 计次并将数据保存至EEPROM
④、按P3.2按键 查看计次值(读取EEPROM数据)⑤、按P5.4按键 计时器数值、计次值清零,清空存储数据区域(擦除EEPROM第0扇区)程序代码:(因代码量太大,这里只展示主函数,EEPROM相关函数来源于STC库函数)
#include <STC8H.H>
#include "EEPROM.h"
#include "Timer.h"
#include "Timer_Key.h"
#include "OLED.h"
#define Record_MAX 20//存储计次最多数量
bit Timer1_ms_1;
bit Timer1_ms_2;
bit OLED_full_sign;
bit OLED_empty_sign;
unsigned char S_hour;//时
unsigned char S_min; //分
unsigned char S_sec; //秒
unsigned char S_cen; //百分之一秒
unsigned char OLED_past;
unsigned char OLED_know;
unsigned char EEPROM_unit_write;
unsigned char EEPROM_unit_read;
unsigned char Key_number;
unsigned int sign_more_delay;
unsigned int OLED_empty_count;
unsigned int OLED_full_count;
unsigned char Check_empty;
void main()
{
P_SW2 |= 0x80;
P2M0 = 0x00; P2M1 = 0x00;
P3M0 = 0x00; P3M1 = 0x00;
P4M0 = 0x00; P4M1 = 0x00;
P5M0 = 0x00; P5M1 = 0x00;
P6M0 = 0x00; P6M1 = 0x00;
P34 = 1;P35 = 1;P32 = 1;P33 = 1;P54 = 1;//初始化开天斧板载独立按键
Timer0_Init();
Timer1_Init();
OLED_Init();
OLED_ColorTurn(0);//0正常显示,1 反色显示
OLED_DisplayTurn(0);//0正常显示 1 屏幕翻转显示
OLED_Clear();
TR0 = 0;
EA = 1;
OLED_ShowChinese(34,0,0,16);//计
OLED_ShowChinese(54,0,1,16);//时
OLED_ShowChinese(74,0,2,16);//器
OLED_ShowNum(7,3,0,2,16);
OLED_ShowChar(24,3,'h',16);
OLED_ShowNum(38,3,0,2,16);
OLED_ShowChar(55,3,'m',16);
OLED_ShowNum(69,3,0,2,16);
OLED_ShowChar(86,3,'s',16);
OLED_ShowNum(100,3,0,2,16);
OLED_ShowString(117,3,"c",16);
OLED_ShowChinese(0,6,6,16);
OLED_ShowChar(17,6,':',16);
OLED_ShowNum(24,6,0,2,16);
while(1)
{
Key_number = Key();
OLED_past = OLED_know;
OLED_know = S_cen;
if(OLED_past != OLED_know)//if计时变量改变刷新OLED
{
OLED_ShowNum(7,3,S_hour,2,16);
OLED_ShowNum(38,3,S_min,2,16);
OLED_ShowNum(69,3,S_sec,2,16);
OLED_ShowNum(100,3,S_cen,2,16);
}
if(Key_number == 1)//开启/关闭计时器
{
TR0 = ~TR0;
if(TR0 == 0)
{
sign_more_delay = 200;
}
else
{
sign_more_delay = 0;
}
}
if(Key_number == 2)//计时器清零
{
S_cen = 0;S_sec = 0;S_min = 0;S_hour = 0;
}
if(Key_number == 3)//计时器计次
{
OLED_ShowChinese(0,6,6,16);
OLED_ShowChar(17,6,':',16);
if(EEPROM_unit_write >= Record_MAX)//如果EEPROM_unit_write大于等于设定值,显示“满!”
{
OLED_ShowChinese(86,6,3,16);
OLED_ShowChinese(103,6,5,16);
OLED_full_sign = 1;
}
else
{
OLED_ShowNum(24,6,EEPROM_unit_write+1,2,16);
EEPROM_write_n(0x0000+1+EEPROM_unit_write*4,&S_hour,1);
EEPROM_write_n(0x0000+2+EEPROM_unit_write*4,&S_min,1);
EEPROM_write_n(0x0000+3+EEPROM_unit_write*4,&S_sec,1);
EEPROM_write_n(0x0000+4+EEPROM_unit_write*4,&S_cen,1);
EEPROM_unit_write++;
}
}
if(Key_number == 4)//计时器显示计次值
{
OLED_ShowChinese(0,6,7,16);
OLED_ShowChar(17,6,':',16);
EEPROM_read_n(0x0000+4+EEPROM_unit_read*4,&Check_empty,1);
if(Check_empty == 0xff )//如果EEPROM下一字节是被擦除状态(0xff),显示“空!”
{
OLED_ShowChinese(86,6,4,16);//显示“空!”
OLED_ShowChinese(103,6,5,16);
OLED_empty_sign = 1;
}
else
{
EEPROM_read_n(0x0000+1+EEPROM_unit_read*4,&S_hour,1);//读取EEPROM数据
EEPROM_read_n(0x0000+2+EEPROM_unit_read*4,&S_min,1);
EEPROM_read_n(0x0000+3+EEPROM_unit_read*4,&S_sec,1);
EEPROM_read_n(0x0000+4+EEPROM_unit_read*4,&S_cen,1);
OLED_ShowNum(7,3,S_hour,2,16);//显示数据
OLED_ShowNum(38,3,S_min,2,16);
OLED_ShowNum(69,3,S_sec,2,16);
OLED_ShowNum(100,3,S_cen,2,16);
OLED_ShowNum(24,6,EEPROM_unit_read+1,2,16);
EEPROM_unit_read++;
}
}
if(Key_number == 5)
{
EEPROM_SectorErase(0x0000);
EEPROM_unit_read = 0;
EEPROM_unit_write = 0;
S_cen = 0;S_sec = 0;S_min = 0;S_hour = 0;
OLED_ShowNum(24,6,EEPROM_unit_write,2,16);
}
if(OLED_empty_sign == 1)
{
if(Timer1_ms_1 == 1)
{
Timer1_ms_1 = 0;
OLED_empty_count++;
if(OLED_empty_count >= 150+sign_more_delay)
{
OLED_empty_count = 0;
OLED_empty_sign = 0;
OLED_ShowString(86,6," ",16);
}
}
}
if(OLED_full_sign == 1)
{
if(Timer1_ms_2 == 1)
{
Timer1_ms_2 = 0;
OLED_full_count++;
if(OLED_full_count >= 150+sign_more_delay)
{
OLED_full_count = 0;
OLED_full_sign = 0;
OLED_ShowString(86,6," ",16);
}
}
}
}
}
void Timer0_Routine(void) interrupt 1
{
static unsigned char Timer0_cen;//定时器循环计时
Timer0_cen++;
if(Timer0_cen >= 10){Timer0_cen = 0;S_cen++;}
if(S_cen >= 100){S_cen = 0;S_sec++;}
if(S_sec >= 60){S_sec = 0;S_min++;}
if(S_min >= 60){S_min = 0;S_hour++;}
}
void Timer1_Routine(void) interrupt 3
{
static unsigned char T0_Key;
T0_Key++;
Timer1_ms_1 = 1;
Timer1_ms_2 = 1;
if(T0_Key>=20)
{
T0_Key = 0;
Timer_Key();
}
}
未元星系
发表于 2024-8-3 21:44:56
自学开天斧第25课:
学习内容:看门狗
学习简介:单片机的看门狗可以有效预防单片机程序跑飞,死机的情况,通过设定看门狗控制寄存器使用,若看门狗定时器溢出前没有按时喂狗,单片机会自动复位。代码内容:看门狗定时0.629秒,变量num delay 600ms加一,程序正常显示,将delay值变为630ms后单片机循环复位,OLED不显示变量num的变化程序代码:#include <STC8H.H>#include "OLED.h"
#define MAIN_Fosc 40000000L
unsigned char num;unsigned char num_past;unsigned char num_now;
void main(){ num = 10; P_SW2 |= 0x80; P2M0 = 0x00; P2M1 = 0x00; OLED_Init(); OLED_ColorTurn(0); OLED_DisplayTurn(0); OLED_Clear(); OLED_ShowNum(0,0,num,3,16); WDT_CONTR = 0x25;//0.629秒
while(1) { Delay_ms(600);//630 WDT_CONTR = 0x35;//0.629秒喂狗 num++; num_past = num_now; num_now = num; if(num_now != num_past) { OLED_ShowNum(0,0,num,3,16); } }}
未元星系
发表于 2024-8-4 20:08:35
本帖最后由 未元星系 于 2024-8-4 20:09 编辑
自学开天斧第26课:
学习内容:硬件I2C
学习简介:STC8H8K64U单片机内置一个I2C总线控制器,相较于软件I2C速度更快,可以减轻CPU负担代码内容:OLED 硬件I2C 起始、等待应答、发送一字节数据、停止相关函数程序代码:bit busy_hI2C;
void I2C_Start(void)//起始信号{ busy_hI2C = 1; I2CMSCR = 0x81; while(busy_hI2C);
}
void I2C_Stop(void)//结束信号{busy_hI2C = 1; I2CMSCR = 0x86; while(busy_hI2C);}
void I2C_WaitAck(void) //等待信号响应{ busy_hI2C = 1; I2CMSCR = 0x83; while(busy_hI2C);}
void Send_Byte(unsigned char dat)//写入一个字节{ I2CTXD = dat; busy_hI2C = 1; I2CMSCR = 0x82; while(busy_hI2C);}
//中断函数:void I2C_isr(void) interrupt 24{if(I2CMSST & 0x40) { I2CMSST &= ~0x40; busy_hI2C = 0; }}
未元星系
发表于 2024-8-5 23:05:25
自学开天斧第27课:
学习内容:不停电下载(CDC)
学习简介:开天斧开发板下载时需要按P3.2按键再按off按键,比较麻烦,如果想更简便一般需要用下载器。不过STC8H8K64U有不使用下载器同时不断电的方法下载程序,这里介绍CDC的方案。代码内容:OLED按频率闪烁学习重点:需要引入stc32_stc8_usb.h 头文件 ,包含stc_usb_cdc_8h_data.lib 文件在项目中。文件可以在STC-ISP中的“资料下载”中下载。代码现象:不使用烧录器,第一次烧录仍需要按按键断电,第二次开始,只需电脑鼠标操作下载即可自动将程序烧录进单片机,无需断电。程序代码#include <STC8H.H>#include "stc32_stc8_usb.h"//引入头文件
#define MAIN_Fosc 40000000L
//============================//不停电下载(CDC)固定格式char *USER_DEVICEDESC = NULL;char *USER_PRODUCTDESC = NULL;char *USER_STCISPCMD = "@STCISP#";//============================
void Delay_ms(unsigned int ms){ unsigned int i; do{ i = MAIN_Fosc / 10000; while(--i); }while(--ms);}
void main(){ P_SW2 |= 0x80; P2M0 = 0x00; P2M1 = 0x00; //=============================//不停电下载(CDC)固定格式 P3M0 &= ~0x03; P3M1 |= 0x03; IRC48MCR = 0x80; while(!(IRC48MCR & 0x01)); USBCLK = 0x00; USBCON = 0x90; usb_init(); IE2 |= 0x80; //============================= EA = 1; while(1) { P2 = ~P2; Delay_ms(500); P2 = ~P2; Delay_ms(500); //======================================//不停电下载(CDC)固定格式 if(bUsbOutReady) { USB_SendData(UsbOutBuffer,OutNumber); usb_OUT_done(); } //====================================== }}
tingyu8675
发表于 2024-8-7 19:21:17
学习记录得很好,跟着你的笔记学就可以了
未元星系
发表于 2024-8-7 19:52:25
tingyu8675 发表于 2024-8-7 19:21
学习记录得很好,跟着你的笔记学就可以了
过奖了{:4_196:}
未元星系
发表于 2024-8-8 10:08:30
自学开天斧第28课:
学习内容:硬件PWM
学习简介:STC8H8K64U单片机集成了8通道16位高级PWM定时器,第一组四个通道可独立实现PWM输出(可设置带死区的互补对称输出)、捕获、比较功能;第二组四个通道可独立实现PWM输出、捕获、比较功能;功能强大但内容复杂,本次学习PWM1P的输出代码内容:PWM1P配置输出引脚为P2.0,输出周期为10ms,占空比在0%-100%范围按每毫秒0.1%的速度递增或递减,实现P2.0口LED表现呼吸灯。学习重点:硬件PWM寄存器的设置代码现象:P2.0口LED,呼吸灯,周期2s。程序代码:#include <STC8H.H>#include "Timer-all.h"#include "stc32_stc8_usb.h"
//============================//不停电下载(CDC)固定格式char *USER_DEVICEDESC = NULL;char *USER_PRODUCTDESC = NULL;char *USER_STCISPCMD = "@STCISP#";//============================
unsigned int Cycle_1;unsigned int Duty_1;
bit PWM1_Flag;
void main(){ P_SW2 |= 0x80; P1M0 = 0x00;P1M1 = 0x00; P2M0 = 0x00;P2M1 = 0x00; //=============================//不停电下载(CDC)固定格式 P3M0 &= ~0x03; P3M1 |= 0x03; IRC48MCR = 0x80; while(!(IRC48MCR & 0x01)); USBCLK = 0x00; USBCON = 0x90; usb_init(); IE2 |= 0x80; //============================= Timer0_Init(); EA = 1; Cycle_1 = 1000;//周期:1ms Duty_1 = 0;//占空比:50% PWMA_PS |= 0x01;//设置PWM通道1输出引脚为P20、P21 PWMA_CCER1 = 0x00;//清零PWMA_CCER1寄存器 PWMA_CCMR1 = 0x60;//选择PWM模式1 PWMA_CCER1 = 0X01; //开启输入捕获/比较输出
PWMA_PSCRH = (40-1) >> 8;//预分频(系统时钟40M) PWMA_PSCRL = (40-1); PWMA_ARRH = Cycle_1 >> 8;//设置周期 PWMA_ARRL = Cycle_1; PWMA_CCR1H = Duty_1>> 8;//设置占空比 PWMA_CCR1L = Duty_1 ; PWMA_ENO = 0X01;//使能PWM1P输出 PWMA_BKR = 0X80;//使能主输出 PWMA_CR1 = 0X01; //使能PWM计数器
while(1) {
//======================================//不停电下载(CDC)固定格式 if(bUsbOutReady) { USB_SendData(UsbOutBuffer,OutNumber); usb_OUT_done(); } //======================================
}}
void Timer0_Routine(void) interrupt 1{ if(!PWM1_Flag) { Duty_1 ++; if(Duty_1 > Cycle_1) PWM1_Flag = 1; } else { Duty_1 --; if(Duty_1 <= 0) PWM1_Flag = 0; } PWMA_CCR1H = Duty_1>> 8;//设置占空比 PWMA_CCR1L = Duty_1 ;}
未元星系
发表于 2024-8-10 20:50:31
自学开天斧第29课:
学习内容:小项目(三):小电子琴
学习简介:通过此项目学习单片机定时器和各电子模块的组合使用。代码内容:定义音调低音5--高音相应频率的定时器装载值数组(16个音),通过矩阵键盘模块控制选择装载值,蜂鸣器模块按选中的音调频率发音,同时OLED模块实时显示当前按下的音调值。(定时器设为十六位不自动重装模式)学习重点:定时器、矩阵键盘模块、蜂鸣器模块、OLED模块代码现象:矩阵键盘控制蜂鸣器按相应音调鸣叫,OLED显示当前音调值。
未元星系
发表于 2024-8-13 21:05:50
对于开天斧开发板的学习笔记暂时就记录到这里,历时两个多月,经过两轮学习,对8H8K64U这款芯片整体上有了初步认识,收获很大,这个暑假过的十分充实。
感谢官方提供的技术论坛,能够让我寻找各种资料,记录学习;感谢论坛和交流群里每一位予以指点的大佬,帮助我克服困难;最后,也感谢那个一路坚持学习的自己。
同时,我对51单片机的学习不会就此完结,我会在后期初步学习电路设计后尝试做真正的单片机项目,到那时,我会继续记录我的单片机学习旅程,加油!