2021年1月

参考

GDB调试工具
GDB程序启动和断点设置
GDB观察和捕捉断点
GDB条件断点
GDB单步调试程序
GDB断点调试详解
GDB查看变量值 print和display
GDB禁用删除断点
GDB调试多线程程序
GDB non-stop模式
GDB后台调试命令
GDB调试多进程程序
GDB反向调试
GDB信号处理
GDB查看栈信息
GDB编辑、搜索源码以及在线帮助

准备

首先, gdb 调试的必须是gcc -g编译出来的.-g是将必要的符号和源码编入了.

gcc -g xxxxx.c xxxx.c

对生成的a.exe编译

进入

运行

gdb a.exe

进入gdb命令行. 常见命令如下, 大多数命令都支持首字母简写:
|名称|意义|
--|--
run|运行
list|列出源码
break 函数名/文件名:行号 [if varname=value] | 插入断点
info break|显示断点
delete n |删除第n个断点
disable n | 禁用第n个断点
enable n| 启用第n个断点
print varname|显示变量
set var varname=value |设置变量
next|执行到下一句
next n|执行到下n句
step|执行进入
return [value]|函数强制返回
finish |执行到当前函数返回处
until linenum|执行到行号
jump linenum|跳转到行号

好久没有写过C, 下面总结下一些坑, 填坑的过程不仅感慨js/python大法好啊!

enum与typedef enum

enum 定义类型后, 再定义变量前面还要把enum加上, 否则会报错

enum TTT{A, B, C};
enum TTT ttt; //不能写作 TTT ttt;

但这个很烦人不是么, typedef的作用就来了:

typedef enum _TTT{A, B, C}TTT;
TTT ttt; //再没有讨厌的enum了

enum定义的变量至少占1个int字节

注意上面enum定义的变量至少占1个int字节,最多占1个longlong字节, 具体是编译器根据enum中的最大值确定的. 在某些RAM捉襟见肘的MCU上(比如51上只有128字节), 1int = 2byte, 真的是大大的浪费. 这时候还不如用#define大法...

#define A 0
#define B 1
#define C 2

全局变量在哪儿定义?在哪儿声明?

首先定义的方式是: int g = 0;(必须初始化)
声明的方式是加extern, 如: extern int g;
全局变量必须且只能在.c中定义(且初始化), 必须且只能在.h中声明. 当然如果其他.c文件不需要使用这个全局变量的话, 就不需要在.h中extern它.
原因1: 全面变量在.h中定义的话, .h文件的相互引用一定会造成编译器报错重复定义. 而且似乎用#ifndef大法也没有用;
原因2: 全局变量在.c中声明的话, 如果不同的.c文件都要使用的时候, 声明很可能重复且不一致,造成编译器报警.

#define, enum, typedef, 函数在哪儿声明?

我感觉, 如果是要让其他C程序使用的, 就在.h中声明, 否则, 就在本.c文件前面声明, 虽然这样c文件前面会很啰嗦...
这样在.c中声明的部分就像是private的, 在.h中声明的部分就像是public的.

CuTest官网
下面讲的是基于Windows平台的编译工具cl.exe(Visual Studio附带)的测试方式.

准备

首先建立一个test文件夹, 将CuTest.hCuTest.c丢进去;

创建入口,加入测试集

  1. 在这个目录下建立AllTests.c文件, 作为测试的入口文件, 并且将所有测试集(TestSuite)加入到测试中, 如下:
    
    #include <stdio.h>
    #include "CuTest.h"

CuSuite *StrUtilGetSuite(); //声明测试集, 还可以声明多个

void RunAllTests(void)
{
CuString output = CuStringNew();
CuSuite
suite = CuSuiteNew();

CuSuiteAddSuite(suite, StrUtilGetSuite()); //加入测试集

CuSuiteRun(suite);
CuSuiteSummary(suite, output);
CuSuiteDetails(suite, output);
printf("%s\n", output->buffer);

}

int main(void)
{
RunAllTests();
}

一般就是上面有注释的两句需要修改或者添加.
## 创建单元测试文件和测试集
假如被测试的函数是`getKey()`, 在`maintask.c`这个文件中, 并且`maintask.c`有一个对应头文件`maintask.h`
创建这个文件的测试文件`testMaintask.c`,如下:
```c
#include "maintask.h"
#include "CuTest.h"

void testGetKey(CuTest *tc) //创建单个测试
{
    //                   测试信息, 测试条件
    CuAssert(tc, "Test getKey()", getKey()); 
}

CuSuite *testSuite1() //创建测试集
{
    CuSuite *suite = CuSuiteNew();
    SUITE_ADD_TEST(suite, testGetKey); //加入单个测试
    return suite;
}

加入stub头文件

比如在maintask.c中, 除了自身对应的头文件外, 还有其他头文件,如wm_include.h, 但这些头文件涉及的整个系统, 还有很多其他函数, 我不想加入, 而是想把其中有的函数打桩掉. 那么, 在test目录下新建这个头文件wm_include.h,作为打桩文件, 里面自行加入要打桩的函数即可.

编译和运行测试(基于cl.exe)

如果test目录是和maintask.c待测文件同一级, 其头文件maintask.h在这一级, 而wm_include.htest中, 那么需要有cl/I参数将这两个目录包含进来. 如/I.\ /I..\,注意/I带的目录后面是没有空格的.

cl /I..\ /I.\ ..\maintask.c Alltests.c CuTest.c testMaintask.c

将会输出:

用于 x64 的 Microsoft (R) C/C++ 优化编译器 19.16.27032.1 版
版权所有(C) Microsoft Corporation。保留所有权利。

maintask.c
Alltests.c
CuTest.c
testMaintask.c
正在生成代码...
Microsoft (R) Incremental Linker Version 14.16.27032.1
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:maintask.exe
maintask.obj
Alltests.obj
CuTest.obj
testMaintask.obj

运行maintask.exe即可看到测试结果.

编译和运行测试(基于gcc.exe)

如果test目录是和maintask.c待测文件同一级, 其头文件maintask.h在这一级, 而wm_include.htest中, 那么需要有gcc-I参数将这两个目录包含进来. 如-I..`,注意-I带的目录后面是没有空格的. 要包含当前目录, 不知为何-I.\不能使用, 必须要先退上一级再下来一级才可以, 如-I..\test\
另外, 加上-g可以方便gdb进行调试.

gcc -I..\  -I..\test\ ..\maintask.c Alltests.c CuTest.c testMaintask.c

如果gcc编译没有任何问题,那么它什么都不会输出, 然后产生一个a.exe的文件.
不过gcc的代码检查要比cl强度高很多.
运行a.exe即可看到测试结果.

这个系列真的很好。i2c看起来很简单但其实好多坑。
第一罪,从机ack拉低sda,主机未完成i2c流程就软复位,导致无法使用i2c。解决:主机scl发9个时钟。
第二罪芯片需要POR电路来进行复位,如果因为别的原因(风扇12v电压通过pwm控制脚倒灌进主控ic,进而倒灌进3.3v的vcc)提前上电,导致POR工作不正常,那么就会有问题。
第三罪scl上升沿上升太慢导致高电平无法检测。太慢源于板子总下拉电容大,可以通过改scl上拉电阻阻值提高上升速度。
第四罪i2c的3.3v转5v设计
第五罪
第六罪
第七罪

坑1

用PIN模拟做时序是万万不能的, 在w601上执行一个pin的0/1转换就需要50us, 做一个utime.sleep_us(1)也需要50us,在us级的时序是无法做的,差不多要到ms级的时序才有可能. 这在很多芯片需要自定义时序的时候就没法子了.
网上说可以打包C库供MicroPython调用.

mpip.install()

支持mpip.install进行额外的库安装,不过就是不知道在哪儿搜索可用的库.

MicroPython语言参考

坑1

除了按官方指导外, 似乎mpy-cross目录下需要单独make一次, 否则会报错找不到mpy-cross.
参考: http://bbs.eeworld.com.cn/thread-579242-1-1.html

坑2

必须使用说明书中推荐的arm-none-eabi-gcc 4.9版本, 而不要用最新的版本(目前是10.x版了).用新版本编译出来的固件会增大不少,导致1M flash里面放不下.
如果已经用高版本arm-none-eabi-gcc编译过, 需要make clean一次后再次make

含电池设备与插电设备区别:

  1. 含电池设备可能处于长期通电状态而不便于断电, 插电设备可以随时断电;
  2. 含电池设备供电电压在一个区间变化, 插电设备供电电压稳定;

设计思考:

  1. 设备长期运行导致的不稳定和bug, 插电设备可以轻易通过断电重启来解决, 而含电池设备必须设计reset接口;
  2. 插电设备可以支持断电升级, 而含电池设备不能支持断电升级, 所以一定要支持有线有电升级;
  3. 含电池设备不同IC输入电压不同, 可能导致有的能工作有的不能正常工作;
  4. 含电池设备在运行时,且超声过程中, 可能被超声震坏, 应休眠后超声.

现在的在线表单一般只解决了一个数据格式化提交和汇总到表格的问题。
OA和流程只解决了一个审批问题。
没有解决的又需要解决的重要问题是什么呢?
1.表格和在线文档解决了整体文档协作问题,又没有解决限制部分内容的协作问题。
2.另外没有解决的就是一个查询问题。
3.另一个解决就是汇总问题,类似于数据透视表。

crontab -e来编辑定时任务
命令格式为min hour day month weekday cmd
min/hout/month/weekday的取值是:

* 取值范围内的所有数字
/ 每过多少个数字
- 从X到Z
,散列数字