产品选型

单片机 浮点 单片机里面的CPU使用率到底该怎么计算?

小编 2024-10-18 产品选型 23 0

单片机里面的CPU使用率到底该怎么计算?

上周提到为什么我们需要关注CPU利用率的问题,总结一句话就是,利用率越低,你的系统效率越高、响应越快,实时性越高。但是并没有具体说该如何计算CPU利用率。

今天,借助国产操作系统RT-Thread,我们开始实操一番。

在实操之前,需要简单了解几个概念。

钩子函数,即以hook命名的那些函数。那么什么是钩子函数呢?说白了,就是一个函数指针 ,只是这个函数比较特殊一点。

特殊在哪?操作系统某些指定位置才会设置钩子函数,比如程序运行到空闲任务了,为了不修改系统源码(没事别修改源码,很危险的事情,除非你是真大佬),系统会提供一个设置钩子函数的函数接口给你,当你需要在空闲任务中执行某些功能时,用这个函数设置你的需要功能函数就可以了,等系统运行到空闲任务,他就会帮你调用这个函数了。

这个功能看着是不是有点眼熟,对的,和所谓的回调函数是一个道理(我也不明白为啥叫钩子函数,可能是因为和系统有关,和通用的回调函数又有点区别,所以就称之为钩子函数吧,不过你不要管名称,只要知道意思就行了)。

除了在空闲任务可以设置钩子函数,还有可能在任务切换、系统启动、任务创建等等关键的地方设置,当然了,这里的每一个钩子函数都是一个单独的函数指针。

前面也说了,设置钩子函数的目的只有一个,那就是可以让你在不修改系统源码的情况下达到私人目的,让系统的扩展性更强,比如今天说的内容(还有下次介绍的线程CPU使用率问题),如果系统没有空闲钩子函数的存在,你只能去修改系统源码才能达到目的啦。

还有文章所说的线程(task)、任务(thread),其实在RTOS中都是一样的。在 uCOS、FreeRTOS 中,叫任务,RT-Thread 叫线程,只是叫的名称不一样,内容都是差不多的。

然后再大概说说怎么计算的问题。也就是在空闲钩子函数里面,我们需要干什么事情才能到达CPU计算的目的。

首先,第一步肯定是设置钩子函数,其次就是钩子函数该怎么写的问题。

这个网上一搜就出现了(鱼鹰也是网上搜的代码),然后就要分析为什么这么写。

前面说过,CPU利用率其实是首先计算一段时间内空闲任务执行时间,然后反推其他任务的执行时间。

这里有两个问题,一段时间是多少?空闲任务的执行时间怎么计算?

先说第二个问题。用定时器时间掐?好像不好,因为你不知道什么时候程序就离开了空闲任务跑去执行其他任务了,而即使你可以知道它什么时候离开空闲任务的,那也会增加计算难度,不是好的方式。

那怎么办?还记得刚学单片机时你是怎么进行软件延时的吗?对,就是用这个方法,软件延时!

只要程序执行到空闲任务了,就用一个变量不停自加。这样就可以根据变量值来大概计算空闲任务的执行时间。

但是这里又存在一个问题:如果这个变量一直自加,肯定会溢出,该怎么解决。

加大变量的大小,比如原先使用一个字节、两个字节的,那么如果溢出,就用四个字节、八个字节。

但32位系统最大能支持的也就8个字节了,如果还是溢出了咋办?再套一个循环,一个循环的数加完了,再加另一数就行了。

但是还有一个问题,如果说自加的时间不做限制,那么再多的变量也不行,而且还会影响CPU计算的实时性,也就不能实时反映CPU利用率了;而如果时间太短,如果刚好有任务的执行时间在这个范围,那么很可能你计算CPU利用率就直接是100%了。

比如说你一个任务需要执行10毫秒,然后你计算CPU的周期也是10毫秒,那么可能刚好开始计算时跳到了那个任务执行,那么你的变量就没有自加了,也就会显示100%利用率了。

这里其实说的是前面的第一个问题,一段时间是多少?

对于这个时间,因为鱼鹰看的书籍比较少,所以也没有理论支撑(如果有道友知道的,不如留言)。

但是肯定既要考虑变量溢出(这个可以通过加循环方式解决),又要考虑实时性,还要考虑其他任务的最大执行时间 ,否则本来系统没有问题的,但是因为你追求实时性,导致CPU利用率80%、90%的,那就很尴尬了。

以上讨论如果没有经验可能比较难理解,所以建议大家在看完后面内容,实操过后,再回头重新看一遍,这样才有更深的理解。

现在再看CPU计算公式:

cpu_usage = (total_count – count)/ total_count × 100 %

cpu_usage: CPU利用率;

total_count:单位时间内全速运行下的变量值;

count:单位时间内空闲任务自加的变量值。

total_count这个值表现了单片机全速运行下,所能达到的最大值。所谓全速运行,即不响应中断,也不去执行其他任务 ,就单纯让它在一个地方持续运行一段时间,这个值可以体现CPU的算力有多大。

比如,51单片机,可能这个值自加10毫秒之后只有100,STM32F1单片机自加能到1000,而STM32F4单片机能到2000,这样就能体现他们之间的算力差别了。

这个值可以是动态的,也可以是静态的。静态有静态的好处,动态有动态的好处。

所谓的静态是指,在系统没有运行任务时,关闭所有的中断,自加这个值。这样,这个值比较准确,但是如果一开始这个值计算错了,那么后面的计算肯定也是有问题的,而且如果系统启动后长时间既不启动任务,也不响应中断,肯定对系统有一定的影响。但是好处是,系统消耗更少,因为他只计算一次。

而动态计算,则是在空闲任务中,当这个值为零时,计算一次,之后只会在空闲任务自加的变量值超过这个数时,才会更新这个值,这样一来,最终还是能准确反映CPU利用率的。好处是,不需要在开机时关闭所有中断,当然坏处是,前期可能不是很准,因为可能由于中断原因导致计算的值较小(中断处理时消耗了算力)。

废话太多了一些,直接开始干吧。新建一个文件,拷贝如下代码:

以上的代码网上找的,首先分析这两个宏,第二个宏就是前面所说的防止变量溢出用的,而第一个值就是CPU计算周期,这个值比较关键,后面再说。

首先在系统启动前设置钩子函数:

然后,就没有然后了。

对的,设置完之后就可以了,但为了让我们能观察到,可以打印出来。

我们可以观察效果如何,开始设置计算周期和任务延时函数一样,10毫秒。

测试结果:

可以看到,因为是动态计算的,所以开始为0,因为系统首先运行其他任务,只有其它任务不运行时,才会开始运行空闲任务,所以CPU利用率为0。

但是即使后面有值了,你也会发现CPU利用率变化很大,0.82%~1.5%。而且你会发现除了开始的0.0%,后面又再次出现了,这又是怎么回事?

通过设置断点分析,发现,这是因为计算值超出了开始的值,重新设置了:

这就是动态计算的一些问题了,它在一开始的一段时间里,因为无法完全表现算力,只能通过后面不停的修正该值才能达到稳定。

现在修改计算周期 20 毫秒:

发现它的表现更差劲,4.3%~11.61%,而且会周期性出现低利用率的情况。

再改,100毫秒:

可以看到这个比较稳定了,13.71%~14.35%。

那么这个测试代码实际情况的CPU利用率是多少呢?

我们可以通过前面的笔记《》大概计算线程执行时间:

1.59毫秒,10毫秒执行周期,如果只有这个任务执行,大概1.59/10=15.9%(准确计算应该是 1.59/(10 + 1.59) =13.7%)。

和前面的100毫秒类似。

我们先不管前面的结果,先理解一下里面的计算方法。

首先,如果total_count开始为0,那么开始第一次计算。这次计算会关闭调度器。

计算过后,就不再进入。

之后就是动态计算过程:

和第一次计算一样,都是在一定时间内自加计数器,不同的是,这次不会关闭调度器,也就是说,如果有高优先级任务就绪,那么是可以执行其他任务的。

并且计时时间使用的是系统函数rt_tick_get(),单位为系统调度时间。测试环境中,系统调度时间为 1 毫秒。

有意思的是,在进行最终的计算时,采用了分步计算,首先计算整数,再计算小数。

为什么要这样做?效率!

这样的计算方法,可以将浮点运算转化成整型运算,这在没有浮点运算单元的单片机中,能大大减少计算时间。

另外,为了防止溢出,还使用了一个循环结构。

理解了以上内容,现在开始进行鱼鹰式深度思考:

1、 上面的分步计算是否存在问题?

2、 关调度器只关闭了任务调度,但还是会响应中断,这能够体现单片机最大算力吗?

3、 使用rt_tick_get() 函数进行计时,精度是多少,会影响最终的计时吗?

4、 有必要使用循环体吗?如果单位时间内不溢出,是否不用循环体会更好?

5、 前面的CPU使用率为什么会跳动,按理说任务的执行时间应该是确定的,也只有一个任务在运行,不应该跳动才对?

6、 10毫秒的计算和100毫秒的计算差别在哪?

7、 终极问题,如何精确计算CPU使用率?

上面的问题,如果只是粗略计算,其实都可以不用考虑,本着对技术的热爱,还是聊一聊好了。

1) 分步计算,不知道你想到了什么BUG?这个问题其实在以往的笔记都提过,这次再说一次。

当你在获取CPU使用率时,如果刚好在更新这两个值,那么可能整数部分是上一次计算的值,而小数部分却是这次计算的值,那么肯定有问题。

这就涉及到数据完整性获取的问题。怎么解决。关调度器、关中断都可以。

但是因为是粗略计算,那么小数部分即使是错误的,也没事。

2) 因为只关调度器,所以对于中断还是会响应,比如说你设定计算周期为100毫秒,那么1毫秒一次的systick中断肯定会执行,那么在100毫秒中,有100次进入中断执行,而这些算力在上述算法中是无法体现的。

3) rt_tick_get() 函数精度问题,因为这个是系统的软件计时器,所以在测试环境中为1毫秒递增一次,也就是说它的精度在1毫秒。因此,在100毫秒的计算周期里面,有1% 的误差存在,在10毫秒的计算周期里面,误差10%!

4) 有没有必要用循环体?在1秒计算一次的情况下,即使不用循环体,也不会导致溢出问题。而且使用了循环体,还会导致精度降低,毕竟样本少了。比如使用循环体最大值为100,不使用时为10000,哪个精度高?

5) CPU使用率跳动问题。因为是测试,所以只有一个任务在运行,而且任务很简单。

这个任务的执行时间应该是固定的才对,但即使是使用了后面的高精度计算方式,CPU使用率还是会跳动,这是为什么?

第一,rt_kprintf函数执行时间是不固定的,不固定在哪,比如要显示的变量开始是1,后面是1000,因此它输出的字符串不一样,并且打印时间也不一样,因为是查询方式打印,所以差别很大!这就是我为什么推荐DMA打印的原因,未使用前是10%,使用后可能就是1%,甚至更低。

第二点,也是非常容易忽视的一点,插入的中断执行时间。

系统每隔1毫秒需要进入systick执行一次(或者其他中断执行时间),如果说任务的执行时间超过1毫秒,那么中间必然会先执行中断,再执行任务,这样一来,因为中断的插入,导致时间不再那么准确了。而当你把打印的时间控制在 1 毫秒以内,那么CPU使用率会变的非常稳定。

第三:延时rt_thread_delay()函数本身的误差,受到系统精度的影响,这个延时时间其实也不是固定的,会有一定的浮动。

6) 10毫秒和100毫秒计算的差别?

如果说你的任务执行时间 小于1毫秒,那么在10毫秒和100毫秒的计算差别不是很大,但是如果说计算周期变成了5毫秒,即使任务执行时间小于1毫秒的情况下,计算值也是会在最大最小 之间来回跳动的。而执行时间一旦超过1毫秒,那么10毫秒和100毫秒的计算就有较大的差别。

并且测试的时候,因为系统延时时间是10毫秒,而计算的时候也是10毫秒的周期,所以出现了比较诡异的事情,因为按理说延时10毫秒,任务执行时间2.56毫秒,任务运行周期为12 毫秒(还记得前面所说的延时误差吗),CPU 使用率按理应该是 21.3 左右,实际上却是 6.5% 左右,相差太大了,这就非常奇怪了。而且如果更改执行时间为1.5毫秒时(通过修改代码修改执行时间),发现计算值又正常了;而即使不修改执行时间,修改计算时间为100毫秒,又正常了,这是怎么回事?

通过深入分析发现,刚好在主任务延时10毫秒的时候,切换到了空闲任务进行空闲时间计算,执行了9.4毫秒的时候,又切回到了主任务,所以计算时,得到了6.5%的计算值。

粗略表示如下所示:

通过这个分析,你应该知道,计算CPU的时候,尽量不要使用和任务延时时间一样的计算周期,否则会出现莫名其妙的事情;还有一点就是,任务的执行周期 = 任务执行时间 + 系统延时,而前面所介绍的计算方法只是粗略的表示,严格来说是有问题的。

7) 终极问题,如何提高计算精度?

通过以上分析,我们其实已经知道了计算时的一些问题点。首先,计算周期问题,这个可以根据系统来确定,但是千万要注意前面的提到的问题。如果说500毫秒计算周期可以满足要求的话,就没必要使用50毫秒,不然你会发现计算值跳动很大。

其次,时间精度问题,这个问题老生常谈了,鱼鹰建议是DWT,如果没有,找一个定时器代替也是可以的。

最后是单位时间算力问题,为了保证精确,可以关闭中断进行第一次计算,或者用短一点的时间,比如1毫秒得到一个算力,如果计算周期为100毫秒,那这个算力乘以100就行了。当然如果系统时钟不经常变的话,也可以通过静态方式先得到单位时间的算力,之后就以它为标准就可以了。这样就不会有长时间关中断的情况出现了。

但是计算算力的时候,千万千万要注意一点的是,C语言转化为汇编代码时,可能一样的代码,在不同的地方执行时间是不一样的 (比如前面代码的第一次计算和后面的计算,看似一样,但实际上有较大差别,原因就在于执行效率不一样),这个涉及到寄存器比内存效率更高的问题,所以计算算力时,可以把它封装成一个函数,这样,只要优化等级不变,那么函数的执行时间就可以认为是确定的。

有帮助的话,记得关注哦!

看完这篇,轻松弄懂STM32 C语言变量的定义和初始化

我们今天探讨C语言变量的定义和初始化。那么我们首先要明确三个问题。第一,我们要明白什么是变量,或者为什么C语言一定要有变量;第二个在C语言中如何去表达这些变量,或者说C语言都有什么类型的变量如何定义这些变量;第三,变量为什么要初始化,以及如何初始化。

第一个问题,关于变量,一个最通俗的理解就是变化的量。本来外在的物质世界就是在不断变化的,不是有句话么:“唯一不变的就是变化”。C语言作为描述客观世界变化的一种语言,首先就是要有能够对外界事物变化状态量化的工具,那么这个工具就是变量。数字世界,首先就是量化,这个是一切后续工作的基础。

我们下面拿几个常用的变量类型进行说明,比如char型变量,它主要是用来应对0 – 255之间变化的事物的,比如字符什么的。比如float,浮点型的变量,它主要是量化客观世界中模拟量的事物,比如汽车的速度、太阳光的强度等等;再比如int型的变量,它的描述范围就比char型大得多了,它主要是应对整数变化的客观事物的,比如学生的个数、苹果的个数等等。

那么实际上,我们说C语言的变量远不止这些简单的数据类型,是吧。我们还有数组,结构体,还有指针、栈、链表等等。每种数据类型的出现都是为了解决一个量化的问题,比如指针,它主要是定位量化计算机中内存寻址问题;比如结构体,它的定位主要是用来描述复杂事物的,就比如汽车,它不仅有行驶的速度,还有轮子的个数,椅子的个数等等;再比如栈,它的主要作用是解决任务切换以及函数调用时,程序现场的保护问题。

那么也就是说每一个变量类型或者量化工具类型的出现,都是有原因的,都是为了解决实际问题的。当我们从这个视角去看这些变量类型和必要性的时候,我们的理解就会深刻很多。举个例子,比如面向对象数据类型的产生,就是把方法或者函数集成到了一个类型中,这样就可以更为准确的去描述客观事物,比如一个狗狗,它不仅有一条大尾巴,还可以跑得非常快,大尾巴是数据,会跑且跑得快是方法。面向对象的语言比如C++或者Java,就把这些变量和方法封装起来,形成一个新的更为综合的量化工具,那就是对象。

站在C语言的基础上,往上看是C++语言等面向对象的,但是如果往下看,比如到了汇编级别,就是另外一番场景。我们知道汇编语言是最接近机器的语言,对于某一类型的单片机,它一般有几十条特定中汇编指令。但是我们说汇编语言是没有数据类型的,它操作的只有二进制类型的数据,并没有对这些数据进行按照其属性进行分类。没有根据属性对数据进行分类,其实也就是说没有对量化工具进行分类,那么人类的大脑就要耗费更多的能量去理解汇编程序,人的大脑本身都是很懒的,能省能量肯定是想办法节省能量。从这个角度上的看,汇编语言更像是机器语言。

但是我们说电脑本身就是机器,不同汇编语言的指令集才能真正反映各个芯片架构的不同,指令集不同可能对应的电路也是不同的。任何高级语言最后还是要在特定的机器上运行的,那也就是说这些高级语言最后还是要翻译成特定的汇编语言。这个翻译的工作就是编译器要做的事情。另外,软件的开发还要有量好的代码编辑环境以及调试环境(比如支持单步调试,实时查看寄存器及存储单元的数据),所以一款新的单片机是不是好用会有多方面的因素影响的,也不能只看指令集的执行效率。

第二个问题,C语言都有什么类型的变量呢?我们可以用一张表来大概描述一下,下面这张表对C语言的数据类型进行了相对完整的总结。大家可以看一下,整体的数据类型被划分为四类:基本类型,构造类型(组合类型),指针类型还有空类型。基本的数据类型肯定是根本,C语言在级别数据类型的基础上构造出更为复杂的数据类型,用于描述相对复杂的事物,比如结构体等等。那么C语言就是使用这些相对抽象的基本类型,去量化和描述纷繁复杂的外部世界的。我们在前面已经提到了绝大多数数据类型产生的原因,这里就不再赘述了。如果有还不理解的同学,可以自己上网去查一查资料。

从下面这幅图可以看出,不同的基础数据类型器长度是不一样的,而且同一种数据类型在不同的机器和编译器编译下其数据长度也是不一样的。不同的基础数据类型,所占据是二进制数据位数发生不同,这个是可以理解的。对于相对简单的事物,比如字符,本来就不需要使用那么长的位数来表达,这个是基本诉求。再一个,比如对于char类型这种需要较少位数就可以表达的可以量化的事物,如果使用int这种长度的数据去表达,本身也是对计算机存储资源的浪费。基于上述两个原因,才出现了不同的数据类型有不同的长度的现象。我们在实际编程的时候,从设计的角度上来看,肯定是选择使用最少的存储位数来量化自己要描述的事物,这样占用的资源才能是最少。当程序代码行数非常多的时候,这种差异就会相对非常明显了。

下面,我们来探讨第三个问题,变量为什么要初始化以及如何初始化。我们首先解释一下为什么单片机数据最好要初始化。众所周知,变量是存储在RAM中,掉电后即丢失,上电后默认全为0。那么这样的话没赋初值的变量值全为0,这也应该是大家认为理所当然的。但是实际上并不是这样,有些类型的单片机,当单片机复位的时候(包括硬件复位即按下复位按钮,看门狗复位,以及其它软件程序复位),单片机只是重新跳回到main函数开始执行,而并没有清空RAM!所以,那些只是定义而没有赋初值的变量(尤其是全局变量)依然会使用复位前留下来的值!那么这样,程序运行可能就会出现异常的结果,尤其是指针变量。数据有一个初始的值,整个程序也就有了一个初始状态,初始状态确定了,如果程序设计得没有问题,那么就可以按照既定的规则跑下去。如果程序错误发生在初始位置上,那就太可惜了。大家在编程的时候,一定要注意这个现象。

那么下面我们看一下,如何对变量进行初始化。不同的变量类型,初始化的方式肯定是不一样的。首先对于基础的数据类型,可以直接初始化成想要的值即可。那么对于数组、结构体等类型,初始化的方法就具体问题具体分析,各具特色了。我们下面举例子进行说明。

一维数组:

int a[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

int a[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

int a[10] = {0};

int a[] = {1, 2, 3, 4, 5};

二维数组:

int a[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};

int a[3][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};

int a[3][4] = {{1}, {5}, {9}};

/*

1 0 0 0

5 0 0 0

9 0 0 0

*/

int a[3][4] = {{1}, {0, 6}, {0, 0, 11}};

/*

1 0 0 0

0 6 0 0

0 0 11 0

*/

int a[3][4] = {{1}, {5, 6}};

/*

1 0 0 0

5 6 0 0

0 0 0 0

*/

int a[][4] = {{0, 0, 3}, {}, {0, 10}};

/*

0 0 3 0

0 0 0 0

0 10 0 0

*/

字符数组:

char c[10] = {'I', ' ', 'a', 'm', ' ', 'h', 'a', 'p', 'p', 'y'};

char c[2][3] = {{'y', 'o', 'u'}, {'a', 'r', 'e'}};

char c[] = {"I am happy"};

char c[] = "I am happy"; // 可以省略花括号

char c[] = {'I', ' ', 'a', 'm', ' ', 'h', 'a', 'p', 'p', 'y', ''}; // 与上面等价

char *p;

p = "I love China"; // 正确

char c[14];

c = "I love China"; // 错误

c[14] = "I love China"; // 错误

结构体:

struct {

char name[20];

int age;

}student1, student2;

//匿名结构体

struct Student {

char name[20];

int age;

}student1, student2;

//声明结构体

struct Student {

char name[20];

int age;

}student1 = {"xiaoming", 20};

struct Student student1={.age=12}; // C99可以只对age进行初始化,其他变量初始化成零

联合体:

union Data {

int i;

char ch;

float f;

}a = {1, 'a', 1.5}; // 错误,不能同时初a.ch = 'A'; // 正确

对共用体赋值要指明赋值对象,如

a.f = 1.5; // 正确

a.i = 40; // 正确

a = 1; // 错误,没有指明赋值对象始化3个

union Data a = {16}; // 正确

union Data a = {.ch='j'}; // 正确 C99新增

枚举:

第一个枚举成员的默认值为整型的 0,后续枚举成员的值在前一个成员上加 1。

声明

enum Weekday {sun, mon, tue, wed, thu, fri, sat};

定义

enum Weekday workday, weedkend;

赋值

enum Weekday {sun=7, mon=1, tue, wed, thu, fri, sat};

// sun=7, mon=1, tue=2, wed=3, thu=4, fri=5, sat=6

把上述三个问题到搞清楚后,我们的这篇文章到此就结束了。

参考资料

(复制链接在浏览器打开)

① C语言数据类型总结

https://blog.csdn.net/xingjiarong/article/details/46942649

② C++继承

https://www.runoob.com/cplusplus/cpp-inheritance.html

③单片机C语言探究--为什么变量最好要赋初值

https://blog.csdn.net/weixin_34342207/article/details/92999746

④ C语言-定义与初始化总结

https://blog.csdn.net/syzdev/article/details/103532435

相关问答

51 单片机 中如何定义 浮点 数?

一般是浮点数乘以10的n次方变成整数进行计算等处理,在输出的时候再次处理小数点。比如,输入的是1234.567,1234保存到2个unsignedchar型变量里面,567保存到2...

在玩 单片机 ,的时候总是会说什么定点运算和 浮点 运算,始终不明白,这个 浮点 运算是什么?

定点运算实际就是整数(定点小数,可以视为整数)运算,浮点运算实际就是实数运算。假如处理器是32位的,也就是说32位都用来表示整数的话,那么对于无符号整数...

51 单片机 能存多少个 浮点 数?

51单片机的存储空间与具体型号和内存大小有关,但一般情况下,它能够存储有限数量的浮点数。由于浮点数在内存中的存储占用空间较大,因此在单片机程序设计中,使...

PIC24EP 单片机 支持 浮点 运算吗?

精度应该是没问题的但是速度很慢,尤其是乘除运算。这个问题值得考虑。精度应该是没问题的但是速度很慢,尤其是乘除运算。这个问题值得考虑。

怎么将 单片机 接收到的十六进制数转为十进制?

你说的数字带小数点的,叫浮点数。小数点的数据用十六进制表示用4个字节。如果小数部分都是0,则可以用整数表示。例如:用长整型表示。longdat;//用于...你说...

c51中为什么很少使用长整型和 浮点 型的数据类型?

大约就以下4种:无符号字符型:0-2558位有符号字符型:-128-1278位无符号整型:0-6553516位有符号整型:-32768-3276716位当然还有位变...大约就以下4种:无...

51 单片机 可以定义单精度数据吗?

单片机可以定义单精度数据。单精度数据是指使用32位来表示浮点数的数据类型,它在单片机的程序中可以被定义和使用。单片机通常具有浮点运算单元(FPU),能够进...

单片机 中if与switch的差异有哪些?

if和switch都是C语言的判断语句一般的场合使用if或者switch都可以达到相同的效果,但其实也是有很大的差异和优缺点的。什么时候使用if?if顾名思义就...

DSP与 单片机 的区别?

存储器结构不同单片机使用冯。诺依曼存储器结构。这种结构中,只有一个存储器空间通过一组总线(一个地址总线和一个数据总线)连接到处理器核。大多数DSP采用...

让小车到达指定位置有哪些PID算法?

许多学生不知道PID是什么,因为许多学生不是自动化的。他们需要信息和程序来开口说话。很明显,这种学习方法是错误的。至少,首先,您需要理解PID是什么。首先,.../...

猜你喜欢