一、计算机基础
1、结构体和联合体的区别
结构体(struct)和联合体(union)的区别_结构体和联合体的区别-优快云博客
重要区别:结构体(struct)的成员各自拥有自己的内存地址;而联合体(union)只有一个内存地址,用于分配给所有成员共同使用。
优缺点:struct比union更占用内存。
疑问:应用场景未知。struct的优点未知。
2、结构体和类的区别
一文讲明白C++中的结构体Struct和类Class的区别以及使用场景_结构体和类-优快云博客
重要区别:结构体(struct)的成员是公共的,在结构体外部可以直接访问;类(class)的成员函数是私有的,在类外不可以直接访问,必须通过调用公共成员函数进行访问
应用场景:struct适合不需要对成员进行变化操作的,只需要简单存储数据;而class更适合用于对成员频繁进行变换操作,实现复杂的操作。
3、数据结构命名格式
C中int8_t、int16_t、int32_t、int64_t、uint8_t、size_t、ssize_t区别-优快云博客
int8_t中的t为type或者typedef的意义,用于表示该变量是在typedef下定义的,不是一种新的数据类型。由于不同平台的int拥有不同的字长,这样可以用于区分。
4、字符和整数的区别
C语言字符型char和整型int的关系和示例_c语言char和int-优快云博客
字符就是0-255整数范围内对ASCII码表的映射,具体如下博客所示。
char字符对应ASCII码表0-255(全)_char字符对应的ascii码值-优快云博客
因此char定义一个8位的字符时,其本质仍是整数,但用ASCII码的形式形成了字符串。
例如:
//正确例子
char a[]="abc"
char a[4]="abc"
//错误例子
char a[3]="abc"
由于char类型需要一个空字符(\0)结尾,所以需要空出至少一个字符。
//指针定义字符串
char* a='abc'
printf("%s\n",a)
//输出结果
abc
用指针即可存储字符串。但是a理论上应该是地址,*a才是数据,上代码展示的结果为a输出数据,是因为printf函数的原因吗?
还有string定义字符串,还不太了解。
字符串和字符在变量上的区别
//三种定义的方式
uint8_t test[5]={0,1,2,3,4}; //数值
uint8_t sectest[]="01234"; //字符串
uint8_t trdtest[]={'0','1','2','3','4'};//字符
数值就是直接使用ASCⅡ码。
字符串是使用字符“1”对应的ASCⅡ码,即49。
字符串结尾会有\0作结尾,所以sizeof取其长度后会比可见的长度多1。
5、常见变量的字节数,字节与字的关系
[c++]-uint8_t,uint16_t,uint32_t,uint64_t代表含义及其标准定义_c++ uint-优快云博客
char字符类型占1个字节,int整形占4个字节,float浮点型占4个字节
long类型的字节数,代表了处理器的位数。(32位机是4个字节,64位机是8个字节,但具体还有待测试)C语言中char、short、int、long各占多少字节_int short long字节-优快云博客
/* There is some amount of overlap with <sys/types.h> as known by inet code */
#ifndef __int8_t_defined
# define __int8_t_defined
typedef signed char int8_t;
typedef short int int16_t;
typedef int int32_t;
# if __WORDSIZE == 64
typedef long int int64_t;
# else
__extension__
typedef long long int int64_t;
# endif
# endif
/* Unsigned. */
typedef unsigned char uint8_t;
typedef unsigned short int uint16_t;
#ifndef __uint32_t_defined
typedef unsigned int uint32_t;
# define __uint32_t_defined
#endif
#if __WORDSIZE == 64
typedef unsigned long int uint64_t;
#else
__extension__
typedef unsigned long long int uint64_t;
#endif
常用的uint8_t实际上是unsigned char,无符号字符0-255。
u8,u16,u32代表的是位数,即8位、16位、32位
数组所占字节按照数组中的元素对应的数据类型来计算。
对字(Word)、半字(Half-Word)、字节(Byte)的概念说明_half word-优快云博客
字的位数有系统决定,32位系统一个字即32位(4个字节),半字就是16位(2个字节)。
6、static静态变量
C语言:关键字---static(声明静态变量)_static声明-优快云博客
一般加了static、extern来修饰的即为声明,其他都为定义。
上博客说,除了extern都是定义?
普通局部变量
存放在进程的栈空间,若不显式赋值其初始值随机,在函数结束时该变量使用完毕便会被释放。
static局部变量
存放在进程的静态数据区,即使不显式赋值其初始值也为0,且在函数结束时仍旧保持该值不变(即下次不会再重新赋值)。
普通全局变量
定义在函数外部,在其他文件中可以使用extern声明后使用,其他文件无法再使用该变量名。
static全局变量
只能在本文件中使用,在其他文件中可以有与他同名的变量,互不影响。
static函数
与static全局变量类似,只能在声明该函数的文件中使用,其他文件可以用同名函数,互不影响。
应用
使用static可以保证不同文件之间不会因为变量名而发生错误。
7、常量
和宏定义作比较~~~~~~~~~~~~~~~~~~~~~~~~~~
8、#if、#endif、#elif、#else、#ifndef(#ifdef)
预处理条件判断的经典三种用法 #if #ifndef #elif #endif - 孤独的二进制 - 每天5分钟 带你走近Arduino开发板的神奇世界_哔哩哔哩_bilibili
本质上是宏定义的使用,这些条件都是为了配合#define使用
1、由于各个头文件之间会交叉引用,加上如下判断代码,便可以在创建一次h文件后不再创建多次。
//sys.h文件
#ifndef __SYS_H
#define __SYS_H
*****
#endif
//key.h文件
#ifndef __KEY_H
#define __KEY_H
#include "sys.h"
#endif
//main文件
#include "sys.h"
#include "key.h"
2、用于整体项目的参数变换
#define STM32
//#define ESP32
#if STM32
****
#elif ESP32
****
#else
****
#endif
3、用于debug模式(和2同理)
#define DEBUG 1
//#define DEBUG 0
#if DEBUG
printf("debug info")
#endif
9、c文件和h文件
一般约定俗称,在头文件声明函数等,在c文件中定义函数等。
h和c文件关系的链接怎么形成?
不同.c文件的函数如何相互调用_两个.c文件如何相互调用-优快云博客
10、内存地址所占字节
每个内存空间由8bit组成,即1个byte;并且每个内存空间对应各自的一个地址(物理地址)。
(44 封私信 / 81 条消息) 为什么计算机被设计为每个内存地址对应8个位(1个字节),而不是更多的位(比如16个位、32个位)? - 知乎 (zhihu.com)
一个内存空间8位是最终确定下来的,为了达成某种目的。
物理地址:存储空间本身的地址
逻辑地址(虚拟地址):代码中为了方便表示,如指针变量的地址?
从计算机底层认识指针!深入理解C语言指针!_哔哩哔哩_bilibili
cpu缓存(SRAM):由触发器构成,1位数据就需要很多门电路,1个门电路需要好几个晶体管
内存条(DRAM):1位数据只需要1个晶体管,通过电容充放电来存储
终于!有人讲懂了DRAM和SRAM!-电子工程专辑 (eet-china.com)
DRAM:dynamic random access memory,动态顾名思义,电容充放电动态过程。
SRAM:static random access memory,静态,由电路瞬间响应。
RAM、SRAM、DRAM、SDRAM、DDRSDRAM等之间的区别_运行内存和内存那个是srm-优快云博客
单片机的内存地址:
sram(RAM):SRAM存储原理-优快云博客
flash(ROM):深入了解存储系统之闪存 (Flash Memory) - 知乎 (zhihu.com)
单片机的内存空间包括几部分:
1、CODE
2、SRAM
3、外设
STM32深入系列01——内存简述(Flash和SRAM)_stm32内存-优快云博客
11、 寄存器
STM32中的32代表:CPU位数=CPU中寄存器的位数=数据宽度=CPU能够一次并行处理的数据宽度
CPU位数、操作系统位数、指令集、寄存器位数、机器字长等_操作系统位数和机器字长-优快云博客
例如,STM32F103中USART的DR(数据寄存器)、SR(状态寄存器),这两者都为16位寄存器,而SR的各个位代表了各种控制,DR的低8位存储数据(高8位硬件强制置零)。
寄存器由D触发器(不确定)构成,1个D触发器代表一位,多个D触发器组成寄存器便是一个多位的寄存器。
12、CPU
CPU组成:运算器(ALU)、控制器(CU)、寄存器、缓存(cache)、总线和接口等。
CPU工作方式:CPU的基本工作是执行存储的指令序列,即程序,程序的执行过程实际上是不断地取出指令、分析指令、执行指令的过程。
CPU运行效率的决定因素:核心数、主频、寄存器位数、缓存容量等
什么是CPU?CPU的性能指标是什么?_决定cpu性能的主要指标-优快云博客
CPU指令集:指令集是连接CPU和操作系统的桥梁:
CPU——指令集(机器语言,实现CPU基本功能)——操作系统HAL库
【计算机基础】-详解CPU位数、操作系统位数、编译器位数_cpu的位数-优快云博客
二、固件代码部分
1、模块化编写思路
将每个要实现的功能模块放置到各自的C文件当中,形成函数形式,并且设定好全局变量。main文件里调用各功能函数即可。
2、keil调试
Keil5软件使用-进阶调试篇_keil调试教程-优快云博客
1)追踪变量watch1、watch2中,全局变量可以一直监视,局部变量只能在函数中监视,static变量无法监视。
寄存器窗口internal-mode的模块:thread表示在任务中,handle表示在中断中。
2)程序卡死后进行debug测试:debug-run-进入卡死状态-stop-查看命令停止位置(PC位置)-怀疑跑飞-观察call stack。(可能存在跑飞现象,停止后仍不显示黄色命令指针)
3)Keil优化等级影响局部变量查看,默认level3最简化,会在debug时无法查看局部变量;level0时最复杂,可以debug查看局部变量。
3、STM32CubeIDE
4、HAL库使用
1、DMA配置
HAL_ADC_Start_DMA(&hadc1,(u32*)ADC_Value,Channel);
该函数为DMA方式调用ADC采集数据时的函数,相比查询和中断两种方式,编写起来最为简单。
注意其中ADC_Value为数据缓存区,输入的必须是地址(一般输入为数组时,其数组名便为地址;为变量时,需要加上&来取地址),
Channel即为开启的ADC通道数(多通道依次排序,单通道则是同一个值做循环),同时决定了其与缓存区放置的个数。一般常用:Channel数=ADC通道数
当Channel数>ADC通道数时,例如Channel=1即只放在缓存区1(即地址指针不会增加),Channel=4即放在缓存区1,2,3,4(即地址指针会增加)
调用一次,只能采集一次。
在使用单通道采集时,需要将其配置为如下图所示
Scan、Continuous、Discontinuous都需要禁用,原理尚未弄懂
DMA处的配置如上图所示,Mode设置为Circular和Normal都可以。
2、HAL_XXX_Init与HAL_XXX_MspInit的关系
HAL_XXX_Init函数中会调用到HAL_XXX_MspInit,从而可以将初始化参数配置与硬件管脚映射部分分离。
5、PWM函数设置
为了简化占空比为0-100,可以在SetCompare()函数中设置一个变量为
A=compare*period/100
将A作为最终的compare值去比较
这样便可以实现compare0-100表示占空比0-100的简化设置。
sConfigOC.Pulse该参数即初始的脉宽值,相当于Compare。
6、位置式PID与增量式PID
推导过程略过
位置式PID
u(x)=Kp*e(x)+Ki*[e(0)+e(1)+...+e(x)]*dt+Kd*[e(x)-e(x-1)]/dt
简化之后为
u(x)=Kp*e(x)+KI*[e(0)+e(1)+...+e(x)]+KD*[e(x)-e(x-1)]
即此处的KI和KD都和时间相关,dt表示单位时间间隔
增量式PID
Δu(x)=u(x)-u(x-1)=Kp*[e(x)-e(x-1)]+Ki*e(x)*dt+Kd*[e(x)-2e(x-1)+e(x-2)]/dt
简化之后为
Δu(x)=u(x)-u(x-1)=Kp*[e(x)-e(x-1)]+KI*e(x)+KD*[e(x)-2e(x-1)+e(x-2)]
u(x)=u(x-1)+Δu(x)
7、USART/UART配置
【STM32F4+CubeMX零基础快速入门】串口收发全攻略_哔哩哔哩_bilibili
正点原子教程,配置串口基本步骤
1、初始化;2、配置中断:HAL_UART_Receive_IT;3、设置中断优先级;4、编写中断服务函数;5、串口数据发送
8、宏和extern变量在头文件中的区别
宏定义的值是可以直接通过引用.h文件便可以引用
而变量要想通过头文件引用过来,必须加上extern的声明
9、SWD和JTAG
SWD是ARM公司开发的协议,可以用于调试和下载;只需2个引脚。可以通过其I/O口打印调试信息?
JTAG兼容性更强,可以用于STM以外的其他芯片,而且功能更丰富。
10、操作系统
相对于操作系统而言的,就是裸机开发。
例如STM32,裸机开发其实是对硬件直接操作,编写对寄存器进行控制;
而操作系统,则是将各种封装好的API提供调用,从而不用去配置寄存器,省时省力。
PS:一般的C语言开发本身也已经封装好了各种函数,最底层的机器语言则为01代码。
11、中断配置的一般步骤
以定时器中断为例:
配置时钟——配置外设参数(定时器就是分频系数,计数值,计数模式等等)——配置中断优先级、使能中断、启动中断——编写中断服务函数(及中断回调函数)
三、硬件电路部分
1、IO口电气特性
st单片机IO口类型FT、TC是什么意思_百度知道 (baidu.com)
TC:标准3.3V
FT:5V耐压
2、推挽输出和开漏输出
推挽输出:
由一个PMOS和一个NMOS组合而成的推挽结构,低电平输入时,PMOS导通,NMOS截止,输出便被拉高(即连接VDD);当高电平输入时,NMOS导通,PMOS截止,输出便被拉低(即连接GND)。
特点:
能实现真正的高电平和低电平,在两种电平下都有驱动能力。
无法线与,当用两个GPIO控制一个输出时,当一个输出高电平,另一个输出低电平时,短路,两个导通的MOS管必会因为大电流而烧毁一个。
开漏输出:
类比于开集输出,使用单个三极管来控制信号的导通与否(B极作为控制开关);
开漏输出使用单个MOS管来控制输出,例如NMOS,对高电平没有驱动能力(因为没有电阻形成电流?),必须上拉电阻才能真正输出高电平。
特点:
可以根据上拉电阻来调节电源特性,适合运用在需要调节电平的地方,开漏输出只有一个MOS管来控制接地或者高阻态,因此上拉任何电压时,控制高阻态可以输出任何电平。
可以线与,可以多个GPIO口来控制同一个输出,只有GPIO都为1的时候才能输出1,否则为0。
GPIO推挽输出和开漏输出
GPIO推挽输出和开漏输出模式区别详解_gpio的推挽输出和开漏输出有何区别-优快云博客
当配置为推挽输出时,两个MOS管都工作;当配置为开漏输出时,只有NMOS启用。
GPIO在配置为输出时没有上拉下拉的配置?
单片机通信中推挽和开漏的应用
总结:推挽输出更适合单线通信(SPI,UART),开漏输出更适合双向单线通信(I2C,One-Wire)。
复用推挽输出与复用开漏输出
简单而言,一般的输出模式为CPU控制寄存器写入;而复用则是由片上外设决定,相当于cpu先控制外设,外设再产生信号(如UART推挽输出),所以复用时无上下拉。
3、电压电流采样电路
同向放大电路(同向和反向的区别待分析,注重反向端负反馈的特性)
同相放大电路-通俗易懂教程 - 知乎 (zhihu.com)
定义:输入信号从同向端输入
特点:
1、输入阻抗高(输入端高达几MΩ),意义是?
2、输出阻抗低(输出端低至几Ω),带负载能力强。
3、电压放大倍数,A=(R2/R3)+1
4、输出和输入保持同相位
应用:
1、小信号的放大:因为输入阻抗高,即使放大倍数极大也可以对信号调节。
2、电压跟随器:将输出电压全服负反馈到反向输入端,就可以使输出电压等于输入电压。
3、有源滤波器:在输入端串并联电容电感等,从而可以选取需要的信号频率。
电压采样
电压、电流采样电路设计以及放大倍数计算_电流采集电路-优快云博客
通过分压然后放大的形式获得Vout,然后由单片机检测该值。
为什么要用正向放大?为什么不是反向放大?
电流采样
低端电流采样
高端电流采样
4、差分放大电路
6.深入浅出:差分放大电路——参考《模拟电子技术基础》清华大学华成英主讲-优快云博客
简单概括,差分放大电路就是为了减小共模信号的干扰(比如对地信号有波动,那么便可以消除)。
差分放大电路芯片AD620
特性:高精度:40ppm最大非线性精度、低失调电压、低失调漂移
5、TTL和RS232
RS232电平和TTL电平有什么不同?如何转换? - 知乎 (zhihu.com)
TTL和RS232之间的详细对比_232 ttl-优快云博客
UART、RS232、TTL关系浅析 - 知乎 (zhihu.com)
PL2303是USB转RS232的芯片,为什么开发板上用它连接单片机串口和USB接口呢?单片机串口不是TTL电平么?_百度知道 (baidu.com)
串行通信/并行通信和UART、SPI、I2C、USB以及TTL、CMOS、RS-232、RS-485区别详细整理_串口与usb,spi通信有啥区别-优快云博客
1、TTL和RS232的电平标准范围不同,TTL一般为0-5V,而RS232为正负十几V,且RS232使用负电压表示逻辑1,正电压表示逻辑0。
2、TTL一般用于嵌入式设备,RS232(属于COM)一般用于PC级设备。
3、PL2303芯片是RS232(TTL)转USB(TTL),
个人理解,USB是一种通信方式(USB广义上也是串行),而RS232(串口)是另一种通信方式,那么此处的转换的意思是通信方式上的转换;区分于RS232转TTL,该者为电平上的转换。此处的RS232实际意义相当于UART。
通信方式列举:串口通信(serial communication)、USB、蓝牙、以太网
逻辑电平列举:TTL、CMOS、RS232、RS485
电平和通信方式,是否无法同时做转换。
6、三极管
三极管的两个二极管朝向恰好相反。
三极管的奥秘:如何用小电流控制大电流_三极管怎么用小电流控制大电流-优快云博客
三极管的集电极反偏为什么会导通,由于三极管NPN各自层的厚度和掺杂不同。如下图所示,导致发射极的多数电子到达基极后只能与少量空穴结合,从而剩下的大量电子被集电极的高电势吸引,从而流动过去。顾名思义,发射极发射电子,集电极收集电子。
7、线性稳压器(LR)和开关稳压器(SR)
线性稳压器一般只能做压降,常见的有LDO,原理简单。
开关稳压器,也常被称为DC-DC,使用开关电路来做到升压、降压等的效果,电路设计复杂,效率高。
8、集成运放(模电视频)
还搞不懂虚短与虚断概念?虚断与虚断通俗讲解,几分钟带你搞定_虚短虚断的概念-优快云博客
运放特性:输入阻抗无穷大,当输入电压进输入端时,其电流几乎为0,称为虚断。
开环的时候工作在线性工作区:Uo=A*(Up-Un)——A为1,000,000(同向反向定义:当另一者输出为0时,Up与Uo相位一致,Un与Uo相位相反)
比如±15V供电时,输出假设最多14V,那么在A的放大倍数下,Up-Un最大可以达到14uV才能放大,否则饱和。(而一般情况下14uV视为导通,因此一般Up=Un,称为虚短)
运放组成部分:输入级(差分放大)、中间级(电压放大:共射级)、输出级(互补对称电路?)、偏置电路(提供电源)
中间级的放大倍数要求:RC要大,同时满足IC要在mA级别——此时电流源即可满足
9、PL2303USB转TTL模块调试心得
1、模块上的指示灯能有效显示是否数据有发送或者接收,从而判断问题来自上位机或者下位机。
2、当与下位机串口引脚相连时发现,发送数据后TXD指示灯不亮,可能是上下位机TXD与TXD相连的原因。
10、电源抑制比PSRR(即纹波抑制比)
关于dB计算,本身的计算的是功率系数为10,转换成电压后就便为20
dB=10lg(P1/P2)=10lg((U1/U2)^2)=20lg(U1/U2)
dB运算是log前面是10还是20_百度知道 (baidu.com)
11、MOS管
1、源极与衬底相连
2、栅极电压Ugs>0时,多子空穴被打到衬底底部,少子电子被吸到衬底顶部,从而在两个N极之间形成沟道,因此称为N沟道型;Ugs越宽,沟道的宽度越宽。
(SiO2绝缘层直接被挪开了?电场强行开辟出了沟道)
3、增强型和耗尽型:
增强型要加上Ugs>Ugsth,才能形成沟道
耗尽型自带沟道,Ugs>Ugsoff时,都是开启状态
12、电机驱动信号
步进电机通过斩波恒流驱动
斩波:就是恒定的直流通过不断开关来控制输出的电压,类似于PWM。
斩波恒流:通过PI控制算法来进行电流检测和控制稳定。
13、FPGA
FPGA本质上是一个可以编程的数字电路器件,软件用于配置其硬件的走线、寄存器等等。
FPGA的时钟是数字电路器件中需要的时序。
CPLD与FPGA在原理上有一定差别,但都属于可编程数字电路器件。具体选型需要哪个,取决于项目情况的需要,比如FPGA可以拓展PLL、DSP等等,因此具有更高的扩展性。