百度作为中国互联网的一颗毒瘤,实在是需要远离。可惜华为系统自带的输入法恰恰就是百度的输入法。
GCC支持在代码中对头文件是否存在的判断(__has_include)
我们在实际编写代码的时候,经常需要判断当前编译环境是否存在我们需要的头文件,如果不存在,则使用其他头文件代替。
以前这个操作都是通过外部的configure
文件生成Makefile
的时候指定。
最近的GCC
已经增加了__has_include
这个内置函数判断头文件是否存在。
这个功能最早是Clang
实现的,现在GCC
终于补上了这个功能。
例子如下:
1 2 3 4 5 6 7 8 9 10 11 12 |
#ifdef __has_include # if __has_include(<optional>) # include <optional> # define have_optional 1 # elif __has_include(<experimental/optional>) # include <experimental/optional> # define have_optional 1 # define experimental_optional # else # define have_optional 0 # endif #endif |
Code Composer Studio 8.2.0.00007
,GCC 5
支持这个内置函数。
参考链接
解决Ti CC1310上I2C驱动能力不足(稳定性)问题
在使用Ti CC1310
的硬件I2C
与外部设备进行通信的时候,无法长时间稳定运行,尤其是平时的测试开发板,使用杜邦线连接的情况下。
同等情况下的SPI
总线,一般不会发生此类问题,因此怀疑是GPIO
管脚的驱动能力不足导致的。
我的工程是通过简单修改Ti CC1310 SDK
自带的Code Composer Studio
的例子工程而创建的,因此存在已经进行过默认配置的CC1310_LAUNCHXL.c
,CC1310_LAUNCHXL.h
这两个文件。用Code Composer Studio
完全重新创建的工程,缺乏必要的默认配置。因此,我们还是建议根据Ti CC1310 SDK
自带的例子进行修改,而不是完全从头创建工程。
我们研究I2C
初始化GPIO
管脚的代码在simplelink_cc13x0_sdk_2_30_00_20/source/ti/drivers/i2c/I2CCC26XX.c
,相关的代码如下:
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 |
/* * ======== I2CCC26XX_initIO ======== * This functions initializes the I2C IOs. * * @pre Function assumes that the I2C handle is pointing to a hardware * module which has already been opened. */ static int I2CCC26XX_initIO(I2C_Handle handle, void *pinCfg) { I2CCC26XX_Object *object; I2CCC26XX_HWAttrsV1 const *hwAttrs; I2CCC26XX_I2CPinCfg i2cPins; PIN_Config i2cPinTable[3]; uint32_t i=0; /* Get the pointer to the object and hwAttrs */ object = handle->object; hwAttrs = handle->hwAttrs; /* If the pinCfg pointer is NULL, use hwAttrs pins */ if (pinCfg == NULL) { i2cPins.pinSDA = hwAttrs->sdaPin; i2cPins.pinSCL = hwAttrs->sclPin; } else { i2cPins.pinSDA = ((I2CCC26XX_I2CPinCfg *)pinCfg)->pinSDA; i2cPins.pinSCL = ((I2CCC26XX_I2CPinCfg *)pinCfg)->pinSCL; } /* Handle error */ if(i2cPins.pinSDA == PIN_UNASSIGNED || i2cPins.pinSCL == PIN_UNASSIGNED) { return I2C_STATUS_ERROR; } /* Configure I2C pins SDA and SCL*/ i2cPinTable[i++] = i2cPins.pinSDA | PIN_INPUT_EN | PIN_PULLUP | PIN_OPENDRAIN; i2cPinTable[i++] = i2cPins.pinSCL | PIN_INPUT_EN | PIN_PULLUP | PIN_OPENDRAIN; i2cPinTable[i++] = PIN_TERMINATE; /* Allocate pins*/ object->hPin = PIN_open(&object->pinState, i2cPinTable); if (!object->hPin) { return I2C_STATUS_ERROR; } /* Set IO muxing for the UART pins */ PINCC26XX_setMux(object->hPin, i2cPins.pinSDA, IOC_PORT_MCU_I2C_MSSDA); PINCC26XX_setMux(object->hPin, i2cPins.pinSCL, IOC_PORT_MCU_I2C_MSSCL); return I2C_STATUS_SUCCESS; } |
从上面的代码中,我们发现GPIO
管脚的驱动能力没有设置,根据CC13x0, CC26x0 SimpleLink ™ Wireless MCU Technical Reference Manual中的介绍,我们发现,如果不设置,默认情况下是PIN_DRVSTR_MIN(< (*) Lowest drive strength)
。这个驱动模式下,最省电,但是当有干扰存在的情况下,最容易导致出现错误。
查找Ti CC1310
提供的I2C
驱动程序,没有找到可以设置管脚驱动能力的接口。
网上有人通过直接修改Ti CC1310
提供的I2C
驱动程序,也就是simplelink_cc13x0_sdk_2_30_00_20/source/ti/drivers/i2c/I2CCC26XX.c
里的初始化代码(见上面的代码段)增加驱动能力属性来实现这个功能。
但是这种修改方式不推荐,修改的侵入太强。
从GPIO
的操作函数中,我们找到如下函数:
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 |
/** @brief Returns pin configuration * * @param pinId Pin ID * @return Current pin configuration as a device-independent #PIN_Config value * @note The pin ID is embedded in return value. * @note There is usually a device-specific version of this function that * returns device-specific options * @par Usage * @code * // Get config of pin 14 to be able to revert later * myPinConfig = PIN_getConfig(PIN_ID(14)); * // ... * // Lots of pin reconfigurations * // ... * // Restore previous configuration * PIN_setConfig(hPins, PIN_BM_ALL, myPinConfig); * @endcode */ extern PIN_Config PIN_getConfig(PIN_Id pinId); /** @brief Sets complete pin configuration * * @param handle Handle provided by previous call to PIN_open() * @param updateMask Bitmask specifying which fields in cfg that should take * effect, the rest keep their current value. * @param pinCfg #PIN_Config entry with pin ID and pin configuration * @return #PIN_SUCCESS if successful, else error code * @par Usage * @code * // Set drive strength on pin 15 * PIN_setConfig(hPins, PIN_BM_DRVSTR, PIN_ID(15)|PIN_DRVSTR_MAX); * @endcode */ extern PIN_Status PIN_setConfig(PIN_Handle handle, PIN_Config updateMask, PIN_Config pinCfg); |
我们尝试从上层找到I2C
驱动使用的GPIO
管脚,然后调整其驱动能力。
1 2 3 |
/* I2C */ #define CC1310_LAUNCHXL_I2C0_SCL0 (IOID_5) #define CC1310_LAUNCHXL_I2C0_SDA0 (IOID_6) |
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 |
/* * =============================== I2C =============================== */ #include <ti/drivers/I2C.h> #include <ti/drivers/i2c/I2CCC26XX.h> I2CCC26XX_Object i2cCC26xxObjects[CC1310_LAUNCHXL_I2CCOUNT]; const I2CCC26XX_HWAttrsV1 i2cCC26xxHWAttrs[CC1310_LAUNCHXL_I2CCOUNT] = { { .baseAddr = I2C0_BASE, .powerMngrId = PowerCC26XX_PERIPH_I2C0, .intNum = INT_I2C_IRQ, .intPriority = ~0, .swiPriority = 0, .sdaPin = CC1310_LAUNCHXL_I2C0_SDA0, .sclPin = CC1310_LAUNCHXL_I2C0_SCL0, } }; const I2C_Config I2C_config[CC1310_LAUNCHXL_I2CCOUNT] = { { .fxnTablePtr = &I2CCC26XX_fxnTable, .object = &i2cCC26xxObjects[CC1310_LAUNCHXL_I2C0], .hwAttrs = &i2cCC26xxHWAttrs[CC1310_LAUNCHXL_I2C0] } }; const uint_least8_t I2C_count = CC1310_LAUNCHXL_I2CCOUNT; |
从上面我们看到I2C
句柄的.object
指针指向了I2CCC26XX_Object
类型的结构体,这个结构体在simplelink_cc13x0_sdk_2_30_00_20/source/ti/drivers/i2c/I2CCC26XX.c
里的static int I2CCC26XX_initIO(I2C_Handle handle, void *pinCfg)
函数中被初始化,这个结构体记录了详细的GPIO
操作句柄。
因此我们可以使用如下的代码对GPIO
管脚的驱动能力进行调整:
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 |
#include <ti/drivers/I2C.h> #include <ti/drivers/i2c/I2CCC26XX.h> #include "CC1310_LAUNCHXL.h" static I2C_Handle initI2c(void) { I2C_Params i2cParams; /* Create I2C for usage */ I2C_Params_init(&i2cParams); i2cParams.bitRate = I2C_400kHz; i2cParams.transferMode = I2C_MODE_BLOCKING; I2C_Handle i2c = I2C_open(Board_I2C_TMP, &i2cParams); if (NULL == i2c) { return NULL; } /* Change I2C Pin Drive Strength*/ /*CC1310_LAUNCHXL.c I2CCC26XX_Object i2cCC26xxObjects[CC1310_LAUNCHXL_I2CCOUNT];*/ I2CCC26XX_Object* i2cObj = i2c->object; if (NULL != i2cObj) { PIN_Config pinCfg = PIN_getConfig(PIN_ID(CC1310_LAUNCHXL_I2C0_SDA0)); PIN_Status status = PIN_setConfig(i2cObj->hPin, PIN_BM_ALL, pinCfg | PIN_DRVSTR_MAX); if (PIN_SUCCESS != status){ I2C_close(i2c); return NULL; } pinCfg = PIN_getConfig(PIN_ID(CC1310_LAUNCHXL_I2C0_SCL0)); status = PIN_setConfig(i2cObj->hPin, PIN_BM_ALL, pinCfg | PIN_DRVSTR_MAX); if (PIN_SUCCESS != status){ I2C_close(handle->i2c); return NULL; } } return i2c; } |
参考链接
macOS Mojave(10.14)安装AX88179(USB 3.0 有线网卡)驱动程序
自己的mac book pro
在升级到macOS Mojave(10.14)
之后,无法识别USB 3.0
有线网卡。
使用如下命令,查询网卡的设备类型:
1 |
$ system_profiler SPUSBDataType |
然后下载驱动安装才可以。
官方下载地址:
AX88179 Drivers Download
sql-按条件统计非重复值,count(distinct case when)使用
背景
项目中,遇到一个统计需求,从某张表中按照条件分别统计。刚开始想到用union all的写法,太臃肿,后来使用count(distinct case when)解决此问题
count
数据统计中,count出现最频繁
最简单的用法
select count(*) from table where ....
select count(distinct xx) from table where ...
但最简单的用法也有其深刻的地方,比如这里其实有3种写法,count(1)、count(*)、count(字段),它们有什么区别呢?
- count(1) 和 count(*)
count(1)和count(*)差别不大,使用count(*)时,sql会帮你自动优化,指定到最快的字段。所以推荐使用count(*)
- count(*) 和 count(字段)
count(*)会统计所有行数,count(字段)不会统计null值
count(case when)
条件统计,即对某个table分条件统计,比如表test_db,有一个字段user_id(可能重复), gender(man、women),需要统计man和women的人数
可以使用where分别统计
select count(distinct user_id) as man_cnt from test_db where gender = 'man'
select count(distinct user_id) as women_cnt from test_db where gender = 'women'
也可以使用按条件统计
select count(distinct case gender = 'man' then user_id end) as man_cnt
, count(distinct case gender = 'women' then user_id end) as woman_cnt
from test_db
Raspberry Pi Zero W解决调试信息不足的问题(以libzmq-dev为例)
最近在参照 树莓派实时系统下脚本语言的选择(应当使用Lua而不是Python) 调用调试lua-zmq
的时候,发现使用PAIR
模式进行线程之间通信,长时间运行后会出现死锁的情况。
当我们需要跟踪问题的时候,使用apt-get
安装的版本缺乏必要的调试信息。
我们可以手工安装调试信息包,如下:
1 2 3 4 5 6 7 8 9 10 |
$ sudo apt install libzmq-dbg # 系统默认的lua 5.1的符号信息使用如下命令安装 $ sudo apt-get install liblua5.1-0-dbg # libreadline提供了多个版本(5,6,7),根据需要下载对应的版本 $ sudo apt-get install libreadline6-dbg # sudo apt-get install search libtinfo* 根据返回的结果安装 $ sudo apt-get install libtinfo5-dbg |
我们也可以从源代码重新编译一份,如下:
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 |
# 打开源代码的源 $ sudo sed -i "s/^#deb-src/deb-src/g" /etc/apt/sources.list # 安装必要的编译工具 $ sudo apt-get install build-essential devscripts lintian $ cd ~ # 下载源代码,此处不可sudo,否则后续操作会出现权限问题 $ apt-get source libzmq-dev $ cd zeromq*/ # 安装依赖项 $ sudo apt-get install libpgm-dev $ sudo apt-get install uuid-dev $ debuild -us -uc -b $ cd .. $ sudo dpkg -i libzmq-dev*.deb $ sudo dpkg -i libzmq-dbg*.deb $ sudo ldconfig |
以上是针对
Debian
安装包来进行的处理,然而对于luarocks
安装的插件来说,默认luarocks
是不能编译调试版本的,此时就需要我们手工编译了。我们以lua-zmq
为例子,参考如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
$ sudo apt-get -y install cmake $ git clone git://github.com/Neopallium/lua-zmq.git $ git checkout v1.1 $ cd lua-zmq ; mkdir build ; cd build $ cmake -DCMAKE_BUILD_TYPE=Debug .. $ make $ sudo make install $ sudo ldconfig |
sql中!=与的区别
背景
sql中表示不等于关系时,有两种写法!=、<>,抱着刨根问底的精神,看看这两个符号到底有什么不同
标准答案
https://stackoverflow.com/questions/18015422/what-is-difference-between-and-in-sql-server
搬运过来
这两个符号没有任何区别,你使用哪个都一样。
Microsoft的文档中,这两个符号的意义相同,都是 Not Equal To
https://docs.microsoft.com/en-us/sql/t-sql/language-elements/not-equal-to-transact-sql-exclamation?view=sql-server-2017
但,<>是ANSI 99 SQL标准中的定义,!=不是。所以,不是所有的DB引擎都支持!=。
最终推荐使用<>。
STM32开发环境
OpenSTM32开发环境
mysql-使用联合索引
背景
项目使用mysql数据库,某张表数据量2000W左右,比较大,在组合查询时发生超时,需要优化。
优化
查询优化常用方法就是建立索引
查询语句样例:查询在20181001-20181010时间范围内A=a且B=b的数据
select * from table where A = 'a' and B = 'b' and Date > '20181001' and Date < '20181010'
按照查询建立了一个组合索引 idx_query(A, B, Date),查询仍然报超时。然后开始研究索引原理,最后更改索引中列的顺序为idx_query(B,A,Date)解决。
---------------------------------------------------------------------------------------------
上面只是简单的项目背景,下面从原理细细解读索引
索引原理
首先,出问题以后再建立索引,是一种不好的习惯。排查、定位、解决问题会耗费很多开发时间和精力。
当然,不充分思考就给大多数列建立单列索引,给所有查询建立联合索引,是一种更坏的习惯。毕竟删除索引风险远高于添加索引。
使用索引需要成本:
- 空间成本:索引是占空间的,大表的联合索引占用空间不可忽略
- 时间成本:使用不对的索引会浪费时间
- 更新成本:增删改操作只要跟索引有关系,就需要更新索引
索引适合在返回大表中很小一部分数据时使用,很小当然越小越好,5%以下还可以,1%以下更好,如果表的数据量非常大,千万级别,返回行数要小到0.1%以下
从很大表中用联合条件查询出很小的数据,等价于count(distinct 联合条件) 的值很大,这个值被称为Cardinality,索引适合度=Cardinality/表记录数。
索引误区
多个单列索引(如idx_A, idx_B)不等同于联合索引(idx_A_B)
两个联合索引里的列顺序不同,这两个联合索引也不等价,适用场景不同
联合索引idx_A_B能够覆盖的场景
- A = ? and B = ?
- A = ? and B in (?, ?, ?)
- A = ?
- A in (?,?,?)
覆盖场景可以根据索引存储结构(B+Tree)推导出。
上面罗列的几种覆盖场景,效率由上至下越来越低,因为扫描行数越来越大
联合条件中,最左侧的索引列很重要,最好使用Cardinality最大的列。
总结
项目中遇到的问题,最终根据索引原理中 最左侧索引列使用Cardinality最大列 解决。
解决Raspberry PI Zero W中Lua使用lua-periphery与Python中设置的GPIO端口不一致的问题
在树莓派实时系统下脚本语言的选择(应当使用Lua而不是Python)中,我们没有使用rpi-gpio,而是使用了lua-periphery来解决Lua
语言下操作树莓派GPIO
的问题。
当时选择lua-periphery的原因在于rpi-gpio在Raspberry PI Zero W
中使用的时候会崩溃。这个原因是在于cpuinfo.c这个文件中缺少对于BCM2835
这颗新的CPU
的判断,只判断了BCM2708
(估计写这个库的时候,只有BCM2708
)。导致RPi_GPIO_Lua_module.c在初始化GPIO
的时候抛出了异常。这个已经有人提交了代码合并请求,估计很快会修复。
但是在lua-periphery中,没有对于GPIO
进行重新映射,导致跟rpi-gpio以及树莓派自带的Python
库在设置GPIO
的时候,端口号对应不一致。比如,在Python
中设置GPIO 22
,执行命令观察ls /sys/class/gpio/
,会发现系统创建的是GPIO 25
这个对应关系就是通过查表获取的。如下图:
继续阅读解决Raspberry PI Zero W中Lua使用lua-periphery与Python中设置的GPIO端口不一致的问题