初学51单片机之数字秒表
不同数据类型间的相互转换
在C语言中,不同数据类型之间是可以混合运算的。当表达式中的数据类型不一致时,首先转换为同一类型,然后再进行计算。C语言有两种方式实现类型转换。一是自动类型转换,另外一种是强制类型转换。
转换的主要原则:短字节的数据向长字节数据转换。 比如
unsigned char a; unsigned int b; unsigned int c; c = a * b;
如果: a = 10; b = 200; 那么C的结果就是2000。
那么当 a = 100 , b = 700 ,C会是70000吗? unsigned int 的范围是0 ~65535,但是70000超过了65535,其结果就会溢出 ,最终C的结果是(70000 - 65536)= 4464 这个结果具体和你的编译器有关,不同的编译器结果可能不一样。1111 1111 1111 1111 这个二进制表达的数组是65535也就是16位数据能表达的最大数值,70000的二进制是0001 0001 0001 0111 0000,因为它被限制只能储存16位的数据因此高4位被砍掉了,C显示的就是低16的数据 0001 0001 0111 0000 = 4464
要想C正常获得70000这个结果,需要把C定义成一个unsigned long型
比如 unsigned char a; unsigned int b; unsigned long c; c = a * b;
发现结果仍是4464
C语言不同类型运算的时候数组会转换为同一类型运算,但是每一步运算都会进行识别判断,但不会进行一个总的分析判断。上述 a * b 的时候是按照unsigned int运算的,那么运算的结果也是unsigned int,运算的结果就会是unsigned int类型的4464,最终就是把unsigned int的4464赋值给了一个unsigned long型的变量而已。若想避免产生此类问题,就需要采用强制类型转换变量。
所谓强制类型转换就是在一个变量前面加上一个数据类型名,且这个类型名用小括号括起来如
C = (unsigned long)a * b; 由于强制类型转换运算符优先级高于*,所以先把a转换成unsigned long型的变量,而后与b相乘。根据C语言的规则,b会自动转换成一个unsigned long型的变量,而后运算结果也是unsigned long型了,最后赋值给C。
但是 c = (unsigned long)(a*b) 的结果依然是4464注意区分,圆括号的运算优先级是最高的。
在51单片机里,有一种特殊情况就是bit类型的变量,这个bit类型的强制类型转换是不符合上边讲的这个原则的,比如
bit a = 0; unsigned char b ; a= (bit)b;
使用bit做强制类型转换,不是取b的最低位,而是它会判断b这个变量是0还是非0的值,如果b是0,那么a的结果就是0,如果b是任意非0的其它值,那么a的结果都是1.
秒表功能分析
首先笔者的单片机开发版数码管共6个,秒表的精度要求达到小数点后2位。
首先计算秒表的精度:99i+i = 1s i=10ms,精度是10ms
显然如果精度达到小数点后3位 999i +i = 1s i=1ms 精度是1ms
因此6个数码管前4个显示秒表的整数部分,后两个显示秒表的小数部分。先上代码(该代码来自教材)
# include<reg52.h>sbit ADDR3 = P1^3;
sbit ENLED = P1^4;sbit KEY1 = P2^4;
sbit KEY2 = P2^5;
sbit KEY3 = P2^6;
sbit KEY4 = P2^7;code unsigned char LedChar[] = {0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, //数码管0-F的值0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
};
unsigned char KeySta[4] = { //按键当前态1,1,1,1
};
unsigned char LedBuff[6] = { //数码管显示缓冲区0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
};bit StopwatchRunning = 0; //秒表运行标记
bit StopwatchRefresh = 1; //秒表计数刷新标志
unsigned char DecimalPart = 0; //秒表的小说部分
unsigned int IntegerPart = 0; //秒表的整数部分
unsigned char T0RH = 0; //T0 重载值的高字节
unsigned char T0RL = 0; //T0 重载值的低字节void ConfigTimer0(unsigned int ms);
void StopwatchDisplay();
void KeyDriver();void main()
{EA = 1; //开总中断ENLED = 0; //使能数码管 ADDR3 = 1; P2 = 0xFE; //1111 1110 P2.0置0,选择第4行按键作为独立按键ConfigTimer0(2); // 配置T0定时2mswhile(1){if(StopwatchRefresh) //需要刷新秒表示数时调用显示函数{StopwatchRefresh = 0;StopwatchDisplay();}KeyDriver();}}/*配置并启动,ms-T0定时时间 */
void ConfigTimer0(unsigned int ms)
{unsigned long tmp; // 定义临时变量tmp = 11059200 / 12; //定时器计数频率,即每秒的机器周期数tmp = (tmp * ms) / 1000 ; //计算所需的计数值tmp = 65536 - tmp ; //计算定时器的初值(重载值)tmp = tmp + 18; //补偿响应延时造成的误差T0RH = (unsigned char)(tmp >> 8) ; //定时器重载值拆分成为高低字节T0RL = (unsigned char)tmp;TMOD &= 0xF0; //清零T0的控制位TMOD |= 0x01; //配置T0位模式1TH0 = T0RH;TL0 = T0RL;ET0 = 1; //使能T0中断TR0 = 1; //启动T0定时器}/*秒表计数显示函数 */
void StopwatchDisplay()
{signed char i;unsigned char buf[4]; //数据转换的缓冲区//小数部分转换到低2位//LedBuff[0] = LedChar[DecimalPart % 10];//LedBuff[1] = LedChar[DecimalPart / 10];LedBuff[0] = LedChar[DecimalPart%10];LedBuff[1] = LedChar[DecimalPart/10];//整数部分转换到高4位buf[0] = IntegerPart % 10;buf[1] = (IntegerPart / 10) %10;buf[2] = (IntegerPart / 100) %10;buf[3] = (IntegerPart / 1000) %10;for(i = 3; i >= 1; i--) // 整数部分高位的0转换为空字符{if( buf[i] == 0)LedBuff[i+2] = 0xFF;elsebreak;}for(; i >= 0; i--) //有效数字位转换为显示字符{LedBuff[i+2] = LedChar[buf[i]];}LedBuff[2] &= 0x7F; //点亮小数点}/*秒表启停函数 */
void StopwatchAction()
{if(StopwatchRunning)StopwatchRunning = 0; //已启动则停止elseStopwatchRunning = 1; //未启动则启动
}/*秒表复位函数 */void StopwatchReset()
{StopwatchRunning = 0; //停止秒表DecimalPart = 0; //清零计数值IntegerPart = 0;StopwatchRefresh = 1; //重置刷新标志
}/*按键驱动函数,检测按键动作,调度相应动作函数,需要在主循环中调用*/void KeyDriver()
{unsigned char i;static unsigned char backup[4] = {1,1,1,1}; //循环检测4个按键for(i = 0; i < 4; i++){if(backup[i] != KeySta[i]) //按键动作检测{if( backup[i] != 0){if( i == 1) //ESC键复位秒表StopwatchReset();else if ( i == 2) //回车键启停秒表StopwatchAction();}backup[i] = KeySta[i]; //刷新前一次的备份值}}
}/* 按键扫描函数,需在定时中断中调用 */void KeyScan()
{unsigned char i;static unsigned char keybuf[4] = { //按键扫描缓冲区0xFF,0xFF,0xFF,0xFF };//按键值移入缓冲区keybuf[0] = (keybuf[0] << 1) | KEY1;keybuf[1] = (keybuf[1] << 1) | KEY2;keybuf[2] = (keybuf[2] << 1) | KEY3;keybuf[3] = (keybuf[3] << 1) | KEY4;//消抖后更新按键状态for (i = 0; i < 4; i++){if (keybuf[i] == 0x00){ //连续8次扫描为0,即16ms内都是按下状态,可以认为按键已稳定的按下KeySta[i] = 0;} else if (keybuf[i] == 0xFF) //连续8次扫描为1,即16ms内都是弹起状态,可以认为按键已稳定的弹起KeySta[i] = 1;}
}/* 按键扫描函数,需在定时中断中调用 */void LedScan()
{static unsigned char i = 0;P0 = 0xFF; //显示消隐P1 = (P1 & 0xF8) | i; //F8=1111 1000 位选索引值赋值P1口低3位P0 = LedBuff[i]; //缓冲区中索引位置的数据送到P0口if(i < 5) //索引递增循环,遍历整个缓冲区i++;elsei = 0; }/* 秒表计数函数,每隔10ms调用一次进行秒表计数累加 */
void StopwatchCount()
{if(StopwatchRunning) //当处于运行状态时递增计数值{DecimalPart++; //小数部分+1if (DecimalPart >= 100) //小数部分计到100时进位到整数部分{DecimalPart = 0;IntegerPart++; //整数部分+1if(IntegerPart >= 10000) //整数部分计到10000时归零{IntegerPart = 0;}}StopwatchRefresh = 1; //设置秒表计数刷新标志}}/* T0中断服务函数,完成数码管、按键扫描与秒表计数 */void InterruptTimer0() interrupt 1
{static unsigned char tmr10ms = 0;TH0 = T0RH; //重新加载重载值TL0 = T0RL;LedScan(); //数码管扫描显示KeyScan(); //按键扫描tmr10ms++;if(tmr10ms >= 5){tmr10ms = 0;StopwatchCount(); //调用秒表计数函数}}
精度10ms的秒表计时_哔哩哔哩_bilibili
看一下结果视频:可以看到秒表确实正常工作了,有清零,暂停两种基本功能。该秒表的最大量程是9999.99秒。
然后分析下该程序的一些可能有疑问的部分。
1:
void LedScan()
{static unsigned char i = 0;P0 = 0xFF; //显示消隐P1 = (P1 & 0xF8) | i; //F8=1111 1000 位选索引值赋值P1口低3位P0 = LedBuff[i]; //缓冲区中索引位置的数据送到P0口if(i < 5) //索引递增循环,遍历整个缓冲区i++;elsei = 0; }
下图是上图的等效语句,上图显然精炼点。
P0 = 0xFF; //刷新数码管前P0口8位全部置1使LED都不工作。switch(i){case 0: ADDR2 = 0; ADDR1 = 0; ADDR0 = 0; i++; P0 = LedBuff[0]; break; //数码管1刷新case 1: ADDR2 = 0; ADDR1 = 0; ADDR0 = 1; i++; P0 = LedBuff[1]; break; //数码管2刷新case 2: ADDR2 = 0; ADDR1 = 1; ADDR0 = 0; i++; P0 = LedBuff[2]; break; //数码管3刷新case 3: ADDR2 = 0; ADDR1 = 1; ADDR0 = 1; i++; P0 = LedBuff[3]; break; //数码管4刷新case 4: ADDR2 = 1; ADDR1 = 0; ADDR0 = 0; i++; P0 = LedBuff[4]; break; //数码管5刷新case 5: ADDR2 = 1; ADDR1 = 0; ADDR0 = 1; i = 0; P0 = LedBuff[5]; break; //数码管6刷新default: break;}
2:

这个小数点的值算法可以参考我之前的博文初学51单片机定时器数码管及C语言实践_51单片机定时器控制数码管程序-CSDN博客
这个点是数码管的dp位,它是最高位,因为是低电平使能,因此要加上这个点只要
0111 1111 = 0x7F 与原先的数相与,就可以把原先数值的最高位置0。而本案的数码管真值,表示的都是不带点的数,因此最高位都是1。该与运算就把点加上去了。
3:

该程序里的18是怎么来的?tmp是中断初值设置,因此这个18是18个机器周期的意思,加18会使tmp值变大,相应中断时间变短了,说明该程序在某处浪费了时间。
首先这个误差可以通过软件debug出来,也可以通过累计计时比如计时30分钟和正确的秒表计时比较后确定总的误差是多少,然后通过计算转换到每个中断需要补偿的时间。
那边必然产生一个问题,误差到底是由哪些语句产生的?
首先我们得知道数码管显示语句在哪里,通读程序可知他是由中断函数里的LedScan()函数提供,我们之前说秒表的精度是10ms,但是实际上它每隔2ms就会刷新一下数码管。假设我们有子弹时间的话,一开始同一数码管的视觉上时间显示间隔可以这么理解。当然这个速度很快
事实上当开始执行的时候,
数码管0-5间隔2ms依次显示下去的,一开始的显示表示。

数码管0显示0只持续了2ms即1个中断间隔,但是肉眼看上去没有灭过,是因为人眼的余晖效应,每个数码管的刷新频率是1/0.012=83HZ,肉眼是不会感到明显的闪烁感的。通过两图结合程序对比可知,事实上当过去10ms的时候,即第5次进入中断,小数部分低位数码管显示数值已经刷新了,即0变成1了,但是第5次中断数码管显示的是第5个数码管的数值
P0 = LedBuff[4];,此时第5个的数码管的数值是0,但高位0不显示。而需要再次显示
P0 =LedBuff[0] 还需要两个中断,也就是说秒表在计时的时候除却其他人为操作造成的误差,由于程序结构照成的误差就有1个精度单位,本案为10ms。注:这是显示误差不是计时误差。这个误差是必然发生的,要想降低这个误差就需要更改中断间隔,提高秒表的精度。
所以除却该误差,如果要想数码管显示准确无误,它必须满足这几个条件:
1:中断的时间间隔是非常精确的2ms,而这个精度由单片机和晶振提供的,就是该单片机的机器周期,本案是12/11059200=1.085us也就是微秒级的。这个对于我们的普通秒表来说应该是够了
2:进入中断后马上执行显示语句,并且显示语句本身以及显示语句之前都没有时间损耗(显然不可能)
3:电路响应足够快,快到对于秒表精度来说忽略不计。
第二个条件
查看中断函数与数码管显示函数
void InterruptTimer0() interrupt 1
{static unsigned char tmr10ms = 0;TH0 = T0RH; //重新加载重载值TL0 = T0RL;LedScan(); //数码管扫描显示KeyScan(); //按键扫描tmr10ms++;if(tmr10ms >= 5){tmr10ms = 0;StopwatchCount(); //调用秒表计数函数}}
void LedScan()
{static unsigned char i = 0;P0 = 0xFF; //显示消隐P1 = (P1 & 0xF8) | i; //F8=1111 1000 位选索引值赋值P1口低3位P0 = LedBuff[i]; //缓冲区中索引位置的数据送到P0口if(i < 5) //索引递增循环,遍历整个缓冲区i++;elsei = 0; }
显然数码管显示语句P0 = LedBuff[i];前有好几个语句这些都有可能照成误差,开始分析一下。
一开始进入中断,TF0 = 0(中断函数自动清0),那代表着第二个中断时间开始计时了,这时定时器的初值是0x00;
TH0 = T0RH;
TL0 = T0RL; 执行完这句定时器0初值重载,那么前面的三句都是时间上的误差。执行了该句以后,马上执行LedScan(); 该函数
static unsigned char i = 0;
P0 = 0xFF; //显示消隐
P1 = (P1 & 0xF8) | i; //F8=1111 1000 位选索引值赋值P1口低3位
P0 = LedBuff[i]; 执行完该句数码管才显示结束因此前面的加上它本身共4句都是误差来源。
事实上对于该程序的时间误差,统筹的看会比较好一点。对于时间流逝流程如下图,(这个过程不太好描述,笔者无法准确表述,各位自己感悟一下,秒表的显示逻辑是这样的,我们肉眼看到也是如此)

debug一下,把程序停在该句 P0 = LedBuff[i]
第一次运行是0.00421875
第二次运行是0.00623806
第三次运行是0.00825738
......
第11次运行是0.02441406
计算第11次的值减去第1次的值,除以10.求得该时间间隔是0.002019531
我们希望的时间是2ms即0.002s,所以多余的时间就是误差,这个误差我们需要消除
即0.000019531*11059200/12=17.9997个机器周期取18,这就是程序里补偿时间18的来由。
debug过程debug误差过程_哔哩哔哩_bilibili
事实上对于中断函数里的程序,笔者看了一会觉得逻辑是这样的才对,
一开始写大概率这种逻辑的,但是从debug的过程中可以看到,第5次中断的时候会进入if函数,那么时间间隔就会发生变化了,当然这依然可以时间补偿,就是求时间的时候需要注意一下,而且这会导致每5次中断,其中一个数码管的刷新时间变得长一点,虽然不影响显示结果,因此该函数放在LedSCan()后面,而且因为几乎必然发生的显示延迟,对于进位结果下个中断使能,好像也不是很难接受的结果。
按照刷新频率来说,第一个0ms-100ms为例,小数部分最低位的数码管我们希望的值是0-9共显示10次数字,但是数码管的刷新完一次要12ms, 则100/12= 8 余4,也就是说在头一个100ms里它只完成扫描了8次,第9次只扫描了前4个数码管。因此必然有个数没显示,经过笔者计算第一个100ms里无法显示的数是6,它是从5直接跳到7,当然下一个100ms未显示的数未必是6了。
最低位数码管从5跳到7.这个7显示正确吗,精度有问题吗?事实上7的精度是最高的,5的误差是最大的,这边可以理解为显示延迟已经不支持它显示6了,数码管是第6次刷新,但是时间已经累计到7了并且该处ledbuff[0]值已进被程序赋值为7了,因此跳过了6直接显示7。可以预见的是如果一直计时在未来的某个100ms里可能会出现跳过两次数字显示,毕竟发现一次跳过显示只需要72ms,但不会出现三次跳过的情况。
3:只要电路的响应频率远大于2ms,应该都没问题。
笔者自己梳理的程序逻辑导图:

总结:今天又进步了一小步。
相关文章:
初学51单片机之数字秒表
不同数据类型间的相互转换 在C语言中,不同数据类型之间是可以混合运算的。当表达式中的数据类型不一致时,首先转换为同一类型,然后再进行计算。C语言有两种方式实现类型转换。一是自动类型转换,另外一种是强制类型转换。 转换的主…...
SpringBoot整合justauth实现多种方式的第三方登陆
目录 0.准备工作 1.引入依赖 2.yml文件 3. Controller代码 4.效果 参考 0.准备工作 你需要获取三方登陆的client-id和client-secret 以github为例 申请地址:Sign in to GitHub GitHub 1.引入依赖 <?xml version"1.0" encoding"UTF-8&quo…...
【Java算法】滑动窗口
🔥个人主页: 中草药 🔥专栏:【算法工作坊】算法实战揭秘 👖一. 长度最小的子数组 题目链接:209.长度最小的子数组 算法原理 滑动窗口 滑动窗口算法常用于处理数组/字符串等序列问题,通过定义一…...
C# —— 属性和字段
属性和字段的区别 1.都是定义在一个类中,属于类成员变量 2.字段一般都是私有的private,属性一般是公开的Public 3.字段以小驼峰命名方式 age,属性一般是以大驼峰命名 Age 4.字段可以存储数据,属性不能存储数据,通过属性…...
【计算机视觉】人脸算法之图像处理基础知识(四)
图像的几何变换 图像的几何变换是指在不改变图像内容的前提下对图像的像素进行空间几何变换。主要包括图像的平移变换、镜像变换、缩放和旋转等。 1.插值算法 插值通常用来放缩图像大小,在图像处理中常见的插值算法有最邻近插值法、双线性插值法、二次立方、三次…...
探索 Spring Boot 集成缓存功能的最佳实践
在线工具站 推荐一个程序员在线工具站:程序员常用工具(http://cxytools.com),有时间戳、JSON格式化、文本对比、HASH生成、UUID生成等常用工具,效率加倍嘎嘎好用。 程序员资料站 推荐一个程序员编程资料站:…...
微信小程序使用方法
一.在网页注册小程序账号(在未注册的情况下) 1.如果你还没有微信公众平台的账号,请先进入微信公众平台首页,点击 “立即注册” 按钮进行注册。我们选择 “小程序” 即可。 接着填写账号信息,需要注意的是,…...
前后端分离的后台管理系统源码,快速开发OA、CMS网站后台管理、毕业设计项目
那有没有一款软件解-决这种现状呢?答案是肯定的。引入我们的软件——eladmin。 介绍 ELADMIN,一个简单且易上手的 Spring boot 后台管理框架,已发布 Mybatis-Plus 版本,为开发者提供了一个全-面、高-效的解-决方案。 特点 高-效率:前后端完全分离,项目简单可配,内置代码…...
[深度学习]--分类问题的排查错误的流程
原因复现: 原生的.pt 好使, 转化后的 CoreML不好使, 分类有问题。 yolov8 格式的支持情况 Format Argument Suffix CPU GPU 0 PyTorch - .pt True True 1 Tor…...
微信小程序简易录音机
首先先创建一个项目(想必大家都会啦那就直接开干) 首先上html结构 <view class"wx-container"><view id"title">录音机</view><view id"time">{{hours}}:{{minute}}:{{second}}</view>&l…...
Ollama深度探索:AI大模型本地部署的全面教程
目录 引言一、Ollama概述1、定义与定位2、核心功能3、技术优势4、应用场景 二、安装与配置1、系统要求2、安装方法3、配置指南4、启动Ollama服务 四、快速开始1、启动Ollama2、部署运行模型3、REEST API 五、自定义模型1、定制化的必要性2、使用Modelfile定制模型3、参数调整4、…...
文心一言 VS 讯飞星火 VS chatgpt (285)-- 算法导论21.2 4题
四、请给出图 21-3 所示操作序列的一个运行时间的渐近紧确界,假定使用链表表示和加权合并启发式策略。如果要写代码,请用go语言。 文心一言: 图 21-3 没有直接给出,但通常这种操作序列可能与某种数据结构(如并查集或…...
基于springboot实现影院订票系统项目【项目源码+论文说明】计算机毕业设计
基于springboot实现影院订票系统演示 摘要 现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本影院订票系统就是在这样的大环境下诞生,其可以帮助管理者在…...
Linux 常用命令合集
进入root模式 su -> 回车 -> 输入root用户密码关闭系统 方法1: shutdown -h now 方法2: init 0 方法3: telinit 0按预定时间关闭系统 shutdown -h hours:minutes &取消按预定时间关闭系统 shutdown -c重启 shutdown -r now重启…...
Vue3插件安装
一、volar插件安装 volar:Vue文件的语法提示和高亮提醒。volar已经更名为Vue - Official,其安装步骤如下。 (1)打开vscode,点击扩展面板,在搜索窗口中输入volar,选择Vue - Official进行安装。 (2࿰…...
Redis精要
一、什么是缓存击穿、缓存穿透、缓存雪崩? 缓存穿透 【针对大量非法访问的请求,缓存中没有,直接访问DB】 缓存穿透指的查询缓存和数据库中都不存在的数据,这样每次请求直接打到数据库,就好像缓存不存在 一样。 对于系…...
国产24位I2S输入+192kHz立体声DAC音频数模转换器CJC4344
CJC4344是一款立体声数模转换芯片,内含插值滤波器、multi bit数模转换器、输出模拟滤波器。CJC4344系列支持大部分的音频数据格式。CJC4344基于一个带线性模拟低通滤波器的四阶multi-bitΔ-Σ调制器,而且本芯片可以通过检测信号频率和主时钟频率…...
UniApp 开发微信小程序教程(一):准备工作和环境搭建,项目结构和配置
文章目录 一、准备工作和环境搭建1. 安装 HBuilderX步骤: 2. 注册微信开发者账号步骤: 3. 创建 UniApp 项目步骤: 二、项目结构和配置1. UniApp 项目结构2. 配置微信小程序修改 manifest.json修改 pages.json 3. 添加首页文件index.vue 示例&…...
[WTL/Win32]_[中级]_[MVP架构在实际项目中的应用]
场景 在开发Windows和macOS的界面软件时,Windows用的是WTL/Win32技术,而macOS用的是Cocoa技术。而两种技术的本地语言一个主打是C,另一个却是Object-c。界面软件的源码随着项目功能增多而增多,这就会给同步Windows和macOS的功能造成很大负担…...
《Windows API每日一练》5.2 按键消息
上一节中我们得知,Windows系统的按键消息有很多类型,大部分按键消息都是由Windows系统的默认窗口过程处理的,我们自己只需要处理少数几个按键消息。这一节我们将详细讲述Windows系统的所有按键消息及其处理方式。 本节必须掌握的知识点&…...
css实现圆环展示百分比,根据值动态展示所占比例
代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...
SCAU期末笔记 - 数据分析与数据挖掘题库解析
这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...
剑指offer20_链表中环的入口节点
链表中环的入口节点 给定一个链表,若其中包含环,则输出环的入口节点。 若其中不包含环,则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...
【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...
如何将联系人从 iPhone 转移到 Android
从 iPhone 换到 Android 手机时,你可能需要保留重要的数据,例如通讯录。好在,将通讯录从 iPhone 转移到 Android 手机非常简单,你可以从本文中学习 6 种可靠的方法,确保随时保持连接,不错过任何信息。 第 1…...
【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)
升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点,但无自动故障转移能力,Master宕机后需人工切换,期间消息可能无法读取。Slave仅存储数据,无法主动升级为Master响应请求ÿ…...
Typeerror: cannot read properties of undefined (reading ‘XXX‘)
最近需要在离线机器上运行软件,所以得把软件用docker打包起来,大部分功能都没问题,出了一个奇怪的事情。同样的代码,在本机上用vscode可以运行起来,但是打包之后在docker里出现了问题。使用的是dialog组件,…...
Java数值运算常见陷阱与规避方法
整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
Vite中定义@软链接
在webpack中可以直接通过符号表示src路径,但是vite中默认不可以。 如何实现: vite中提供了resolve.alias:通过别名在指向一个具体的路径 在vite.config.js中 import { join } from pathexport default defineConfig({plugins: [vue()],//…...
