我曾用 gpiozero 和 RPi.GPIO 来控制我的 3D 打印机。然而,速度相当慢。经过一番令人紧张的调试后,我发现是 Python 导致了电机运动缓慢。即使删除了 time.sleep() 那一行,它仍然运行得很慢。这让我别无选择,只能用 C 重写控制模块。重写之后,问题终于解决了。
在 C 代码中,我选择使用 wiringPi 库来控制 GPIO 引脚。
gpiozero、RPi.GPIO 和 wiringPi 使用的 GPIO 编号体系差异很大。可以用这个命令获取你的 Raspberry Pi 的引脚编号映射:
gpio readall
然后将 C 代码封装成 Python 扩展模块。
创建一个名为 test.c 的文件,并在其中定义 Python 模块函数。关键点是,导出的初始化函数必须与 setup.py 中使用的模块名匹配。
创建一个名为 setup.py 的文件来构建扩展。由于该扩展需要链接 wiringPi,要在扩展配置中指定它:
from distutils.core import setup, Extension
module = Extension(
'keywdarg',
sources=['test.c'],
library_dirs=[''],
libraries=['wiringPi'],
)
setup(
name='keywdarg',
version='1.0',
description='Python C extension using wiringPi',
ext_modules=[module],
)
代码完成后,构建并安装它。我建议安装在虚拟环境中。
sudo apt install python-virtualenv
python setup.py build
python setup.py install
在编译或导入模块时,你可能会遇到一些问题。
Table of Contents
Python.h: No such file or directory
这是一个编译错误。对我来说,重新安装 python3-dev 后解决了。
sudo apt install python3-dev
undefined symbol: digitalWrite
因为这个扩展使用了 wiringPi 库,所以必须在 setup.py 中指定它。否则,Python 可能能导入已编译的模块,但在尝试解析 digitalWrite() 时失败。
Extension(
...,
library_dirs=[''],
libraries=['wiringPi'],
)
ImportError: dynamic module does not define init function
我把初始化函数的名字写错了。对于 Python 3,PyInit_keywdarg 的后缀必须与 keywdargmodule 中定义的模块名相同。
static struct PyModuleDef keywdargmodule = {
PyModuleDef_HEAD_INIT,
"keywdarg",
NULL,
-1,
keywdarg_methods
};
PyMODINIT_FUNC
PyInit_keywdarg(void)
{
return PyModule_Create(&keywdargmodule);
}
SystemError: Bad call flags in PyCFunction_Call. METH_OLDARGS is no longer supported!
在 PyMethodDef 数组中,方法标志应定义为:
METH_VARARGS | METH_KEYWORDS
例如:
static PyMethodDef keywdarg_methods[] = {
/* The cast of the function is necessary since PyCFunction values
* only take two PyObject* parameters, and keywdarg_parrot() takes
* three.
*/
{"parrot", (PyCFunction)keywdarg_parrot, METH_VARARGS | METH_KEYWORDS,
"Print a lovely skit to standard output."},
{NULL, NULL, 0, NULL} /* sentinel */
};
更多细节可以参考 Python 文档:
