STC32G128K芯片学习打卡贴
本帖最后由 好学天上 于 2024-5-31 17:46 编辑实验箱教学视频打卡
第一至三集 评论区打卡
第四集 点灯打卡 2024-04-18
LED点亮原理:两边电压差
GPIO:软件可以读取引脚输入电平或控制引脚输入电平(总算搞明白)
基本的书写规则
查找:ctrl+f
不掉电下载:根据视频加了lib文件和库文件,不知道为啥还是报错,也找了范例程序还是没找出原因。
作业点亮所以LED灯,按照之前学的,可以一个一个点亮P60 =0; P61=0....也可以P6=0x00即0000 0000八个口全置0点亮。
工程基本流程已掌握。
希望有老师可以解决一下我的问题
(os:讲课的老师手真好看嘻嘻)
已解决,没设置memory model,这也是和51不一样的地方;
第五集 C语言运算符打卡 2024-04-19
这节课的内容都比较基础,是在学校学C语言和单片机的时候都讲过
还有一个printf函数的使用,进制转换那些我们以前考试经常考的基础题,以及与或非之类的逻辑运算。还有变量类型以及它的大小。没有特别大的难度
第六集 流水灯打卡 2024-04-20
1. u16 → unsigned int
2.while和do while的区别:while一开始就做判断,判断条件为真则进入循环;do while会先执行一遍循环体,再做条件判断是否继续执行循环体;while比do while应用更多
3.--a和a--的区别: 若a=10; →--a=9;a=9;而a--=9;a=9a--是先进行取值,后进行自减;--a是先进行自减,后进行取值;
4.编程中常见小错误:忘记写分号;关键词写错;
5.#dedine用法,相当于把需要定义的内容换一个名字 。
哎哟 我已经掌握了图片排列的奥秘!
函数使用三步: 定义→声明→调用
课后练习:SOS灯光
没有一行一行写,但感觉应该有更简便的方法。
第七集 按键 打卡 2024-04-22
1.按键的原理:两个引脚间的通断:按下之后导通(常开);按下之后断开(常闭);
2.注意:机械开关断开闭合时都会有抖动,所以要靠变成消抖,判断按键状态后等待10ms后再次判断开关状态,此为消抖
3.按键应用:按下点亮松开熄灭;按下熄灭松开点亮;按一下取反;按一下led灯左移一位
4.数组的应用:定义→赋值,索引是长度减1,因为是从0开始。
5.课后作业:按下一次,LED右移一位
第八集 蜂鸣器 打卡 2024-04-23
1.蜂鸣器:无源蜂鸣器和有源蜂鸣器,区别是有源蜂鸣器内部有震荡,通电即可打开,而无源直流信号无法令其运行;
2.控制原理:和LED相同
3.电磁炉实战:按键配合LED和蜂鸣器,其实是把前几节课都结合起来了。
ps:老师好像没有在写第二个功能模式按键的时候做开机判断,这样即使在关机模式也可以功能切换吧。
4.课后作业:第三个启动按键,按下对应功能模式闪烁,且功能切换不可用
第九-十集数码管打卡 2024-04-24
1.数码管:多个发光二极管组成
2.控制原理:和LED类似,共阳给0亮,共阴给1亮
3.按键循环0-9:
4.静态显示作业:H 0x89;J 0xF0;L 0xC7;n 0xAD;o 0xA3;P 0x8C;U 0xC1;t 0x87;r 0x8F
按键1自加循环0-9,按键2控制蜂鸣器鸣叫对应数字次数
5.数码管动态显示原理:每一位数码管轮流点亮,但间隔时间只有1ms,是因为人无法分辨50Hz以上的刷新频率二产生视觉残留,看起来好像是一起亮的。
6.10秒免单计数器
7.注意事项:写代码过程中会有很多不注意的小细节,简单的书写错误,格式错误都会在最后要调试很多次,还是写得太少。
8.课后作业:简易时钟00.00.00,三十秒时闹钟响,蜂鸣器鸣叫3s;
本来想的是把闹钟设置直接放函数中判断,打开之后延时3s后关闭,但这样会干扰到本身的时钟运转,所以就设置了一个时钟标志位,将三秒的时间放入到时钟计数当中。但其实这种时钟计时方式应该本身就会不那么准确的。
第十一-十二集 定时计数器打卡 2024-04-25
1.定时器是定时/计数器的统称,定时器是硬件计时或每隔一段时间完成一次操作,可以替代长时间延时,提高CPU运行效率和处理速度,能及时响应某个事件;
2.相关的寄存器:TMOD,使用模式0定时器时,值为0x00;AUXR寄存器,设置分频,默认为12分频;定时器工作模式有模式0-16位自动重载,模式1-16位不自动重载,模式2-8位自动重载,模式3-不可屏蔽中断的16位自动重载,其中断优先级最高;
3.定时器初始化步骤:设置模式TMOD→设置分频AUXR→设置时间TH,TL→启动定时器TR0→使能定时器中断ET0→使能总中断EA
4.作业:用定时器驱动时钟并设置启动暂停按键
主函数
定时器函数;
5.计数器:计数器用途有电机测速等,输出信号带高低电平变化都可以用计数器;
6.计数器的配置:
按键P35每按下一次,led灯P60状态取反一次;
7.例程:电机测速模拟,用按键模拟脉冲
8.作业:2s周期改为每分钟:
9.思考:如果计数器溢出了程序要怎么写? 答:说实话这节课还有点云里雾里。
第十三集 TIM多任务处理打卡 2024-04-26
1.创建程序文件三步;程序定义三步,函数定义三步;这里前面的课程都有提到过。
2.extern:在a文件里要使用b文件的变量时,用extern在b的库文件中定义该变量,再在a.c中包含b的库文件;
3.bdata位寻址变量定义,例如定义一个八位变量使用bdata,可以分别定义八位的每一位,单独赋值。
4.static静态变量,静态变量时的赋值只一次有效;
5.数码管LED的程序文件:
2024-04-28接上文
6.按键的函数文件,不使用while循环,改用状态机的模式,在主函数中定时使用循环读取每个按键状态的函数,再在使用到某个按键时,使用另一个函数直接读取该按键的状态,返回定义好的状态返回值,有未按下,抖动,按下,按下结束,长按,长按结束这些状态,再在主函数当中通过判断该按键状态来书写功能函数:
key.h
key.c
主函数中功能实现
7.蜂鸣器的函数文件,通过设置蜂鸣器鸣叫时间的函数来决定蜂鸣器要不要叫,要叫多久,再在主函数中10ms循环运行蜂鸣器运行函数,当设置的时间等于0,蜂鸣器不鸣叫,大于0,则鸣叫该时间后关闭。
beep.h
beep.c
beep在主函数中调用使用。
8.定时器函数文件,只需要将之前在主函数写的定时器初始化移到定时器c文件中,在主函数里调用函数使用;而中断函数最好仍在主函数中书写,因其需要调用不同的功能函数,所以放在主函数更佳。
9.课后作业:
LED0每200ms取反一次;LED1每400ms取反一次;LED2每800ms取反一次
感觉这个方法不是那么聪明的样子;
改写电磁炉程序:
10.体会:主要是将程序都结构化,让看代码的人也能清晰易懂,修改优化程序时更方便。
第十四集 矩阵按键打卡 2024-04-29
1.矩阵按键:每行由一个IO口控制,每列由一个IO口控制,不同于独立按键的一对一IO口,可以大大节省硬件资源;
2.控制原理:通过扫描行和列,判断按键按下位置,类似二维坐标;先将行IO都置低电平,列置高电平,确定哪一列;再将行置高电平,列置低电平,确定哪一行;通过二者确定按键位置;
3.实践程序:电子门锁----矩阵按键输入八位密码,密码正确门开则LED亮,密码错误蜂鸣器鸣叫2s后关闭;每次按键输入蜂鸣器鸣叫20ms。
+课后作业:门锁打开5s后自动关闭,切回锁定状态,设置门内手动开门按钮,使用独立按键,可以一键直接开门;10s内无操作数码管熄灭,有操作即点亮。
#include "COMM/stc.h"
#include "COMM/usb.h"
#include "led_seg.h"
#include "key.h"
#include "beep.h"
#include "time0.h"
char *USER_DEVICEDESC = NULL;
char *USER_PRODUCTDESC = NULL;
char *USER_STCISPCMD = "@STCISP#"; //设置自动复位到ISP区的用户接口命令
bit TIME_10MS = 0; //10ms标志位
void delay_ms(u16 ms);
void sys_init(void);
#define MAIN_Fosc 24000000UL
u16 timi = 0;
void main()//程序开始运行的入口
{
u8 key_num = 0; //矩阵按键键值
u8 key_str = 0; //密码输入数量
u8 i;
u32 count = 0;//计时单位
bit flag = 0;//门锁是否开启标志
sys_init();//USB功能+IO初始化
usb_init();//USB CDC 接口配置
Timer0_Init();
EA = 1;//打开总中断
SEG0 = SEG1 = SEG2 = SEG3 = SEG4 = SEG5 = SEG6 = SEG7 =22;//初始显示--------
LED = 0xff;//数码管和LED显示
while(1) //死循环
{
if(DeviceState != DEVSTATE_CONFIGURED)
continue;
if(bUsbOutReady)
{
usb_OUT_done();//接受应答(固定格式)
}
if(TIME_10MS == 1) //10ms刷新一次
{
TIME_10MS =0;
count++;
BEEP_RUN(); //蜂鸣器运行
KEY_Deal(); //读取按键状态
key_num = MatrixKEY_Read(); //获取键值
if(count == 100) //10s时数码管全都关闭
{
count = 0;
SEG0 = SEG1 = SEG2 = SEG3 = SEG4 = SEG5 = SEG6 = SEG7 =21;
}
if(key_num > 0)//有按键按下
{
count = 0; //有按键按下时,重新计算10s
BEEP_ON(2); //每次按键按下蜂鸣器叫20ms
key_str++; //密码位数++
show_Tab = key_num; //数码管显示输入值
if(key_str > 8) //输入密码已达八位
{
key_str = 0; //初始化密码位数
for(i = 0; i < 8; i++)
{
if(show_Tab == 1) //判断每一位都等于1否
{
flag = 1; //密码一致
}
else
{
flag = 0; //有任何一位不一致,返回门锁未开标志,退出循环
break;
}
}
if(flag == 1 )//门锁已开,灯亮
{
LED0 = 0;
delay_ms(5000); //门开后5s,继续上锁,返回门锁上标志
flag = 0;
}
else if(flag == 0) //门锁关闭,蜂鸣器鸣叫2s,灯灭
{
BEEP_ON(20);
LED0 = 1;
}
SEG0 = SEG1 = SEG2 = SEG3 = SEG4 = SEG5 = SEG6 = SEG7 =22;
}
key_num = 0;
}
}
if(KEY_ReadState(KEY1) == KEY_PRESS) //按下按键1,门直接打开
{
flag = 1;
}
}
}
void Timer0_Isr(void) interrupt 1
{
static time_count = 0;
SEG_refresh(); //刷新数码管
time_count++;
if(time_count >= 10)
{
TIME_10MS = 1;//10ms时间到
time_count = 0;
}
}
void Timer1_Isr(void) interrupt 3
{
}
void sys_init()
{
WTST = 0;//设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
EAXFR = 1; //扩展寄存器(XFR)访问使能
CKCON = 0; //提高访问XRAM速度
P0M1 = 0x00; P0M0 = 0x00; //设置为准双向口
P1M1 = 0x00; P1M0 = 0x00; //设置为准双向口
P2M1 = 0x00; P2M0 = 0x00; //设置为准双向口
P3M1 = 0x00; P3M0 = 0x00; //设置为准双向口
P4M1 = 0x00; P4M0 = 0x00; //设置为准双向口
P5M1 = 0x00; P5M0 = 0x00; //设置为准双向口
P6M1 = 0x00; P6M0 = 0x00; //设置为准双向口
P7M1 = 0x00; P7M0 = 0x00; //设置为准双向口
//====== USB 初始化 ======
P3M0 &= ~0x03;
P3M1 |= 0x03;
IRC48MCR = 0x80;
while (!(IRC48MCR & 0x01));
USBCLK = 0x00;
USBCON = 0x90;
//========================
}
void delay_ms(u16 ms)
{
u16 i;
do
{
i=MAIN_Fosc/6000;
while(--i);
}
while(--ms);
}
第十五集 外部中断打卡 2024-04-29
1.中断系统:CPU处理紧急事件的功能部件。优点:a.总是响应优先级最高的中断请求;b.每个中断源都可以独立控制开关;c.低优先级中断处理中可以被高优先级中断打断;d.部分中断源优先级可以通过软件改变。
2.外部中断:单片机的一个引脚上由于外部因素造成的电平变化(如按键按下),引起的中断就是外部中断.单片机原理图标了INTx的就是外部中断口.
3.外部中断使用:
void INT0_init(void)//外部中断初始化函数
{
IT0 = 1;//1为下降沿有效,0为上升沿下降沿均可触发
EX0 = 1;//中断允许
IE0 = 0;//清楚中断标志
}//外部中断0初始化
//中断功能函数
弄清中断使用的寄存器的每个标志位的高低电平分别是什么功能.
4.课后作业:写外部中断1和中断2-4的函数
void INT1_init(void)//外部中断1
{
IT1 = 1;//1为下降沿有效,0为上升沿下降沿均可触发
EX1 = 1;//中断允许
IE1 = 0;//清楚中断标志
}
void INT1_isr(void) interrupt 2
{
//功能
}
void INT2_init(void)//外部中断2
{
//只能下降沿触发
EX2 = 1;//中断允许
}
void INT2_isr(void) interrupt 10
{
//功能
}
void INT3_init(void)//外部中断3
{
//只能下降沿触发
EX3 = 1;//中断允许
}
void INT3_isr(void) interrupt 11
{
//功能
}
void INT4_init(void)//外部中断4
{
//只能下降沿触发
EX4 = 1;//中断允许
}
void INT4_isr(void) interrupt 16
{
//功能
}思考:什么时候用外部中断?
之前学的定时器中断使用在周期性中断,一定时间才会处理一次事件,而外部中断,只要有外部触发就可以产生中断,优先处理,且执行完以后会自己继续执行主程序。可以用外部中断来写之前按键的功能函数。
第十六集 IO中断打卡 2024-04-30
1.IO中断:普通IO口均可中断,有四种触发方式,低电平、高电平、上升沿、下降沿;stc32暂时别用上升沿和下降沿触发方式;
2.IO中断和外部中断的区别:外部中断只能单次触发,用下降沿触发;IO口使用高电平,低电平触发,可以持续进入中断,例如按键按住不放,持续低电平,按下有效,松开无效;
3.IO中断相关寄存器: PnIM1 PnIM0 --设置触发方式;INTE -- 中断使能,0关1开;INTF -- 中断标志寄存器 0无中断请求,1有中断请求,若使能中断了,标志位需软件清零;PnIPH PnIP -- 设置中断优先级 00-11,00是最低的11是最高的;
void P3EXIT_init(void)
{
P3IM0 = 0X00;
P3IM1 = 0XFF; //M1 M0 = 10 低电平中断
P3INTE = 0X20; //p35中断0010 0000
}//IO中断初始化程序
4.实践:八个按键对应八个LED灯,按键按下灯亮门开,灯灭门锁;一个独立按键模拟应急按钮,按下门全开,且不能用按键解锁,再次按下应急按钮则五秒后解锁:
void main()//程序开始运行的入口
{
u8 key_num = 0; //矩阵按键键值
u8 key_str = 0; //密码输入数量
bit flag = 0;//门锁是否开启标志
sys_init();//USB功能+IO初始化
usb_init();//USB CDC 接口配置
Timer0_Init();
EA = 1;//打开总中断
SEG0 = SEG1 = SEG2 = SEG3 = SEG4 = SEG5 = SEG6 = SEG7 =22;//初始显示--------
LED = 0xff;//数码管和LED显示
while(1) //死循环
{
if(DeviceState != DEVSTATE_CONFIGURED)
continue;
if(bUsbOutReady)
{
usb_OUT_done();//接受应答(固定格式)
}
if(TIME_10MS == 1)
{
TIME_10MS = 0;
if( Tcount ==0 ) //如果没有按下过应急按钮
{
key_num = MatrixKEY_Read(); //当前矩阵按键的键值1-8
BEEP_RUN(); //蜂鸣运行
if( key_num > 0 ) //如果有按键拿下
{
BEEP_ON(2);
flag ^=(1<<(key_num-1)); //获取当前是第几个按钮按下,{1-8}-》
}
LED = flag; //初始状态熄灭所有LED
SEG0 = 21; //熄灭数码管
}
else //按下了应急按钮
{
Tcount--;
SEG0 = (Tcount/100+1); //500/100 499
}
}
}
}//主程序
void P3EXIT_isr(void) interrupt 40
{
u8 intf;
intf = P3INTF ;//中断标志寄存器
if (intf)
{
P3INTF = 0x00;//清零标志位
if(intf & 0x20)
{
LED = 0x00; //打开所有门锁
SEG0 = 5; //数码管持续显示5
Tcount = 500;//5秒倒计时的一个变化
}
}
}
//中断功能函数
5.课后作业:
按下P35四位秒表50s倒计时,最小单位10ms;P54中断,低电平触发,按下SEG1从0-9计数;设置P54优先级高于P35,可以中断秒表计时
void P3EXIT_init(void)
{
P3IM0 = 0X00;
P3IM1 = 0XFF; //M1 M0 = 10 低电平中断
P3INTE = 0X20; //p35中断0010 0000
}
//void P3EXIT_init(void) interrupt 31 写在主函数
void P5EXIT_init(void)
{
P5IM0 = 0X00;
P5IM1 = 0XFF; //M1 M0 = 10 低电平中断
IPH = 0;
IP= 1;//优先级设为1,这样就高于P3
P5INTE = 0X20; //p54中断0001 0000
}//初始化中断函数
void main()//程序开始运行的入口
{
u8 key_num = 0; //矩阵按键键值
u8 key_str = 0; //密码输入数量
sys_init();//USB功能+IO初始化
usb_init();//USB CDC 接口配置
Timer0_Init();
EA = 1;//打开总中断
SEG0 = SEG1 = SEG2 = SEG3 = SEG4 = SEG5 = SEG6 = SEG7 =21;//初始显示--------
LED = 0xff;//数码管和LED显示
while(1) //死循环
{
if(DeviceState != DEVSTATE_CONFIGURED)
continue;
if(bUsbOutReady)
{
usb_OUT_done();//接受应答(固定格式)
}
if(TIME_10MS == 1)
{
TIME_10MS = 0; if(flag != 1)
{
if(Tcount > 0)
{
Tcount--;
SEG4 = 0;//ms
SEG5 = Tcount/10; //10ms
SEG6 = Tcount/100;//1s
SEG7 = Tcount/1000;//10s
}
}
else
{
SEG1 += 1;//数码管1数字加一
}
}
}
}void P3EXIT_isr(void) interrupt 31
{
u8 intf;
intf = P3INTF ;//中断标志寄存器
if (intf)
{
P3INTF = 0x00;//清零标志位
if(intf & 0x20)
{
// LED = 0x00; //打开所有门锁
// SEG0 = 5; //数码管持续显示5
// Tcount = 500;//5秒倒计时的一个变化
Tcount = 5000; //50s
SEG4 = 0;//ms
SEG5 = 0; //10ms
SEG6 = 0;//1s
SEG7 = 5;//10s
}
}
}
void P5EXIT_isr(void) interrupt 42
{
u8 intf;
intf = P5INTF ;//中断标志寄存器
if (intf)
{
P5INTF = 0x00;//清零标志位
if(intf & 0x10)//P54
{
flag = 1;
SEG1 = 0;//数码管1循环显示0-9
}
}
}//主函数和中断功能函数
感觉我这个代码还是有很大问题的,没关系等我实验箱到了我就可以根据一一调试了,我实验箱后天就要到了哈哈哈哈哈哈哈哈哈哈{:lol:}
第十七集 ADC采集打卡 2024-05-06
1.ADC:analog to digital converter 模数转换器
2.12位高速A/D转换器:ADC数值占十二位;
3.ADC相关寄存器:ADC控制寄存器(ADC_CONTR)包括ADC电源开关位,ADC转换启动位,ADC转换结束标志位,ADCPWM使能标志位,ADC模拟通道选择位;ADC配置寄存器(ADCCFG);ADC时序控制寄存器(ADCTIM)
4.ADC查询功能初始化
//========================================================================
// 函数名称: ADC_init()
// 函数功能: ADC查询初始化
// 入口参数: 无
// 函数返回: 无
// 当前版本: VER1.0
// 修改日期: 2024-05-06
// 当前作者:
// 其他备注:
//========================================================================
void ADC_init(void)
{
P1M0 = 0x00;
P1M1 = 0x01; //设置P1.0为ADC口,高阻输入
ADCTIM = 0x3F; //设置ADC内部时序
ADCCFG = 0x2F; //设置ADC系统时钟为fosc/2/16,右对齐
ADC_POWER = 1; //使能ADC模块
}
//========================================================================
// 函数名称: ADC_Read
// 函数功能: 读取指定通道的adc电压
// 入口参数: @no:通道0-15
// 函数返回: 当前的12位adc数值
// 当前版本: VER1.0
// 修改日期: 2024-05-07
// 当前作者:
// 其他备注:
//========================================================================
u16 ADC_Read(u8 no)
{
u16 adcval; //adc数值保存变量
ADC_CONTR &= 0xf0;//清空通道选择
ADC_CONTR |= no; //选择通道
ADC_START = 1; //开启ADC转化
_nop_(); //空操作指令,延时作用
_nop_();
while(!ADC_FLAG); //等待ADC转换结束,结束flag会变成1,1取反变成0,0退出循环
ADC_FLAG = 0; //ADC转换结束标志位置0,以便下次转换
adcval = (ADC_RES << 8) + ADC_RESL;//高四位左移八位再与低八位相加得出adc数值
adc_val = adcval;//
return adcval;
}
//ADC查询相关定义ADC中断功能初始化
#elif ADC_FUNC == ADC_isr
//========================================================================
// 函数名称: ADC_init()
// 函数功能: 中断ADC初始化
// 入口参数: 无
// 函数返回: 无
// 当前版本: VER1.0
// 修改日期: 2024-05-06
// 当前作者:
// 其他备注:
//========================================================================
void ADC_init(void)
{
P1M0 = 0x00;
P1M1 = 0x01; //设置P1.0为ADC口,高阻输入
ADCTIM = 0x3F; //设置ADC内部时序
ADCCFG = 0x2F; //设置ADC系统时钟为fosc/2/16,右对齐
ADC_POWER = 1; //使能ADC模块
EADC = 1; //打开ADC中断
ADC_START = 1; //开启ADC转化
}
//========================================================================
// 函数名称: ADC_isr
// 函数功能: ADC中断函数
// 入口参数: 无
// 函数返回: 无
// 当前版本: VER1.0
// 修改日期: 2024-05-09
// 当前作者:
// 其他备注:
//========================================================================
void ADC_isr() interrupt 5//ADC中断函数
{
ADC_flag = 0;//清空读取标志位
adc_val= (ADC_RESL << 8) + ADC_RESL; //读取ADC数值
ADC_START = 1; //开启ADC转化
}
//ADC中断的相关定义用到if预定义,来选择中断或查询功能。
ADC数值转换成电压值函数:
u16 ADC_CAL_VOLTAGE(u16 num)
{
return num*2.5*1000 / 4096;//ADC值*2.5v*0.001V(转成mv)/4096(最大数值)
}5.作业 :前四位显示数值,后四位显示电压,超过2.2V蜂鸣器常响(我改成LED0常亮)
代码
if(TIME_10MS == 1)
{
TIME_10MS = 0;
ADC_Read(0);//读取ADC0的值,也就是ADC16个按键,0-F分别为256,..,4095,加256一级
SEG0 = (int)adc_val/1000;
SEG1 = (int)adc_val/100%10;
SEG2 = (int)adc_val/10%10;
SEG3 = (int)adc_val%10;
SEG4 = (int)ADC_CAL_VOLTAGE(adc_val)/1000;
SEG5 = (int)ADC_CAL_VOLTAGE(adc_val)/100%10;
SEG6 = (int)ADC_CAL_VOLTAGE(adc_val)/10%10;
SEG7 = (int)ADC_CAL_VOLTAGE(adc_val)%10;
//printf("当前ADC数\xfd值:%d\t%dmv\r\n",(int)adc_val,(int)ADC_CAL_VOLTAGE(adc_val));
if((int)ADC_CAL_VOLTAGE(adc_val) > 2200)
{
LED0 = 0;
}
else
LED0 = 1;
}效果
看了两遍终于结束这一课了,{:victory:}
第十八集 ADC应用打卡 2024-05-07~14 拖拖拉拉
1.用ADC15通道反推电源电压:外部通道输入电压=内部参考信号源电压/内部参考信号源ADC测量值*外部通道输入ADC测量值
u16 ADC_VrefCal(void)
{
u8 i;
int res;
int vcc;
BGV = ((VREFH_ADDR << 8) + VREFL_ADDR);//从CHIPID中读取内部参考电压值
ADC_init(); //ADC初始化,ADC15内部参考信号源1.19V,不用设置端口高阻输入,所以可以直接使用初始化函数
ADC_Read(15);//丢弃前几次数据,刚上电读取数值不稳定
ADC_Read(15);
ADC_Read(15);
ADC_Read(15);
for(i=0;i<8;i++)//读取八次数据,保证数据准确
{
res += ADC_Read(15);//内部参考信号源ADC数值
}
res >>=3;//右移3位,除以8取平均值
vcc = (int)(4096L * BGV/res); //(12位ADC算法)计算VREF管脚电压,即电池电压;4096L是外部通道输入电压ADC测量值
return vcc; //返回电压值
}2.ADC扫描按键(自动循环):
#define ADC_OFFSET 64//ADC误差允许
u8 ADC_keyread(u16 adc)
{
u8 i;
u16 adc_cmp; //当前adc数值和每个按键代表adc数值adc_cmp比较
static u8 key_last = 0; //按键上一次的状态
static u16 key_downtime;//按下时间(长按/短按)
//按钮有无按下,第一个按键按下adc是256
if(adc < (256 - ADC_OFFSET)) //adc数值小于256-误差允许,没有按键按下,值为0
{
key_downtime = 0; //没有按键按下,按下时间为0
key_last = 0; //上一次按键状态清零
}
adc_cmp = 256; //有按键按下时,从第一个按键初始值开始比较,依次加256判断是第几个按键
for(i=1;i<=16;i++)//循环16次,依次与16个按键比较,键值i从1开始
{
if((adc >= (adc_cmp - ADC_OFFSET)) && (adc <= (adc_cmp + ADC_OFFSET)))//检测到adc数值跟每个按键数值比较,误差±64
{
//return i;//满足本次判断,返回该减值,并退出循环,不执行后面
break;
}
else
adc_cmp += 256;//不满足单次判断,adc_cmp+256,继续比较
}
if(i>16)//循环都执行完了,没有键值满足
{
key_downtime = 0;//即没有按键按下,这个变量清零
key_last = 0; //上一次按键状态清零
}
else//有按键按下,判断按下时间
{
if(key_last == i)//上一次按键等于这一次按键状态,说明按键是一直按下的
{
key_downtime ++;//按下时间加1
if(key_downtime == 3)//主函数每10ms扫描一次,当按下时间等于3时,即30ms
{
return i;//还是返回该键值
}
else if(key_downtime == 300)//长按3秒
{
key_downtime = 290; //长按3秒后每10ms触发一次长按功能
return i+0x80;
}
else
return 0;
}
else
{
key_downtime = 0;//刚按下按下时间还未加
key_last = i; //保留按键状态,以便开始按键时间计数
}
}
return 0; //3.实战小练(简易时钟)+课后作业(闹钟设置):
虽然描述很简单,思考的过程很漫长,花了很多的时间去理解老师的代码,又花了很多时间想闹钟设置,一步一步调试代码,再加上自己遇难则退,就拖拖拉拉了一个礼拜了。后面要抓紧了,毕竟还有活要干叻。。{:mad:}
第十九集 NTC温度测温打卡 2024-05-14~05-16
1.NTC原理:Negative Temparature Coefficient,热敏电阻的特性温度越高,电阻的阻值呈指数下降
2.NTC测温程序:单片机的ADC3通道接的NTC电路,通过读取ADC2的adc数值来对照不同等级adc值获取对应温度;
//========================================================================
// 函数名称: temp_cal
// 函数功能: 通过读取的adc数值计算出温度。
// 入口参数: @adc
// 函数返回: 当前的温度值,保留一位小数,-40到150度的温度对应数值-400~1500
// 当前版本: VER1.0
// 修改日期: 2024-05-14
// 当前作者:
// 其他备注: int -32678~32767
//========================================================================
int temp_cal(u16 adc)
{
u8 i;//循环比较变量
float temp;//温度中间变量
if(adc > adc_table)//比-40温度对应adc值还要大,超出范围
return -32678; //超出返回值
else if (adc < adc_table) //比150度对应adc值还要小,超出范围
return 32767; //超出返回值
else//adc值在NTC范围内
{
for(i=0;i<190;i++) //遍历ADC值对应温度表,从-40度开始比较
{
if(adc == adc_table)//正好等于该等级adc数值
{
return (i-40)*10; //序号减去40,加一位小数乘以10
}
else if(adc<adc_table)//小于,不处理
{
}
else //大于adc_table,adc_table的值加上算出小数点值
{
i = i - 1; //
temp = adc_table - adc; //adc-标准值的余量即为小数部分adc值
temp = temp*10/(adc_table - adc_table);//adc转温度值
temp += (i-40)*10; //小数部分加上整数部分
return temp;
}
}
}
}3.实战小练加课后作业:简易温度计,数码管显示温度保留小数点后一位,开关机按键,按一下取反;测量按键,按下读取温度20次后取平均值显示,测量完成LED亮3s;30s不操作自动关机;
温度读取老师教的没有花多少时间;功能处理花了很多时间,感觉自己逻辑很差,每次第一次从头顺下来的逻辑都是错的,基本上要从头到尾全部改一遍;再加上粗心导致的问题,有时候系统不会报错就会花很多时间才能发现问题,可能就是变量的名字取得很相似,搞混了,还有一些时间计数变量,太多了导致弄混了,都要花很多时间找出来。{:4_257:}
第二十-二十一集 串口通信打卡 2024-05-16~05-18
1.通信:串口通信(每次发送一位数据),并行通信(多位数据一起发)
2.串口通信:外设和计算机之间通过数据信号线、地线、控制线等,按位传输数据;传输双方波特率、校验位、停止位、数据位都要一致;
3.STC32串口通信:全双工(可以同时收发),异步通信()
4.串口通信代码实现:
#define BRT (65536-(MAIN_FOSC/115200+2)/4) //加2操作是为了让keil编译器自动实现四舍五入运算
bit busy; //忙碌标志位
char wptr;//发送计数
char rptr;//接收计数
char buffer;//接收缓冲
void usart2_init(void)
{
P_SW2 = 0x80; //串口2功能脚切换寄存器
P_SW2 |= 0x01;//将串口2的引脚切换到P46 P47,且不改变第一步的设置
S2CFG = 0x01; //串口2配置寄存器 要用串口2必须设置该寄存器最后一位W1为1,不用可不特别设置
S2CON = 0x50; //串口2控制寄存器 选择模式2工作方式,固定波特率9位数据方式
T2L = BRT; //
T2H = BRT >> 8;
T2x12 = 1;
T2R = 1;
wptr= 0x00;
rptr= 0x00;
busy= 0;
}
void usart2_isr() interrupt 8
{
if(S2TI)
{
S2TI = 0;
busy = 0;
}
if(S2RI)
{
S2RI = 0;
buffer = S2BUF;
wptr &= 0x0f;
}
}
void usart2_send(char dat)
{
while(busy);
busy = 1;
S2BUF = dat;
}
void usart2_sendstr(char *p)
{
while(*p)//到停止位结束
{
usart2_send(*p++);
}
}主程序
usart2_sendstr("Uart Test!\r\n");5.串口通信应用:copy实例里的串口设置函数。二十集的作业独立思考完成不了。。
/*
项目名称:简易串口控制器。
1.串口发送字符 Ax\r\n,(x表示0-7)板子点亮对应LED
2.串口发送 Bxxxx\r\n,xxxx表示一个四位数,四位数码管显示这个4位数
2.串口发送 Z\r\n,板子给电脑发送“Hello STC”;
3.串口发送字符 Cx\r\n,(x表示0-1)板子打开/关闭蜂鸣
4.串口发送字符 D\r\n,板子通过串口发送当前温度给电脑。
*/temp = temp_cal(ADC_Read(3));
if(Rec_Flag == 1)
{
switch(RX2_Buffer)
{
case 'A':
if((RX2_Buffer >= 48) && (RX2_Buffer <= 55))//0-9的ASCII码
{
LED = ~(1<<(RX2_Buffer-48));
}
break;
case 'B':
SEG0= RX2_Buffer-48;SEG1= RX2_Buffer-48;SEG2= RX2_Buffer-48;SEG3= RX2_Buffer-48;
break;
case 'C':
if(RX2_Buffer == 48)
BEEP = 0;
else
BEEP = 1;
break;
case 'D':
sprintf(str,"当前温度:%d\r\n",temp); PrintString2(str);
break;
case 'Z':
PrintString2("Hello STC!");
break;
default:
break;
}
Rec_Flag = 0;
}6.课后作业:串口控制PC(ADC按键配合)
按下按钮0-7,发送“Ax\r\n”,x:0-7;
按下8,发送“B0000\r\n”;
按下9,发送“Z\r\n”;
按下A,发送“C0\r\n”;按下B,发送“C1\r\n”;
按下C,发送“Dx\r\n”;
keynum = ADC_keyread(ADC_Read(0));
switch(keynum)
{
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
sprintf(str,"A%d\r\n",keynum-1);
PrintString2(str);
break;
case 9:
PrintString2("B0000\r\n");
break;
case 10:
PrintString2("Z\r\n");
break;
case 11:
PrintString2("C0\r\n");
break;
case 12:
PrintString2("C1\r\n");
break;
case 13:
PrintString2("Dx\r\n");
break;
default:
break;
}终于终于学到串口通信,就是因为学习研发卡死在串口通信第一步,才开始重头学单片机哈哈,希望这次不要忘得太快{:4_244:}
第二十二集 CDC串口通信打卡 2024-05-20
CDC串口通信和传统串口通信区别:
可以不断电下载;
串口通信波特率随便选也不会出错;
传统打印字符要通过sprintf转化变量为字符,CDC可以直接使用printf;
使用简单可能是可以直接用例程里的代码;
只需要一根线,usb线又供电又通信。
void main()//程序开始运行的入口
{
int temp; //温度
sys_init();//USB功能+IO初始化
usb_init();//USB CDC 接口配置
EUSB = 1;
Timer0_Init();
ADC_init();
P1M0 = 0x00; P1M1 = 0x08;
EA = 1;//打开总中断
while(DeviceState != DEVSTATE_CONFIGURED);
while(1) //死循环
{
delay_ms(2);
temp = temp_cal(ADC_Read(3));
if(bUsbOutReady)
{
usb_OUT_done();//接受应答(固定格式)
switch(UsbOutBuffer)
{
case 'A':
if((UsbOutBuffer >= 48) && (UsbOutBuffer <= 55))//0-9的ASCII码
{
LED = ~(1<<(UsbOutBuffer-48));
}
break;
case 'B':
SEG0= UsbOutBuffer-48;SEG1= UsbOutBuffer-48;SEG2= UsbOutBuffer-48;SEG3= UsbOutBuffer-48;
break;
case 'C':
if(UsbOutBuffer == 48)
BEEP = 0;
else
BEEP = 1;
break;
case 'D':
printf("当前温度:%d\r\n",temp);
break;
case 'Z':
printf("Hello STC!");
break;
default:
break;
}
Rec_Flag = 0;
//USB_SendData(UsbOutBuffer,OutNumber);
usb_OUT_done();
}
if(TIME_10MS == 1)
{
TIME_10MS = 0;
}
}
} 之前也是一直在使用CDC,不过还是云里雾里的,多应用试试看吧。
第二十三集 看门狗打卡 2024-05-20
1.系统复位方式:上电复位;低压复位;复位脚复位(低电平);看门狗复位。
2.看门狗:是一个计数器,其基本功能是在软件问题和程序跑偏后重启系统,看门狗正常工作时会自动计数,程序进程会定时将其归零。如系统在某个地方卡住或者跑了,定时器就会溢出,系统强制复位。使用有看门狗的芯片时要注意清理看门狗。看门口是独立的计数器,普通的计数器不可代替。
3.看门狗控制寄存器WDT_CONTR:WDT_FLAG溢出标志;EN_WDT使能标志;CLR_WDT定时器清零(喂狗);IDL_WDT,空闲时是否计数;WDT_PS时钟分频系数;
4.实现代码:
void WDT_init(void)//初始化WDT设置
{
WDT_CONTR = 0x24;//EN_WDT = 1;使能看门狗,WDT_PS=100,分频系数32
}
void WDT_feed(void)//看门狗喂狗
{
WDT_CONTR = 0x34;//EN_WDT=1,使能看门狗;CLR_WDT=1,看门狗定时器清零;WDT_PS=100,分频系数32
}void main()//程序开始运行的入口
{注意:等待喂狗的时间要大于代码需要执行的时间,留有余量;
第二十四集 比较器打卡 2024-05-21
1.作用:通过比较两端的电压大小,来及时响应某些动作;
2.原理:比较运放的正端电压和负端电压;若V+>V-,输出1,反之为0;
3.比较器初始化函数:
void CMP_init(void)//初始化CMP设置
{
CMPEXCFG = 0x00;
CMPEXCFG |= 0xc0; //比较器DC迟滞输入选择,0:0mV; 0x40:10mV; 0x80:20mV; 0xc0:30mV(一般选最大)
//CMPEXCFG &= ~0x04; //P3.6为CMP-输入脚
CMPEXCFG |= 0x04; //内部1.19V参考电压为CMP-输入脚
CMPEXCFG &= ~0x03; //P3.7为CMP+输入脚
//CMPEXCFG |= 0x01; //P5.0为CMP+输入脚
//CMPEXCFG |= 0x02; //P5.1为CMP+输入脚
CMPEXCFG |= 0x03; //ADC输入脚为CMP+输入脚
CMPCR2 = 0x00;
INVCMPO = 0; //比较器正向输出
//INVCMPO = 1; //比较器反向输出
DISFLT = 0; //使能0.1us滤波
//DISFLT = 1; //禁止0.1us滤波
//CMPCR2 &= ~0x3f; //比较器结果直接输出
CMPCR2 |= 0x10; //比较器结果经过16个去抖时钟后输出
CMPCR1 = 0x00;
//PIE = 0; //禁止比较器上升沿中断
PIE = 1; //使能比较器上升沿中断
//NIE = 0; //禁止比较器下降沿中断
NIE = 1; //使能比较器下降沿中断
//CMPOE = 0; //禁止比较器输出
CMPOE = 1; //使能比较器输出
CMPO_S = 0; //选择P3.4作为比较器输出脚
//CMPO_S = 1; //选择P4.1作为比较器输出脚
CMPEN = 1; //使能比较器模块
}4.作业:用ADC按键和比较器的方式控制数码管显示当前比较结果,电压大于1.19V显示“On”,否则显示“OFF”;
bit temp = 0;while(1) //死循环
{
delay_ms(2);
if(bUsbOutReady)
{
//USB_SendData(UsbOutBuffer,OutNumber);
usb_OUT_done();
}
if(TIME_10MS == 1)
{
TIME_10MS = 0;
//ADC_Read(0);
if(temp)
{
SEG0 = 0; //O
SEG1 = 22;//
SEG2 = 20;//n
}
else
{
SEG0 = 0; //O
SEG1 = 23;//F
SEG2 = 23;//F
}
}void CMP_isr() interrupt 21
{
CMPIF = 0; //清除中断标志
if(CMPRES) //为1,正端电压大于负端电压
{
}
else //为0,正端电压小于负端电压
{
}
temp = CMPRES; //中断方式读取比较器比较结果
}注意变量定义的位置;
第二十五集 flash模拟EEprom ,跌在坑底躺平了 不知道为什么就是没看懂 , 先接着学之后再回过头来,总要继续的吧,不然就一点事也没得做 ,还是入门级
第二十六集 DS18B20温度传感器打卡2024-05-28~05-30
1.DS18B20简介:数字温度传感器,输出数字信号;单线发送温度信息,三个引脚,正极和负极和DQ脚
2.DS18B20代码过程:
咋了,前面发的贴 看不到了吗 国学芯用 发表于 2024-4-17 13:25
咋了,前面发的贴 看不到了吗
没有啊 只是之前是回复在视频教学帖子下面,工作人员建议我开个新帖记录打卡 这图片排列这么混乱的吗{:dizzy:} 怎么又突然冒出bug{:dizzy:} 原来是因为我没有选择那个图片他就跑到最末尾去了 哟西粘代码比粘截图好多了,又学到了 {:4_250:} 好学天上 发表于 2024-4-17 13:38
没有啊 只是之前是回复在视频教学帖子下面,工作人员建议我开个新帖记录打卡 ...
我也是搞错了 eeprom真的没搞懂,改写CDC也没搞清楚,又要跌在坑里躺下了{:cry:}
页:
[1]