当前位置: 首页 > news >正文

初学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语言中&#xff0c;不同数据类型之间是可以混合运算的。当表达式中的数据类型不一致时&#xff0c;首先转换为同一类型&#xff0c;然后再进行计算。C语言有两种方式实现类型转换。一是自动类型转换&#xff0c;另外一种是强制类型转换。 转换的主…...

SpringBoot整合justauth实现多种方式的第三方登陆

目录 0.准备工作 1.引入依赖 2.yml文件 3. Controller代码 4.效果 参考 0.准备工作 你需要获取三方登陆的client-id和client-secret 以github为例 申请地址&#xff1a;Sign in to GitHub GitHub 1.引入依赖 <?xml version"1.0" encoding"UTF-8&quo…...

【Java算法】滑动窗口

&#x1f525;个人主页&#xff1a; 中草药 &#x1f525;专栏&#xff1a;【算法工作坊】算法实战揭秘 &#x1f456;一. 长度最小的子数组 题目链接&#xff1a;209.长度最小的子数组 算法原理 滑动窗口 滑动窗口算法常用于处理数组/字符串等序列问题&#xff0c;通过定义一…...

C# —— 属性和字段

属性和字段的区别 1.都是定义在一个类中&#xff0c;属于类成员变量 2.字段一般都是私有的private&#xff0c;属性一般是公开的Public 3.字段以小驼峰命名方式 age&#xff0c;属性一般是以大驼峰命名 Age 4.字段可以存储数据&#xff0c;属性不能存储数据&#xff0c;通过属性…...

【计算机视觉】人脸算法之图像处理基础知识(四)

图像的几何变换 图像的几何变换是指在不改变图像内容的前提下对图像的像素进行空间几何变换。主要包括图像的平移变换、镜像变换、缩放和旋转等。 1.插值算法 插值通常用来放缩图像大小&#xff0c;在图像处理中常见的插值算法有最邻近插值法、双线性插值法、二次立方、三次…...

探索 Spring Boot 集成缓存功能的最佳实践

在线工具站 推荐一个程序员在线工具站&#xff1a;程序员常用工具&#xff08;http://cxytools.com&#xff09;&#xff0c;有时间戳、JSON格式化、文本对比、HASH生成、UUID生成等常用工具&#xff0c;效率加倍嘎嘎好用。 程序员资料站 推荐一个程序员编程资料站&#xff1a;…...

微信小程序使用方法

一.在网页注册小程序账号&#xff08;在未注册的情况下&#xff09; 1.如果你还没有微信公众平台的账号&#xff0c;请先进入微信公众平台首页&#xff0c;点击 “立即注册” 按钮进行注册。我们选择 “小程序” 即可。 接着填写账号信息&#xff0c;需要注意的是&#xff0c;…...

前后端分离的后台管理系统源码,快速开发OA、CMS网站后台管理、毕业设计项目

那有没有一款软件解-决这种现状呢?答案是肯定的。引入我们的软件——eladmin。 介绍 ELADMIN,一个简单且易上手的 Spring boot 后台管理框架,已发布 Mybatis-Plus 版本,为开发者提供了一个全-面、高-效的解-决方案。 特点 高-效率:前后端完全分离,项目简单可配,内置代码…...

[深度学习]--分类问题的排查错误的流程

原因复现&#xff1a; 原生的.pt 好使&#xff0c; 转化后的 CoreML不好使&#xff0c; 分类有问题。 yolov8 格式的支持情况 Format Argument Suffix CPU GPU 0 PyTorch - .pt True True 1 Tor…...

微信小程序简易录音机

首先先创建一个项目&#xff08;想必大家都会啦那就直接开干&#xff09; 首先上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 所示操作序列的一个运行时间的渐近紧确界&#xff0c;假定使用链表表示和加权合并启发式策略。如果要写代码&#xff0c;请用go语言。 文心一言&#xff1a; 图 21-3 没有直接给出&#xff0c;但通常这种操作序列可能与某种数据结构&#xff08;如并查集或…...

基于springboot实现影院订票系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现影院订票系统演示 摘要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本影院订票系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在…...

Linux 常用命令合集

进入root模式 su -> 回车 -> 输入root用户密码关闭系统 方法1&#xff1a; shutdown -h now 方法2&#xff1a; init 0 方法3&#xff1a; telinit 0按预定时间关闭系统 shutdown -h hours:minutes &取消按预定时间关闭系统 shutdown -c重启 shutdown -r now重启…...

Vue3插件安装

一、volar插件安装 volar&#xff1a;Vue文件的语法提示和高亮提醒。volar已经更名为Vue - Official&#xff0c;其安装步骤如下。 (1)打开vscode&#xff0c;点击扩展面板&#xff0c;在搜索窗口中输入volar&#xff0c;选择Vue - Official进行安装。 &#xff08;2&#xff0…...

Redis精要

一、什么是缓存击穿、缓存穿透、缓存雪崩&#xff1f; 缓存穿透 【针对大量非法访问的请求&#xff0c;缓存中没有&#xff0c;直接访问DB】 缓存穿透指的查询缓存和数据库中都不存在的数据&#xff0c;这样每次请求直接打到数据库&#xff0c;就好像缓存不存在 一样。 对于系…...

国产24位I2S输入+192kHz立体声DAC音频数模转换器CJC4344

CJC4344是一款立体声数模转换芯片&#xff0c;内含插值滤波器、multi bit数模转换器、输出模拟滤波器。CJC4344系列支持大部分的音频数据格式。CJC4344基于一个带线性模拟低通滤波器的四阶multi-bitΔ-Σ调制器&#xff0c;而且本芯片可以通过检测信号频率和主时钟频率&#xf…...

UniApp 开发微信小程序教程(一):准备工作和环境搭建,项目结构和配置

文章目录 一、准备工作和环境搭建1. 安装 HBuilderX步骤&#xff1a; 2. 注册微信开发者账号步骤&#xff1a; 3. 创建 UniApp 项目步骤&#xff1a; 二、项目结构和配置1. UniApp 项目结构2. 配置微信小程序修改 manifest.json修改 pages.json 3. 添加首页文件index.vue 示例&…...

[WTL/Win32]_[中级]_[MVP架构在实际项目中的应用]

场景 在开发Windows和macOS的界面软件时&#xff0c;Windows用的是WTL/Win32技术&#xff0c;而macOS用的是Cocoa技术。而两种技术的本地语言一个主打是C,另一个却是Object-c。界面软件的源码随着项目功能增多而增多&#xff0c;这就会给同步Windows和macOS的功能造成很大负担…...

《Windows API每日一练》5.2 按键消息

上一节中我们得知&#xff0c;Windows系统的按键消息有很多类型&#xff0c;大部分按键消息都是由Windows系统的默认窗口过程处理的&#xff0c;我们自己只需要处理少数几个按键消息。这一节我们将详细讲述Windows系统的所有按键消息及其处理方式。 本节必须掌握的知识点&…...

adb 截屏和录屏命令

adb 录屏命令 screenrecord 简介 screenrecord 是一个 shell 命令 支持 Android 4.4(API level 19)以上 支持视频格式: mp4 一些限制 某些设备可能无法直接录制,原因是分辨率太高,如果遇到此类问题&#xff0c;请试着指定较低的分辨率 不支持录制过程中屏幕旋转,如果录制…...

springboot相关的一些知识

SpringBoot可以同时处理多少请求 SpringBoot默认的内嵌容器是Tomcat&#xff0c;所以SpringBoot可以同时处理多少请求取决于Tomcat。 SpringBoot中处理请求数量相关的参数有四个&#xff1a; server.tomcat.thread.min-spare&#xff1a;最少的工作线程数&#xff0c;默认大小…...

DP:完全背包+多重背包问题

完全背包和01背包的区别就是&#xff1a;可以多次选 一、完全背包&#xff08;模版&#xff09; 【模板】完全背包_牛客题霸_牛客网 #include <iostream> #include<string.h> using namespace std; const int N1001; int n,V,w[N],v[N],dp[N][N]; //dp[i][j]表示…...

购物返利系统的安全性:防范欺诈与数据保护

购物返利系统的安全性&#xff1a;防范欺诈与数据保护 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 购物返利系统作为一种电子商务模式&#xff0c;通过向消…...

从WebM到MP3:利用Python和wxPython提取音乐的魔法

前言 有没有遇到过这样的问题&#xff1a;你有一个包含多首歌曲的WebM视频文件&#xff0c;但你只想提取其中的每一首歌曲&#xff0c;并将它们保存为单独的MP3文件&#xff1f;这听起来可能有些复杂&#xff0c;但借助Python和几个强大的库&#xff0c;这个任务变得异常简单。…...

图片转pdf,图片转pdf在线转换,在线图片转pdf

图片转PDF&#xff0c;听起来似乎是一个简单的操作&#xff0c;但实际上&#xff0c;它涉及到许多细节和技巧。有时候我们需要将图片转换为PDF格式&#xff0c;以便于分享、打印或保存。那么&#xff0c;如何将图片转换成PDF呢&#xff1f;接下来&#xff0c;我将为您详细介绍几…...

SpringBoot3使用Swagger3

SpringBoot3使用Swagger3 项目中的后端接口进行简单的前端展示一、依赖引入二、快速启动1.在application.yml中配置2.或者properties文件,则配置3.启动项目访问swagger 三、使用注解标注接口Swagger配置文件Swagger 注解迁移举例五种常用ApiApiOperationApiImplicitParamApiMod…...

【51单片机基础教程】点亮led

文章目录 前言51单片机点亮LED的原理硬件部分软件部分51单片机的寄存器编程步骤proteus仿真点亮一个led 点亮多个ledproteus仿真代码 流水灯 总结 前言 单片机&#xff08;Microcontroller Unit, MCU&#xff09;是一种集成电路&#xff0c;广泛应用于各种电子产品中。作为嵌入…...

Docker之overlay2的迁移

原因 docker默认将文件及其容器放置在了系统盘的挂载区内&#xff0c;如果长期使用会发现系统挂载区被overlay2挤爆了,因此在一开始我们将其迁移在大容量外挂磁盘上,就可以避免系统盘被挤爆,放心使用. 具体操作 # 停止容器 systemctl stop docker# 修改容器配置&#xff0c…...

CentOS中的rename命令

目录 CentOS中的rename命令基本语法使用示例注意事项安装prename CentOS中的rename命令 在CentOS系统中&#xff0c;rename命令通常是指util-linux包中提供的版本&#xff0c;它用于批量重命名文件&#xff0c;但与Perl版本的rename命令相比&#xff0c;功能较为简单&#xff…...

海南网站建设粤icp备/淘宝的前100个关键词排名

详情请参考&#xff1a;https://help.aliyun.com/document_detail/32069.html?spma2c4g.11186623.6.763.ZgC59a或者https://help.aliyun.com/document_detail/64041.html?spma2c4g.11186623.6.762.EmuWIt这里写的是最简单的写法&#xff0c;只是实现其简单的功能1.引入js文件…...

1做网站推广/百度网站推广一年多少钱

云联系人概览&#xff1f;联系人云同步功能&#xff0c;可以将您手机的“联系人”数据安全存储至云端&#xff0c;并保持实时更新&#xff0c;随时随地实现多设备共享。在小米品牌手机、平板、电脑、网页版i.mi.com上&#xff0c;以及安装了小米云服务电脑客户端中&#xff0c;…...

外贸做哪个网站平台/故事型软文广告

原标题&#xff1a;Pythonrequestsunittestexcel实现接口自动化测试框架 一、框架结构&#xff1a;工程目录二、Case文件设计三、基础包 base 3.1 封装get/post请求&#xff08;runmethon.py&#xff09; 1importrequests 2importjson 3classRunMethod: 4defpost_main(self,url…...

wordpress加载完再显示图片/推广网站平台

USART/UART串口通信 1、USART介绍 通用同步异步收发器(Universal Synchronous Asynchronous Receiver and Transmitter)是一个串行通信设备,可以灵活地与外部设备进行全双工数据交换。 USART提供了一种灵活的方式,可以与需要行业标准 NRZ 异步串行数据格式的外部设备进行…...

自己建立网站的方法/优化大师win7官方免费下载

无论谁在使用电脑的时候都可能会发现搭建Python 3.5.2开发环境进行设置。很多对电脑不太熟悉的小伙伴不知道win10系统搭建Python 3.5.2开发环境到底该如何设置&#xff1f;其实win10系统搭建Python 3.5.2开发环境有什么简便的处理方式呢&#xff0c;其实只要依照1、首先从Pytho…...

可道网站建设/深圳网站优化哪家好

步骤1&#xff1a;声明表示基本动作方法的模块Taction //声明表示基本动作方法的模块Taction trait TAction { def doAction }步骤2&#xff1a;定义一下加入了前置处理和后置处理的特征TBeforeAfter trait TBeforeAfter extends TAction { abstract override def doAction {/…...