本帖最后由 好学天上 于 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=9 a--是先进行取值,后进行自减;--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内无操作数码管熄灭,有操作即点亮。
复制代码
第十五集 外部中断打卡 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
- }
- }
- }
复制代码
//主函数和中断功能函数
感觉我这个代码还是有很大问题的,没关系等我实验箱到了我就可以根据一一调试了,我实验箱后天就要到了哈哈哈哈哈哈哈哈哈哈
第十七集 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;
- }
复制代码
效果
看了两遍终于结束这一课了,
第十八集 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.实战小练(简易时钟)+课后作业(闹钟设置):
Demo.uvproj
(13.53 KB, 下载次数: 149)
虽然描述很简单,思考的过程很漫长,花了很多的时间去理解老师的代码,又花了很多时间想闹钟设置,一步一步调试代码,再加上自己遇难则退,就拖拖拉拉了一个礼拜了。后面要抓紧了,毕竟还有活要干叻。。
第十九集 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[0]) //比-40温度对应adc值还要大,超出范围
- return -32678; //超出返回值
- else if (adc < adc_table[190]) //比150度对应adc值还要小,超出范围
- return 32767; //超出返回值
- else //adc值在NTC范围内
- {
- for(i=0;i<190;i++) //遍历ADC值对应温度表,从-40度开始比较
- {
- if(adc == adc_table[i]) //正好等于该等级adc数值
- {
- return (i-40)*10; //序号减去40,加一位小数乘以10
- }
- else if(adc<adc_table[i]) //小于,不处理
- {
-
- }
- else //大于adc_table[i],adc_table[i-1]的值加上算出小数点值
- {
- i = i - 1; //
- temp = adc_table[i] - adc; //adc-标准值的余量即为小数部分adc值
- temp = temp*10/(adc_table[i] - adc_table[i+1]); //adc转温度值
- temp += (i-40)*10; //小数部分加上整数部分
- return temp;
- }
- }
- }
- }
复制代码
3.实战小练加课后作业:简易温度计,数码管显示温度保留小数点后一位,开关机按键,按一下取反;测量按键,按下读取温度20次后取平均值显示,测量完成LED亮3s;30s不操作自动关机;
Demo.uvproj
(13.71 KB, 下载次数: 137)
温度读取老师教的没有花多少时间;功能处理花了很多时间,感觉自己逻辑很差,每次第一次从头顺下来的逻辑都是错的,基本上要从头到尾全部改一遍;再加上粗心导致的问题,有时候系统不会报错就会花很多时间才能发现问题,可能就是变量的名字取得很相似,搞混了,还有一些时间计数变量,太多了导致弄混了,都要花很多时间找出来。
第二十-二十一集 串口通信打卡 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[16]; //接收缓冲
-
- 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[wptr++] = 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[0])
- {
- case 'A':
- if((RX2_Buffer[1] >= 48) && (RX2_Buffer[1] <= 55)) //0-9的ASCII码
- {
- LED = ~(1<<(RX2_Buffer[1]-48));
- }
- break;
- case 'B':
- SEG0= RX2_Buffer[1]-48;SEG1= RX2_Buffer[2]-48;SEG2= RX2_Buffer[3]-48;SEG3= RX2_Buffer[4]-48;
- break;
- case 'C':
- if(RX2_Buffer[1] == 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;
- }
复制代码
终于终于学到串口通信,就是因为学习研发卡死在串口通信第一步,才开始重头学单片机哈哈,希望这次不要忘得太快
第二十二集 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[0])
- {
- case 'A':
- if((UsbOutBuffer[1] >= 48) && (UsbOutBuffer[1] <= 55)) //0-9的ASCII码
- {
- LED = ~(1<<(UsbOutBuffer[1]-48));
- }
- break;
- case 'B':
- SEG0= UsbOutBuffer[1]-48;SEG1= UsbOutBuffer[2]-48;SEG2= UsbOutBuffer[3]-48;SEG3= UsbOutBuffer[4]-48;
- break;
- case 'C':
- if(UsbOutBuffer[1] == 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[2:0]时钟分频系数;
4.实现代码:
- void WDT_init(void) //初始化WDT设置
- {
- WDT_CONTR = 0x24; //EN_WDT = 1;使能看门狗,WDT_PS[2:0]=100,分频系数32
- }
-
- void WDT_feed(void) //看门狗喂狗
- {
- WDT_CONTR = 0x34; //EN_WDT=1,使能看门狗;CLR_WDT=1,看门狗定时器清零;WDT_PS[2:0]=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代码过程:
|