前言

前两个完结篇介绍了B码的结构,B码保护程序和B码的1PPS产生程序,下面介绍B码的UTC时间产生。当然B码中含有UTC时间和UTC时间的关键信息。程序的整体思路是差不多的,翻过来调过去也就是那点东西,就看怎么去一步一步的去解析里面的信息帧。解析过程和程序无关,和B码的结构有关。当然顺序执行的程序都是这个逻辑,只不过现在用顺序解析的办法去编写并行程序。

写在前面

从FPGA的双精度double数加和乘,整数转双精度double数,再到串口发送,再到SPI采集的主机和从机程序,再到现在的B码解析。感觉FPGA的编程模式已经差不多浮现出来,至于怎么精简程序和一个clk都不出现问题,这个倒没有太过在意。可能大神的程序就是一个clk都不出现错误吧。想着咱们用FPGA编程,一般的容错率还是挺强的,因为FPGA是一个周期一个周期的,而咱们需要的输出量是在很长一段时间内输出就行。所以只要最后结果能输出就好,管他几个clk呢。当然如果需要非常精确的场合,还是得一个clk一个clk的去看得到的值。

疑问:都说一个领域需要去沉淀,需要好好去做,小伙伴们说做到现在这个程度算是精通么?我始终都不太理解说的这个要有深度是需要深度到什么程度。你说做变频器,做到什么程序是精通呢?各个问题都能知道在哪,都知道怎么解决?现在编写STM32的程序,做到什么程度呢?里面的每个状态都能知道?每个寄存器都知道怎么用?如果只是这个程序,那每个都精通了。但是大神眼里的精通应该不是这个程度。所以这个概念非常模糊,也可能自己做的东西太杂,已经不知道精通是什么意思了,也挺无奈的!

第一章:输出介绍

先看UTC时间输出报文吧。

先说下报文格式:报头:5AA5,光电口标志:55,UTC时间:62 FD A9 BE,UTC其他关键信息:08 00,校验:22。

只看UTC时间和UTC关键信息,62 FD A9 BE转化为十进制为:1660791230,对应标准时间为:

通过转化可以看出转化的时间是正确的。

后面的0800,08代表东八区,也就是北京时间,之后的0代表UTC时间精确度,之后的0代表闰秒什么的,当然需要什么关键信息可以自己添加。不过UTC时间主要的关键信息也就这几个:时区,闰秒,精度,其他的解析出来也没太大作用,也可能是我自己不用吧。

关于闰秒问题:这个地方考虑了,但是很鸡肋。产品能用几年呢,等不到闰秒就淘汰了,还管啥闰秒啊,这个我就没有很好的解析!但是从严格的角度还是需要考虑的,看小伙伴的自己的需求。对于军工之类的,这个肯定得好好解析,对于其他的,还是算了,太麻烦!

第二章:流程介绍

前篇文章介绍了2个P标志位锁存,前面是一样的!

详细说下锁存之后做的事情。检测到两个连续P标志电平后,flag置1,并且将保存的cnt置成2。当flag等于1之后,下一次B码下降沿来的时候,cnt会++,也就是下一个B码单元存到第3个单元里面。所以设置100个寄存器,1和2是预留给2个P标志帧的,而真正的存放B码单元值是从3开始的。从3存到100,所以之后解析的时候也是按照这个顺序解析的,当然你也可以按照自己的方式去存储这些值,最后解析的时候对应就可以了。

放入寄存器的时候肯定是从3开始存放,当检测不到2个P帧的时候cnt是不++的,也不会存放到cnt对应的寄存器,这点非常重要。

每次检测到2个P标志之后才开始存储和计数,无论中间出现什么状况,总是从2个P帧标志开始,所以确保了程序1s内存放是正确的。

当flag置1之后,启动time计数器,对应时间到最后1个B码的时候,level置1,并且一定时间后置0。这个时间是固定的,level的下降沿falling时候,time计数器复位。这样计数器和flag就完成了闭环。

在下降沿之后,开始计算后面的数据。正好time计数器走到1s的最后1帧。开始和结尾都是可控的,所以故障不会出现在下1s。

此时如果丢失B码,根据上篇文章中的11ms下降沿,将flag复位,同时将time计数器复位。这样出现错误B码的时候也达到了闭环。

另一个就是cnt问题:如果B码都是正确的,开始是2,那么下次开始也肯定是2,都是从2个P帧标志开始的,相当于有一个复位。但是如果B码中间消失,那么下次开始时,就不一定是2了。所以cnt在B码出现丢失或者错误时也必须形成闭环。也就是11ms下降沿时,也需要cnt复位成2。这样cnt参数也形成了闭环。

当计数器的下降沿,也就是1sB码的最后一帧采集完毕后,开始对所有3到100的B码进行01转换,然后计算UTC时间,计算闰年天数,计算UTC秒数,计算校验,然后给串口发送。具体怎么计算下面程序会详细介绍。

以上就是UTC的解析过程,里面的各个参数都形成闭环,能把故障控制在1s之内。

第三章:程序介绍

下面详细介绍下程序的编写,这部分比较绕,有很多延迟下降沿,这部分也是没办法,顺序执行的程序,只能一个参数准备好,才能进行下一步计算。当然能不能一下子计算,我自己试验了下,也是没问题的,但是为了将来自己能读懂自己的程序,还是一步一步的去计算,并且能保证数据的准确性。

//B码接收
//接收到2个P标志位后,bcodevalidflag置1,根据时间,B码最后一位结束后置0
(*noprune*)reg    bcodevalidflag;
always@(posedge clk or negedge rst_n)
beginif (rst_n == 1'b0)bcodevalidflag <= 1'd0;else if (resettime1_falling_flag == 1'b1)begin if((bcodelevellatch1 == 2'd3)&&(bcodelevellatch2 == 2'd3))    bcodevalidflag <= 1'd1; endelse if (bcodevalidtime_falling_flag == 1'b1)begin bcodevalidflag <= 1'd0; endelse if (code10ms_time_falling_flag == 1'b1)begin bcodevalidflag <= 1'd0; end
end(*noprune*)reg [31:0]      bcodevalidtimer;
always@(posedge clk or negedge rst_n)
beginif (rst_n == 1'b0)bcodevalidtimer <= 32'd0;                     else if (bcodevalidflag == 1'b1)        bcodevalidtimer <= bcodevalidtimer + 32'd1;                                    else if (bcodevalidflag == 1'b0)        bcodevalidtimer <= 32'd0;   else if (code10ms_time_falling_flag == 1'b1)        bcodevalidtimer <= 32'd0;
end
reg        bcodevalidlevel;
always@(posedge clk or negedge rst_n)
beginif (rst_n == 1'b0)bcodevalidlevel <= 1'd0;                     else if (bcodevalidtimer == 32'd49025000)    // 991ms 置高,994ms置低  bcodevalidlevel <= 1'b1;                     else if (bcodevalidtimer == 32'd49075000)     bcodevalidlevel <= 1'b0;
endreg        bcodevalidtime_en_d0;
reg        bcodevalidtime_en_d1;
wire       bcodevalidtime_falling_flag;
wire       bcodevalidtime_rasing_flag;
assign bcodevalidtime_falling_flag = (~bcodevalidtime_en_d0) & bcodevalidtime_en_d1;
assign bcodevalidtime_rasing_flag  = (~bcodevalidtime_en_d1) & bcodevalidtime_en_d0;
always @(posedge clk or negedge rst_n) begin         if (!rst_n) beginbcodevalidtime_en_d0 <= 1'b0;                                  bcodevalidtime_en_d1 <= 1'b0; end                                                      else begin                                               bcodevalidtime_en_d0 <= bcodevalidlevel;                               bcodevalidtime_en_d1 <= bcodevalidtime_en_d0;              end
endreg        bcodebegin_en_d0;
reg        bcodebegin_en_d1;
wire       bcodebegin_falling_flag;
wire       bcodebegin_rasing_flag;
assign bcodebegin_falling_flag = (~bcodebegin_en_d0) & bcodebegin_en_d1;
assign bcodebegin_rasing_flag  = (~bcodebegin_en_d1) & bcodebegin_en_d0;
always @(posedge clk or negedge rst_n) begin         if (!rst_n) beginbcodebegin_en_d0 <= 1'b0;                                  bcodebegin_en_d1 <= 1'b0; end                                                      else begin                                               bcodebegin_en_d0 <= bcodevalidlevel;                               bcodebegin_en_d1 <= bcodebegin_en_d0;              end
end

2个P标志位之后,flag置1,加上B码错误判断。计数器到最后一帧停止复位。

//100个计数单元
(*noprune*)reg [6:0] bcodebitcnt;  /*synthesis noprune*/
(*noprune*)reg       bcodeflag;
always@(posedge clk or negedge rst_n)
beginif (rst_n == 1'b0)bcodebitcnt <= 7'd0;  else if ((bpluse_falling_flag == 1)&&(bcodevalidflag == 1'b1))bcodebitcnt <= bcodebitcnt + 1'b1; else if (bcodebegin_rasing_flag == 1)bcodebitcnt <= 7'd2;else if (code10ms_time_falling_flag == 1'b1)begin bcodebitcnt <= 7'd2; end      else bcodebitcnt <= bcodebitcnt;
end

cnt的单独拿出来介绍,flag等于1之后开始增长++,2个P标志后变成2,B码错误之后也置成2,形成cnt闭环。

(*noprune*)reg  [1:0]  b_data_rec1;
(*noprune*)reg  [1:0]  b_data_rec2;
(*noprune*)reg  [1:0]  b_data_rec3;
(*noprune*)reg  [1:0]  b_data_rec4;
......
(*noprune*)reg  [1:0]  b_data_rec100;
always@(posedge clk or negedge rst_n)
beginif (rst_n == 1'b0)beginb_data_rec1 <= 2'd0;b_data_rec2 <= 2'd0;b_data_rec3 <= 2'd0;......b_data_rec100 <= 2'd0; end            else if (resettime2_falling_flag == 1'b1)begin if(bcodebitcnt == 7'd3) b_data_rec3  <= bcodelevel;if(bcodebitcnt == 7'd4) b_data_rec4  <= bcodelevel;......if(bcodebitcnt == 7'd100)b_data_rec100 <= bcodelevel; end
end

根据cnt的值,将B码的值放入100个寄存器中间,中间部分省略了,需要的可以自己加上。当然这个下降沿在2个P标志后才会起作用。

always@(posedge clk or negedge rst_n)
beginif (rst_n == 1'b0)TimeOtherStatus <= 16'd0;else if (bcodebegin_falling_flag == 1'b1)begin if(b_data_rec72 >= 2'd1)TimeOtherStatus[3] <= 1'b0;if(b_data_rec72 >= 2'd2)TimeOtherStatus[3] <= 1'b1;            if(b_data_rec73 >= 2'd1)TimeOtherStatus[4] <= 1'b0;if(b_data_rec73 >= 2'd2)TimeOtherStatus[4] <= 1'b1;          if(b_data_rec74 >= 2'd1)TimeOtherStatus[5] <= 1'b0;if(b_data_rec74 >= 2'd2)TimeOtherStatus[5] <= 1'b1;          if(b_data_rec75 >= 2'd1)TimeOtherStatus[6] <= 1'b0;if(b_data_rec75 >= 2'd2)TimeOtherStatus[6] <= 1'b1;          if(b_data_rec76 >= 2'd1)TimeOtherStatus[7] <= 1'b0;if(b_data_rec76 >= 2'd2)TimeOtherStatus[7] <= 1'b1;if(b_data_rec67 >= 2'd1)TimeOtherStatus[8] <= 1'b0;if(b_data_rec67 >= 2'd2)TimeOtherStatus[8] <= 1'b1;       if(b_data_rec68 >= 2'd1)TimeOtherStatus[9] <= 1'b0;if(b_data_rec68 >= 2'd2)TimeOtherStatus[9] <= 1'b1;if(b_data_rec69 >= 2'd1)TimeOtherStatus[10] <= 1'b0;if(b_data_rec69 >= 2'd2)TimeOtherStatus[10] <= 1'b1;if(b_data_rec70 >= 2'd1)TimeOtherStatus[11] <= 1'b0;if(b_data_rec70 >= 2'd2)TimeOtherStatus[11] <= 1'b1;if(b_data_rec66 >= 2'd1)TimeOtherStatus[13] <= 1'b0;if(b_data_rec66 >= 2'd2)TimeOtherStatus[13] <= 1'b1;   if(b_data_rec63 >= 2'd1)TimeOtherStatus[14] <= 1'b0;if(b_data_rec63 >= 2'd2)TimeOtherStatus[14] <= 1'b1;    if(b_data_rec62 >= 2'd1)TimeOtherStatus[15] <= 1'b0;if(b_data_rec62 >= 2'd2)TimeOtherStatus[15] <= 1'b1;            end
end

根据每个位的大小,判断各个标志位的0和1,变为可以输出的01信号。

(*noprune*)reg  [6:0]  utc_sec;
(*noprune*)reg  [6:0]  utc_min;
(*noprune*)reg  [5:0]  utc_hou;
(*noprune*)reg  [9:0]  utc_day;
(*noprune*)reg  [7:0]  utc_yea;always@(posedge clk or negedge rst_n)
beginif (rst_n == 1'b0)beginutc_sec <= 7'd0;utc_min <= 7'd0;utc_hou <= 6'd0;utc_day <= 10'd0;utc_yea <= 8'd0;endelse if (bcodebegin_falling_flag == 1'b1)begin if(b_data_rec3 >= 2'd1)utc_sec[0] <= 1'b0;if(b_data_rec3 >= 2'd2)utc_sec[0] <= 1'b1;if(b_data_rec10 >= 2'd1)utc_sec[6] <= 1'b0;if(b_data_rec10 >= 2'd2)utc_sec[6] <= 1'b1;if(b_data_rec12 >= 2'd1)utc_min[0] <= 1'b0;if(b_data_rec12 >= 2'd2)utc_min[0] <= 1'b1;       if(b_data_rec19 >= 2'd1)utc_min[6] <= 1'b0;if(b_data_rec19 >= 2'd2)utc_min[6] <= 1'b1;             if(b_data_rec22 >= 2'd1)utc_hou[0] <= 1'b0;if(b_data_rec22 >= 2'd2)utc_hou[0] <= 1'b1;                  if(b_data_rec28 >= 2'd1)utc_hou[5] <= 1'b0;if(b_data_rec28 >= 2'd2)utc_hou[5] <= 1'b1; if(b_data_rec32 >= 2'd1)utc_day[0] <= 1'b0;if(b_data_rec32 >= 2'd2)utc_day[0] <= 1'b1;        if(b_data_rec43 >= 2'd1)utc_day[9] <= 1'b0;if(b_data_rec43 >= 2'd2)utc_day[9] <= 1'b1;if(b_data_rec52 >= 2'd1)utc_yea[0] <= 1'b0;if(b_data_rec52 >= 2'd2)utc_yea[0] <= 1'b1; if(b_data_rec60 >= 2'd1)utc_yea[7] <= 1'b0;if(b_data_rec60 >= 2'd2)utc_yea[7] <= 1'b1;end
end

这部分根据B码的值,判断utc的各个位的01大小,这部分没有什么可以介绍的了。主要是需要主要B码和寄存器对应,对应对了就可以得出每个数据大小。


(*noprune*)reg  [5:0] utc_SecondData;
(*noprune*)reg  [5:0] utc_MinuteData;
(*noprune*)reg  [4:0] utc_HourData;
(*noprune*)reg  [8:0] utc_DayData;
(*noprune*)reg  [5:0] utc_YearData;
always@(posedge clk or negedge rst_n)
beginif (rst_n == 1'b0)beginutc_SecondData <= 6'd0;utc_MinuteData <= 6'd0;utc_HourData   <= 5'd0;utc_DayData    <= 9'd0;utc_YearData   <= 6'd0;endelse if (resettime3_falling_flag == 1'b1)begin utc_SecondData <= utc_sec[0] + 2*utc_sec[1] + 4*utc_sec[2] + 8*utc_sec[3] +(utc_sec[4] + 2*utc_sec[5] + 4*utc_sec[6])*10; utc_MinuteData <= utc_min[0] + 2*utc_min[1] + 4*utc_min[2] + 8*utc_min[3] +(utc_min[4] + 2*utc_min[5] + 4*utc_min[6])*10; utc_HourData <= utc_hou[0] + 2*utc_hou[1] + 4*utc_hou[2] + 8*utc_hou[3] +(utc_hou[4] + 2*utc_hou[5])*10; utc_DayData   <= utc_day[0] + 2*utc_day[1] + 4*utc_day[2] + 8*utc_day[3] +(utc_day[4] + 2*utc_day[5] + 4*utc_day[6] + 8*utc_day[7])*10+(utc_day[8] + 2*utc_day[9])*100; utc_YearData  <= utc_yea[0] + 2*utc_yea[1] + 4*utc_yea[2] + 8*utc_yea[3] +(utc_yea[4] + 2*utc_yea[5] + 4*utc_yea[6] + 8*utc_yea[7])*10;                           end
end

根据每个位的大小计算具体的时间数据。这部分是B码的定义,怎么计算看看第一篇里面对应的数据就可以了。具体的自己也做了一张表。

具体的可以自己根据自己存放的寄存器去计算。

always@(posedge clk or negedge rst_n)
beginif (rst_n == 1'b0)TimeStamp <= 32'd0;else if (resettime5_falling_flag == 1'b1)begin TimeStamp <=  utc_LeapYear   * 31622400 + utc_Year       * 31536000 + utc_DayData    * 86400+ utc_HourData   * 3600 + utc_MinuteData * 60 + utc_SecondData- 115200;    end
end

然后计算utc时间。具体的也不会占用多少资源,这个计算应该挺方面,直接放进去就行。

为啥不直接传输各个量呢?计算出来utc的秒数,采用32位就可以传输了,单独每个传输的话,会需要很多,32位是绝对搞不定的,所以计算出来秒数是最好的。

always@(posedge clk or negedge rst_n)
beginif (rst_n == 1'b0)TimeStampVerify <= 16'd0;else if (resettime3_falling_flag == 1'b1)begin TimeStampVerify <= 16'd0;    end       else if (resettime6_falling_flag == 1'b1)begin TimeStampVerify <= TimeStampVerify + 8'h5A + 8'hA5 + 8'h55 + TimeStamp[ 7: 0] + TimeStamp[15: 8]+ TimeStamp[23:16] + TimeStamp[31:24]+ TimeOtherStatus[ 7: 0] + TimeOtherStatus[15: 8];end
end

然后进行校验,当然这里我采用和校验,你自己编写CRC校验也可以,主要是为了传输过程中担心传输错误。接收方只要去解析这个校验码就行了。当然里面也需要闭环,在前面的一个下降沿置0,后面计算。

always@(posedge clk or negedge rst_n)
beginif (rst_n == 1'b0)uartsendpluse <= 1'd0;else if (resettime6_falling_flag == 1'b1)begin uartsendpluse <= 1'd1;  end       else if (resettime7_falling_flag == 1'b1)begin uartsendpluse <= 1'd0;end
end

然后是串口发送使能,当然串口这部分可以自己编写,网上很多这样的例子,将全部的数据放进串口的数据区里,就可以得到文章第一章的数据了。

第四章:总结

B码的解析程序和以前的串口发送、SPI接收与发送程序感觉步骤是一样的,只要知道自己什么时候干什么事,哪一个时刻需要做什么就行,下面的就是绕来绕去的逻辑问题。整体来说B码的解析程序没有想象的那么麻烦。

程序最重要的是需要最初的架构做的好点,此时刻不能影响下一个时刻。避免错误时的错误传输,这个逻辑最重要的。

第五章:展望

B码解析到此已经全部完成。大概需要时间为15天。如果有相关代码的话,差不多一个星期可以完成全部程序。如果有小伙伴需要的请自己关注,至于那里有能关注的图片,请移步之前的文章,有的能显示有的不能显示,所以小伙伴自己去斟酌。关注后请私信,到时候会将编写好的代码放到百度网盘中。采用这种方式的原因前面的文章也有说,所以就不赘述了。如果有什么疑问也可以留言,如果能帮助小伙伴在B码解析和FPGA的编程方面,也请关注下。以后做什么项目或者那个软件也会写文章给大家分享!

FPGA解析B码----连载8(完结篇)相关推荐

  1. FPGA解析B码----连载7(完结篇)

    前言 上篇完结篇介绍了程序的整体架构和B码错误保护程序,这篇主要介绍下1PPS的产生. 写到这里想先聊聊现在的软件,用的是QII,不知道这个软件还能免费用多久.现在国外的软件慢慢的都不能用了,只能用国 ...

  2. 3D语音天气球(源码分享)——完结篇

    转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! 开篇废话: 由于这篇文章是本系列最后一篇,有必要进行简单的回顾和思路整理. 这个程序是由两 ...

  3. AL Brooks :《价格行为交易之反转篇》中文版图片连载(完结篇)

    注:本<价格行为交易>系列资料共有三篇: <价格行为交易之趋势篇> <价格行为交易之区间篇> <价格行为交易之反转篇> 本人已经连续将此三篇文章全部发完 ...

  4. 【实战+源码】RGB-D移动抓取服务机器人(四)——完结篇(ROS机器人、系统设计、运动规划、目标定位)

    毕业设计已经完成三个多月了,四月底答辩结束,上周办完离校手续!善始善终,最后一篇结束把这个题目告一段落! 完整代码github托管地址:https://github.com/pengxinyi-up/ ...

  5. FPGA系统性学习笔记连载_Day1数字电路基础篇

    FPGA系统性学习笔记连载_Day1数字电路基础篇 连载<叁芯智能FPGA设计与研发就业班-第一天> <数字电路基础1> 原创作者:紫枫术河 转载请联系群主授权,否则追究责任 ...

  6. FPGA系统性学习笔记连载_Day8【4位乘法器、4位除法器设计】 【原理及verilog实现、仿真】篇

    FPGA系统性学习笔记连载_Day8[4位乘法器.4位除法器设计] [原理及verilog实现.仿真]篇 连载<叁芯智能fpga设计与研发-第8天> [4位乘法器.4位除法器设计] [原理 ...

  7. FPGA系统性学习笔记连载_Day7【16位比较器设计】 【原理及verilog实现、仿真】篇

    FPGA系统性学习笔记连载_Day7[16位比较器设计] [原理及verilog实现.仿真]篇 连载<叁芯智能fpga设计与研发-第7天>[16位比较器设计] [原理及verilog实现. ...

  8. 连载 大学生求职七大昏招(九)说谎 完结篇

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 版权声明 ...

  9. 【JAVA秘籍心法篇-Spring】Spring XML解析源码详解

    [JAVA秘籍心法篇-Spring]Spring XML解析源码详解 所谓天下武功,无坚不摧,唯快不破.但有又太极拳法以快制慢,以柔克刚.武功外式有拳打脚踢,刀剑棍棒,又有内功易筋经九阳神功.所有外功 ...

最新文章

  1. 简单的家庭无线路由设置
  2. Sublime text 简单配置
  3. WPF 操作 richTextBox
  4. 《Python编程从入门到实践》记录之字典嵌套
  5. OpenShift 之 Quarkus(1)创建第一个Quarkus应用
  6. 如何在VB中使用正则表达式
  7. mysql 命令 字符集_MySQL的字符集操作命令总结
  8. Log4net使用指南[转]
  9. 智慧工厂用到的技术_CCF VC物联网应用技术专业工作组走进苏宁物流智慧仓储工厂参观学习...
  10. Mac自带的实用功能
  11. [php]php内存管理
  12. k3 cloud api java_调用K3Cloud webapi
  13. mac 外接键盘让 Home End 键生效
  14. 怎样卸载deepin系统_双系统卸载deepin的方法是什么_双系统完全卸载deepin的方法图文步骤...
  15. 运动会加油稿计算机学院150字,学校运动会加油稿150字10篇
  16. 为什么说人间值得,因为有这么多美好让我们留恋。金秋十月初九于指南山村 。...
  17. 【排行榜】苏州配眼镜推荐这四类口碑眼镜店
  18. Mac Xshell 下载 (FinallShell)
  19. r语言做断轴_手把手教你用R语言做回归后的残差分析
  20. 论团队协作的一个小故事

热门文章

  1. C语言 入门级游戏 ——猜数字游戏
  2. 记一个简单的项目(短信招投标流程)
  3. 激光位移传感器的原理及信号处理方式
  4. python+vue+elementui大学生网络教学平台
  5. 电磁场的概念及常见电磁场产生设备
  6. 单摆c语言编程,单摆时钟
  7. app小程序手机端Python爬虫实战09通过U2实现移动设备九宫格解锁
  8. 巴西柔术_了解巴西手机游戏市场的5个关键
  9. stm32基于TouchGFX的GUI开发(十):Touchgfx图片资源存储在外部Flash教程(SPI和QSPI方法二)
  10. Mac KICAD几个快捷键-移动全图