Raspberry Pi、GPIO、Python、C/C++ 扩展与 wiringPi 的连接

我曾用 gpiozeroRPi.GPIO 来控制我的 3D 打印机。然而,速度相当慢。经过一番令人紧张的调试后,我发现是 Python 导致了电机运动缓慢。即使删除了 time.sleep() 那一行,它仍然运行得很慢。这让我别无选择,只能用 C 重写控制模块。重写之后,问题终于解决了。

在 C 代码中,我选择使用 wiringPi 库来控制 GPIO 引脚。

gpiozeroRPi.GPIOwiringPi 使用的 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

在编译或导入模块时,你可能会遇到一些问题。

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 文档:

https://docs.python.org/3/extending/extending.html#the-module-s-method-table-and-initialization-function

Leave a Reply