0%

参考视频

思考与心得

  1. 本书结构为:编码风格、系统移植系统配置、任务函数、中断配置、临界区保护、多任务、列表结构、系统任务调度器、任务切换、时间管理、队列结构、信号量、软件定时器、事件标志组、任务通知、Tickless模式、空闲任务、内存管理

  2. 跟今年2月份学的rtThread差不多,rtThread是参考官方文档学习的,学习了内核的一部分,和本次学习《FreeRTOS源码详解与应用开发》的内容差不多,也是借本书回忆一下之前学的实时系统。比较关心对接口的使用,本书的源码详解有一部分没有细看,日后需要的时候再啃源码,也可以自己手写一个实时系统来理解内核中核心的部分。

  3. 有些公司可能不想用实时系统,理由是影响速度,那么问题来了,什么情况下需要采用嵌入式操作系统呢?看过这个知乎的回答就知道,大型项目中裸奔会造成资源的浪费,特别是处理GUI、lwip、fatfs等,里面有大量的delay。在需要并行这些就需要用到rtos,使用os还可以实现应用层与底层硬件的隔离,可以方便分块开发、移植等。另外在资源数较少的mcu上不很适合使用rtos。

  4. 光看书没有用,我欠缺rtos的实践,最好是拿个项目练练手。

210710-第一块四层板-1.jpg

  • 第一次画四层板,嘉立创打板的时候出了点问题,嘉立创PCB解析和AD21.3.1版本的不兼容,导致内层存在空气间距

  • 我还测试了下这个TVS和PTC防反接啥的,效果都蛮不错的

  • 总体来说,感觉这块板子设计的蛮不错,目前没啥别的问题

参考

GND可以分为几种

  1. 模拟地线AGND
    模拟信号是微弱信号,容易受到其他电路大电流的影响,大电流会在模拟电路中产生大的压降,会使得模拟信号失真。

  2. 数字地线DGND
    有按键检测电路、USB通信电路、单片机电路,在由0跳变到1的过程,电压产生了变化,根据麦克斯韦电磁理论,变化的电流周围会产生磁场,也就会形成EMC辐射,使用单独的DGND与其他电路隔离,防止辐射扩散。

  3. 功率地线PGND
    大电流会造成不同功能电路之间的地偏移现象。

  4. 电源地线GND
    是所有电路的0V电压参考点,是电源的地线GND。

  5. 交流地线CGND
    在AC-DC电源电路中,一个是交流地线,一个是直流地线,交流地线作为交流电路部分的0V参考点,直流地线作为直流电路部分的0V参考点。通常为了在电路中统一一个地线GND,工程师会将交流地线通过一个耦合电容或者电感与直流地线连接在一起。

  6. 大地地线EGND
    地线是在电系统或电子设备中,接大地、接外壳或接参考电位为零的导线。为了增强电路的安全系数,工程师一般在高压大电流的项目中使用大地的地线EGND,例如在家用电器电风扇、电冰箱、电视机等电路中。

不细分GND会导致的问题

  1. 信号串扰
    假如将不同功能的地线GND直接连接在一起,大功率电路通过地线GND,会影响小功率电路的0V参考点GND,这样就产生了不同电路信号之间的串扰。

  2. 信号精度
    交流电源的地线CGND由于是正弦波,是周期性的上下波动变化,它的电压也是上下波动,不是像直流地线GND一样始终维持在一个0V上不变。将不同电路的地线GND连接在一起,周期性变化的交流地线CGND会带动模拟电路的地线AGND变化,这样就影响了模拟信号的电压精度值了。

  3. EMC实验
    信号越弱,对外的电磁辐射EMC也就越弱;信号越强,对外的电磁辐射EMC也就越强。假如将不同电路的地线GND连接在一起,信号强电路的地线GND,直接干扰了信号弱电路的地线GND。

  4. 电路可靠性
    电路系统之间,信号连接的部分越少,电路独立运行的能力越强;信号连接的部分越多,电路独立运行的能力就越弱。

GND和机壳的连接

​ PCB板卡置于金属机壳中,机壳一般接大地,PCB的GND与机壳之间经常使用一个电容(1nF/1KV)并联一个电阻(1M)连接。

  1. 电容是干啥用的
    从EMS(电磁抗扰度)角度说,这个电容是在假设PE良好连接大地的前提下,降低可能存在的,以大地电平为参考的高频干扰型号对电路的影响,是为了抑制电路和干扰源之间瞬态共模压差的。其实GND直连PE是最好的,但是,直连可能不可操作或者不安全,例如,220V交流电过整流桥之后产生的GND是不可以连接PE的,所以就弄个低频过不去,高频能过去的路径。从EMI(电磁干扰)角度说,如果有与PE相连的金属外壳,有这个高频路径,也能够避免高频信号辐射出来。

  2. 一般在1nF左右比较合适
    如果答主在变频器、伺服驱动器这样8~16kHz开关频率的工业设备上用这么大的电容值,那么,用户摸外壳会有触电的风险的。一般选到这么大,都是电路其他地方设计不合格,为了对付EMC测试,只好把这个电容加大的。最好是安规电容,GND和PE间选用Y电容

  3. 1M电阻是干啥用的
    这是对付ESD(静电放电)测试用的。因为这种用电容连接PE和GND的系统(浮地系统),在做ESD测试的时候,打入被测电路的电荷无处释放,会逐渐累积,抬升或降低GND相对与PE的电平,累积到一定程度,超过了PE和电路之间的绝缘最薄弱处所能耐受的电压范围,GND和PE之间就会放电,几个纳秒间,在PCB上的产生数十到数百安培的电流,这足以让任何电路因EMP(电磁脉冲)宕机,或者是让PE与电路之间绝缘最薄弱处所在信号连接的器件损坏。但是刚才说了,有时候又不能直接连接PE和GND,那么就用一个1-2M的电阻去慢慢释放这个电荷,以消除二者间的压差。当然1-2M这个数值是根据ESD测试标准选择的,因为IEC61000里面规定最高的重复次数只有10次/秒,如果你搞个1000次/秒的非标ESD放电,那么1~2M的电阻我觉得是不能释放掉累积的电荷的。1M电阻的高阻接地方式,往往是为了在提供EMC保护的同时或者说不影响防护效果的同时限制故障电流,印象中1M是根据人体模型得到的结果。比如内部挂了,GND连到高压上的故障,有这个1M,通过的电流不会伤害人体。

  4. PE不可靠!因为很多国内的客户根本不会给你接上有效的PE,也就是说,你根本无法依靠PE来提升EMS或降低EMI的指标。其实这也不能全怪客户,是因为他们的车间、厂房、办公室根本就没按照电工标准来修,压根就是没有接地线的!所以,我明白PE不可靠以后,就使用一些技巧让电路能够硬抗过EMS测试。

  5. PE(机壳)和GND直连是不行的。多见的系统是浮地,机壳连PE,PCBA不连PE,这样机壳就是个很好的法拉第笼,有效屏蔽外界。

环境

  • FPGA采用的是高云小蜜蜂家族的GW1N-LV4QN88C6/I5
  • 摄像头使用OV2640,可以配置数据为JPEG压缩后输出
  • 采用DC-DC降压到3.6V再LDO降至3.3V与2.8V,OV内核和FPGA内核采用DC-DC降到1.2V使用

FPGA部分

编程流程图

  1. I2C驱动
  2. FIFO配置
  3. 通过I2C配置OV2640寄存器
  4. 获取DCMI接口数据
  5. JPEG找出帧头帧尾
  6. 把有效数据存入FIFO
  7. SPI读FIFO数据输出

I2C驱动

参考:正点原子OV5640驱动程序

FIFO配置

通过FIFO SC HS IP核向导生成(SC是同步的意思),在使用的时候需要查看IP核用户指南,重点要看时序部分

SPI驱动

参考:SPI总线的原理与Verilog实现

经验总结

本次FPGA实现读取OV2640并SPI发出来的小功能,解决了低端MCU没有DCMI外设,遇到了IP核不会使用的问题,要多看手册里的时序部分。还有就是有些数据处理的时候要缓冲,加延时,否则会造成信号的错位。后面我要加强流水线结构的设计,多用并行的思想发挥FPGA的优势。

项目GitHub链接

https://github.com/hao0527/fpga-ov2640_fifo_spi

参考

SRAM与DRAM的区别

​ SRAM(Static RAM)与DRAM(Dynamic RAM),从名字上看,SRAM与DRAM的区别只在于一个是静态一个是动态。由于SRAM不需要刷新电路就能够保存数据,所以具有静止存取数据的作用。而DRAM则需要不停地刷新电路,否则内部的数据将会消失。而且不停刷新电路的功耗是很高的,在我们的PC待机时消耗的电量有很大一部分都来自于对内存的刷新。那么为什么我们不用SRAM来作为内存呢?

SRAM的基本单元结构图DRAM的基本单元结构图

210612-RAM-1.jpg

​ SRAM存储一位需要花6个晶体管,而DRAM只需要花一个电容和一个晶体管。cache(高速缓冲存储器)追求的是速度所以选择SRAM,而内存则追求容量所以选择能够在相同空间中存放更多内容并且造价相对低廉的DRAM。

​ 我们姑且不去讨论关于SRAM是如何静态存储数据(触发器)的。为什么DRAM需要不断刷新呢?

​ DRAM的数据实际上是存在电容里的。而电容放久了,内部的电荷就会越来越少,对外就形成不了电位的变化。而且当对DRAM进行读操作的时候需要将电容与外界形成回路,通过检查是否有电荷流进或流出来判断该bit是1还是0。所以无论怎样,在读操作中我们都破坏了原来的数据。所以在读操作结束后需要将数据写回DRAM中。在整个读或者写操作的周期中,计算机都会进行DRAM的刷新,通常是刷新的周期是4ms-64ms。

​ 关于SRAM和DRAM的寻址方式也有所不同。虽然通常我们都认为内存像一个长长的数组呈一维排列,但实际上内存是以一个二维数组的形式排列的,每个单元都有其行地址和列地址,当然cache也一样。而这两者的不同在于对于容量较小的SRAM,我们可以将行地址和列地址一次性传入到SRAM中,而如果我们对DRAM也这样做的话,则需要很多很多根地址线(容量越大,地址越长,地址位数越多)。所以我们选择分别传送行地址和列地址到DRAM中。先选中一整行,然后将整行数据存到一个锁存器中,等待列地址的传送然后选中所需要的数据。这也是为什么SRAM比DRAM快的原因之一。

多线程

什么是多线程

所谓多线程,就是系统可以同时运行多个任务,在操作系统中,每个任务就是一个线程。

Python 多线程可以成倍提高程序的运行速度。

进程是资源分配的最小单位,线程是程序执行的最小单位。

python3多线程

参考:Python3 多线程

异步

什么是异步

同步是指完成事务的逻辑,先执行第一个事务,如果阻塞了,会一直等待,直到这个事务完成,再执行第二个事务,顺序执行。

异步是和同步相对的,异步是指在处理调用这个事务的之后,不会等待这个事务的处理结果,直接处理第二个事务去了,通过状态、通知、回调来通知调用者处理结果。

python3异步

参考:asyncio — 异步 I/O

  1. (判断题)指针就是地址,因此一个变量的指针就是该变量的地址。
    答案:错误;解释:指针是个变量,指针的值是个地址,地址是个常量。

  2. char* s=”AAA”;
    s[0]=’B’; //错误,初始化指针时所创建的字符串常量被定义为只读,修改违法

  3. char ch;int i;float f;double d;
    表达式:ch/i+(f*d-i)的结果类型为double

  4. 1
    2
    3
    4
    5
    6
    7
    8
    int main()
    {
    int a = 5, b = 0;
    int c = MAX(++a, b);
    int d = MAX(++a, b + 10);
    printf("%d %d %d %d\n", a, b, c, d);
    return 0;
    }

    Output: 8 0 7 10
    解释:注意define,第一次调用MAX时,++a先执行了一次,此时a为6,由于满足宏定义中(a)>(b)的条件,所以执行a,这个a就对应++a,所以a又自增了一次,变为7,由此得出c为7。 第二个MAX时a又自增了一次,此时为8,由于不满足条件,所以执行的是宏定义中的b,没有执行++a,所以a最终为8

  5. 关于fseek() 参考:C库函数 - fseek()

  6. 转义字符分三种,一般转义字符,八进制转义字符和十六进制转义字符

    一般转义字符,如‘\b’,由两个字符表示,其实代表一个字符,这个代表退格字符

    八进制转义字符,如‘\007’,三位数字是八进制的,ASCII码为7的表示响铃

    十六进制转义字符,如’\xfe’,同样后面数字是所表示意思的Ascii码的十六进制表示,注意一定要有x,大小写都行

  7. 在C程序中逗号运算符的优先级最低,赋值运算符其次;j++是属于赋值语句;sizeof()属于一元运算符;

  8. C中&&(逻辑与)和&(按位与)

    • 按位与运用二进制进行计算,逻辑与比较符号两边的真假输出逻辑值。
    • 按位与对所有的表达式都要判断,逻辑与运算符第一个表达式不成立的话,后面的表达式不运算,直接返回。
    • 按位与&输出运算结果为不同的数值,逻辑与 && 输出逻辑值true或者 false。
  9. 两个指针变量不可以相加,因为指针变量相加没有意义。

  10. scanf函数不能指定输入精度,可以指定长度,比如%m.nf是不允许的,但是可以%mf(m为整数)。scanf("%7.2f",&a);不合法

  11. 多态类中的虚函数表建立在编译阶段。对类的编译,内存分布不太了解。

  12. const int* p是常量指针,p可以改变,*p不能改变int* const p = &a是指针常量,p不可以改变,*p能改变;记忆方法:const后面是p,p不能改变,const后面是*p,*p不能改变;还有一种const int* const p = &a,p和*p都不能改变。

  13. C++程序执行时,内存划分4个区域。不同区域存放的数据,赋予不同的生命周期,给我们更大的灵活编程。代码区和全局区是在程序运行前划分,堆栈是在程序运行后划分。

    • 代码区:存放函数体和二进制代码,由操作系统负责管理(共享、只读)
    • 全局区:存放全局变量和静态变量以及字符串,全局常量(程序结束后,由系统自动回收)
    • 栈区:由编译器自动分配释放,存放函数的参数值,局部变量,局部常量等
    • 堆区:由程序员分配释放,若程序员不释放,程序结束时,由系统自动回收
  14. C++用new关键字请求内存,用delete释放内存,参考:C++ 动态内存

  15. C没有引用,C++引用变量是一个别名,参考:C++引用;使用引用传参;使用引用作为函数返回值引用的本质是指针常量;

  16. 浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝析构时重复释放堆带来的问题

  17. 类成员中有其他类对象,构造时先构造成员中的类对象,再构造自身;析构时相反。

  18. 静态成员变量:所有对象共享同一份数据;在编译阶段分配内存;类内声明,类外初始化;静态成员函数:所有对象共享同一个函数;静态成员函数只能访问静态成员变量;

  19. 1
    2
    3
    4
    5
    ostream& operator<<(ostream& cout, Class &c)
    { // c++重载<<运算符
    cout << c.m_A; // 打印成员变量
    return cout; // 链式编程
    }

参考

各种电容特性

多层陶瓷电容

多层陶瓷电容(MLCC)不仅尺寸小,而且将低ESR、低ESL和宽工作温度范围特性融于一体,可以说是旁路电容的首选。不过,这类电容也并非完美无缺。根据电介质材料不同,电容值会随着温度、直流偏置和交流信号电压动态变化。另外,电介质材料的压电特性可将振动或机械冲击转换为交流噪声电压。大多数情况下,此类噪声往往以微伏计,但在极端情况下,机械力可以产生毫伏级噪声。

电压控制振荡器(VCO)、锁相环(PLL)、RF功率放大器(PA)和其它模拟电路都对供电轨上的噪声非常敏感。在VCO和PLL中,此类噪声表现为相位噪声;在RF PA中,表现为幅度调制;而在超声、CT扫描以及处理低电平模拟信号的其它应用中,则表现为显示伪像。尽管陶瓷电容存在上述缺陷,但由于尺寸小且成本低,因此几乎在每种电子器件中都会用到。不过,当调节器用在对噪声敏感的应用中时,设计人员必须仔细评估这些副作用。

固态钽电解电容

与陶瓷电容相比,固态钽电容对温度、偏置和振动效应的敏感度相对较低。新兴一种固态钽电容采用导电聚合物电解质,而非常见的二氧化锰电解质,其浪涌电流能力有所提高,而且无需电流限制电阻。此项技术的另一好处是ESR更低。固态钽电容的电容值可以相对于温度和偏置电压保持稳定,因此选择标准仅包括容差、工作温度范围内的降压情况以及最大ESR。

导电聚合物钽电容具有低ESR特性,成本高于陶瓷电容而且体积也略大,但对于不能忍受压电效应噪声的应用而言可能是唯一选择。不过,钽电容的漏电流要远远大于等值陶瓷电容,因此不适合一些低电流应用。

固态聚合物电解质技术的缺点是此类钽电容对无铅焊接过程中的高温更为敏感,因此制造商通常会规定电容在焊接时不得超过三个焊接周期。组装过程中若忽视此项要求,则可能导致长期稳定性问题。

铝电解电容

传统的铝电解电容往往体积较大、ESR和ESL较高、漏电流相对较高且使用寿命有限(以数千小时计)。而OS-CON电容则采用有机半导体电解质和铝箔阴极,以实现较低的ESR。这类电容虽然与固态聚合物钽电容相关,但实际上要比钽电容早10年或更久。由于不存在液态电解质逐渐变干的问题,OS-CON型电容的使用寿命要比传统的铝电解电容长。大多数电容的工作温度上限为105°C,但现在OS-CON型电容可以在最高125°C的温度范围内工作。

虽然OS-CON型电容的性能要优于传统的铝电解电容,但是与陶瓷电容或固态聚合物钽电容相比,往往体积更大且ESR更高。与固态聚合物钽电容一样,这类电容不受压电效应影响,因此适合低噪声应用。

固态电容

固态电容器的全名为固态铝质电解电容器,是目前电容器产品中最高阶的产品,固态电容的介电材料则为功能性导电高分子,能大幅提升产品的稳定度与安全性,它与液态铝质电解电容最大差别,在于所使用的介电材料,过去铝质电解电容所使用的介电材料是电解液,而固态电容则是导电性高分子材料,也因此导致成本相对较高。固态电容特点固态电容具备环保、低阻抗、高低温稳定、耐高纹波及高信赖度等优越特性,是目前电解电容产品中最高阶的产品。由于固态电容特性远优于液态铝电容,固态电容耐温达摄氏260度,且导电性、频率特性及寿命均佳,适用于低电压、高电流的应用。

LDO负载电容

影响LDO稳定工作的外在因素是负载电容容值CL和ESR,以及LDO的输出电流,他们的关系是:

  1. 容值小的CL,ESR往往较大,容值大的CL,ESR往往较小。无论哪种电容,都要保证ESR在合理的范围内,规格书往往会提供这个范围。

  2. 当LDO输出电流是额定电流的20%~50%时,容易发生不稳定状况。

  3. 低温时,CL的ESR会升高,并且容值也会发生变化。选型时要注意!

  4. 在ESR很低,LDO输出电流很小(≤1mA)时,容易发生不稳定状况。

  5. 同一温度下,LDO输出电流越小,保证LDO稳定性的ESR值越高。

  6. 同一输出电流下,温度越高,保证LDO稳定性的ESR越大。

  7. 在低输出电流时,LDO输出波形变成三角波,说明ESR过低;在中等输出电流时,如果LDO波形是正弦波,说明ESR过高。

电容技术 等效串联电阻 等效串联电感 电压稳定性 温度稳定性 振动敏感度 电容值/单位体积
铝电解电容 最高 最高 最低
固态钽电容 中等 中等 最佳
聚合物固态铝电容 最佳
多层陶瓷电容 最低 最低 中等

前言

不会吧,不会吧,我又参加飞卡了?

基础四轮组规则

  • 车模:B车模,车模宽度不超过25厘米,高度不超过20厘米,长度没有限制,如果安装摄像头传感器,摄像头镜片中心的高度距离地面不超过10厘米

  • MCU:指定使用 Infineon系列单片机,允许使用各类电磁、红外光电、摄像头、激光、超声传感器

  • 赛道元素:进出库、三岔路口(进出口120°,路口间直线距离<3m)、环岛(R<0.5m)、坡道(坡度<=20°)、十字路口(需直行)、弯道(曲率半径<0.5m)

电机驱动

IR2184老方案,B车电机比较猛,可能有11.7A,试一下加RDC释放电路来提高刹车性能

210517-飞卡-1.jpg

210517-飞卡-2.jpg

  • 电机驱动加上ina240测电机电流(21.6.11前)

210517-飞卡-7.jpg

210517-飞卡-8.jpg

(21.6.11)程序员通过电机反转来刹车,未加驱动死区时间,导致锂电池保护板保护。

电磁运放

采用逐飞方案,opa4377,单端放大,检波,二极管使用了压降小的肖特基二极管

210517-飞卡-3.jpg

210517-飞卡-4.jpg

tc377主控

考虑到DC-DC负载响应速率不如LDO,继续使用LM1084-ADJ给舵机供6.3v电
210517-飞卡-5.jpg
210517-飞卡-6.jpg

参考

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#include<iostream>
using namespace std;

template<class T>
class Stack
{
public:
Stack():_pData(new T[1]), _capacity(1), _size(0)
{
cout << "creat stack successful" << endl;
}
~Stack()
{
delete [] _pData;
cout << "delete stack successful" << endl;
}
void Push(const T& t)
{
CheckCapacity();
_pData[_size++] = t;
cout << "push " << t << " successful" << endl;
}
T& Pop()
{
T& t = _pData[_size-1];
_size--;
// cout << "pop return " << t << endl;
return t;
}
size_t Size()const
{
return _size;
}
size_t Capacity()const
{
return _capacity;
}

private:
void CheckCapacity()
{
size_t size = Size();
size_t capacity = Capacity();
size_t newcapacity = 2 * capacity;
if (size >= capacity)
{
_capacity = newcapacity;
T* tmp = new T[newcapacity];
for (size_t i = 0; i<size; i++)
{
tmp[i] = _pData[i];
}
delete[] _pData;
_pData = tmp;
cout << "capacity add to " << newcapacity << endl;
}
}

private:
T* _pData;
size_t _capacity;
size_t _size;
};

int main(void)
{
Stack<char> s;
char strInput[100]="test\0";
int i = 0;
cout << "input:";
if(!cin.getline(strInput,100))
cout << "error, input too long" << endl;
while(strInput[i] != '\0')
{
s.Push(strInput[i]);
i++;
}
i = 0;
cout << "output:";
while(strInput[i] != '\0')
{
cout << s.Pop();
i++;
}
cout << endl;
system("pause");
return 0;
}

运行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
creat stack successful
input:i love tzy
push i successful
capacity add to 2
push successful
capacity add to 4
push l successful
push o successful
capacity add to 8
push v successful
push e successful
push successful
push t successful
capacity add to 16
push z successful
push y successful
output:yzt evol i

  1. Sublime里运行程序不能输入数据,只能输出数据(卡在cin)
  2. 参考:动态顺序栈的简单实现 中,数组扩容未改变_capacity