在mm32f3270上为micropython创建SPI模块

2024-02-28 13:32

本文主要是介绍在mm32f3270上为micropython创建SPI模块,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在mm32f3270上为micropython创建SPI模块

文章目录

  • 在mm32f3270上为micropython创建SPI模块
    • Introduction
    • Architecture
    • extmod/machine_spi.c
      • 软件SoftSPI
      • 硬件SPI
    • ports/xx/machine_spi.c

Introduction

在machine中实现SPI,我看各家的代码都引用了extmod/machine_spi.c中的模型,看来我也跑不掉,那就不要自创武功,随大流就好。

大体看了一下代码,预计工作量会在三个方面:

  • 了解extmod/machine_spi.c中对SPI的定义(SPI和SoftSPI)
  • 搞清楚需要port/mm32/machine_spi.c中实现的部分,做好适配
  • 处理好两个machine_spi.c,以及同平台的依赖关系,似乎要做一个hal_pin的适配

Architecture

在这个machine_spi.c中,很明确地要求在工程中启用宏"MICROPY_PY_MACHINE_SPI"。
OK,我也在mm32的mpconfigport.h中增加这个宏定义。

首先,我要把已经验证好的hal_spi.h和hal_spi.c文件放到mm32的驱动代码目录下面,在mm32的ports目录下创建machine_spi.h和machine_spi.c文件,可以预期不会再加更多的源文件了,只是在现有文件结构下改代码。

在具体代码的实现上,我将参考我已实现的machine_uart为参考,处理绑定引脚、多实例的具体问题。

然后在makefile增加新文件。

注意,SRC_C中的 $(SRC_MOD),不是在ports目录下的makefile中定义的,而是在extmod.mk文件中,然而,此处并没有包含extmod/machine_spi.c,所以仍需要手工引入。但阅读了别的包含machine_spi的makefile缺没却没有显示引用extmod的这个文件。算了,启动一次编译,看build log里有没有处理这个文件。
在这里插入图片描述
果然,build默认会把extmod全都过一遍,在启用MICROPY_PY_MACHINE_SPI的时候,extmod/machine_spi.c的代码正式起作用,送入编译器开编。

后续我根据报错的需求,不断添加代码,尽量确保编译通过。可以想见,再次全部编译通过的时候,也即将是移植完成。

extmod/machine_spi.c

之前的试编译中报错,提示在“mp_machine_soft_spi_print”函数中,有个“MP_HAL_PIN_FMT”没有定义。这里就要说道一下了。

extmod/machine_spi.c中有两部分:硬件SPI和软件SoftSPI。虽然我们希望使用硬件SPI,但因为micropython将这俩货放在一起,我们就仍要顺带完成SoftSPI的移植。

在stm32的modmachine.c中对machine模块内部的类定义时,有如下代码:

    #if MICROPY_PY_MACHINE_SPI{ MP_ROM_QSTR(MP_QSTR_SPI),                 MP_ROM_PTR(&machine_hard_spi_type) },{ MP_ROM_QSTR(MP_QSTR_SoftSPI),             MP_ROM_PTR(&mp_machine_soft_spi_type) },#endif

也就是ports目录下的machine_spi.c实现的machine_hard_spi_type,将会被当做SPI注册到系统中,而extmod/machine_spi.c中实现的mp_machine_soft_spi_type将使用SoftSPI这个名字。

从文章的顺序上,这里继读extmod/machine_spi.c的内容,但如果是调代码,这里就已经要在ports/xx/machine_spi.c中写一些代码框架了。

extmod/machine_spi.c的代码中通过代码注解描述了两个部分:

...
/******************************************************************************/
// MicroPython bindings for generic machine.SPI
.../******************************************************************************/
// Implementation of soft SPI
...

我曾经有念头,想先把SoftSPI的部分注释掉,仅实现纯硬件SPI的部分。。。算了,估计后面SoftI2C可能也要用,跑得了和尚跑不了庙,一起看吧。


STATIC mp_obj_t machine_spi_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {mp_obj_base_t *s = (mp_obj_base_t *)MP_OBJ_TO_PTR(args[0]);mp_machine_spi_p_t *spi_p = (mp_machine_spi_p_t *)s->type->protocol;spi_p->init(s, n_args - 1, args + 1, kw_args);return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_spi_init_obj, 1, machine_spi_init);

关于硬件SPI,最基本的是machine_spi_init()、machine_spi_deinit()和machine_spi_transfer()三个函数,这三个函数的实现都是调用s->type->protocol中注册的一堆函数。(在后面阅读代码时可以看到,这三个函数的底层都是在ports/xx/machine_spi.c中实现的,然后包装起来封装到protocol中装入mp_machine_spi_p_t类型的结构体中,然后再由ports/xx/machine_spi.c中引用组装成类模块。本文件中的spi_read()、spi_readinto()、spi_write()、spi_write_readinto()都是基于spi_transfer()定义的。

这里相当于是实现了SPI的一组通用操作的函数,但向上和向下约定了SPI的实现接口。

硬件SPI对底层的要求比较简单,软件SoftSPI相当于是对Pin模型的一个封装,稍微有点麻烦。但其实因为跟此处主题关联不大,所以在时间紧急的情况下,看似无关的工作量容易让人烦躁。

软件SoftSPI

软件SoftSPI直接操作GPIO引脚实现SPI通信。

baudrate_from_delay_half()函数,以500k作为目标频率,将传入的delay_half转换成delay的重复次数。
baudrate_to_delay_half()函数,以500k作为目标频率,将传入的baudrate转换成delay_half的时长。
这两个函数在搞什么鬼?

在mp_machine_soft_spi_print()函数中,mp_machine_soft_spi_obj_t是在extmod/machine_spi.h中定义的,可以不用管。然后就是“MP_HAL_PIN_FMT”,这是之前试编译出现的问题点,看一下别家的代码怎么实现的。

mimxrt在自己的mphalport.h中,包含了所有关于mp_hal_pin的定义,我也拿过来参考一下。刚好mm32的port中也有“machine_pin_obj_t”的定义,相似度最近。注意,随手要加上对machine_pin.h的包含,否则会报错找不到machine_pin_obj_t的定义。

仅仅加了四行代码就能编译通过了,WF。

#define MP_HAL_PIN_FMT          "%q"
#define mp_hal_pin_obj_t        const machine_pin_obj_t *
#define mp_hal_get_pin_obj(o)   pin_find(o)
#define mp_hal_pin_name(p)      ((p)->name)

mp_machine_soft_spi_make_new()函数内部调用的mp_hal_get_pin_obj()实际调用的是pin_find() 函数,而我在mm32上实现的pin_find()函数是兼容mimxrt的,所以可以无缝对接。兼容真香啊。

但这里仅仅是做了个接口定义,对引脚翻转的操作都放在了drivers/bus/softspi.c中,这里的mp_hal_pin_write()、mp_hal_pin_output()、mp_hal_delay_us_fast()等函数我在mm32中都没实现,怎么就编通了?

在py/mphal.h中有这么一段代码解释了我的疑问:

// If port HAL didn't define its own pin API, use generic
// "virtual pin" API from the core.
#ifndef mp_hal_pin_obj_t
#define mp_hal_pin_obj_t mp_obj_t
#define mp_hal_get_pin_obj(pin) (pin)
#define mp_hal_pin_read(pin) mp_virtual_pin_read(pin)
#define mp_hal_pin_write(pin, v) mp_virtual_pin_write(pin, v)
#include "extmod/virtpin.h"
#endif

但我已经定义了mp_hal_pin_obj_t了!!!可能之前编译好的模块乱入了,我要试着重新编译一下。还有mp_hal_pin_output还是要实现一下的。

重新编译竟然还可以通过???我在map文件中也没找到有用的信息。

试着把image下载到板子上运行一下,竟然也没有hardfault。怪哉。

好吧,之前一直担心软件SoftSPI乱入,至少现在暂时不用考虑SoftSPI,可以将注意力集中在硬件SPI。

硬件SPI

忍不了了,终于可以畅快地在ports/xx/machine_spi.c 中写代码了。

ports/xx/machine_spi.c

先参考esp8266的machine_hspi.c实现一个代码框架。设法打通调用路径再插代码。

呵呵,我在esp8288的实现中发现了一个小秘密:

    if (dest == NULL) {// fast case when we only need to write datasize_t chunk_size = 1024;size_t count = len / chunk_size;size_t i = 0;for (size_t j = 0; j < count; ++j) {for (size_t k = 0; k < chunk_size; ++k) {spi_tx8fast(HSPI, src[i]);++i;}ets_loop_iter();}while (i < len) {spi_tx8fast(HSPI, src[i]);++i;}// wait for SPI transaction to completewhile (spi_busy(HSPI)) {}}

这里竟然可以用轮询模式啊。。。

我考虑的是,有一种情况,SPI作为单工模式工作时,类似于UART串口的只发不收和只收不发,软件只要把数塞给缓冲区就好了,由硬件自己搞。但再想想,SPI的接收也是由发送驱动的,全部是主动控制,用轮询问题不大。实际上UART的发送就是轮询实现的。如果是这样,后面的I2C也可以考虑类似的实现。

这里,我先在machine_spi.h中定义了一个machine_hw_spi_obj_t结构体。。。对应的引脚映射后面再补。。。相对于extmod/machine_spi.c,这里实现的spi都是“hw_spi”

一顿操作猛如虎。

编译一下,终于还是报错了。

LINK build-MB_F3270/firmware.elf
C:\msys64\usr\gcc-arm-none-eabi-10-2020-q4-major\bin\arm-none-eabi-ld.exe: build-MB_F3270/extmod/machine_spi.o: in function `mp_machine_soft_spi_transfer':
machine_spi.c:(.text.mp_machine_soft_spi_transfer+0x2): undefined reference to `mp_soft_spi_transfer'
C:\msys64\usr\gcc-arm-none-eabi-10-2020-q4-major\bin\arm-none-eabi-ld.exe: build-MB_F3270/extmod/machine_spi.o: in function `mp_machine_soft_spi_init':
machine_spi.c:(.text.mp_machine_soft_spi_init+0x62): undefined reference to `mp_soft_spi_ioctl'
C:\msys64\usr\gcc-arm-none-eabi-10-2020-q4-major\bin\arm-none-eabi-ld.exe: build-MB_F3270/extmod/machine_spi.o: in function `mp_machine_soft_spi_make_new':
machine_spi.c:(.text.mp_machine_soft_spi_make_new+0x7c): undefined reference to `mp_soft_spi_ioctl'
make: *** [Makefile:200: build-MB_F3270/firmware.elf] Error 1

mp_soft_spi_transfer()等三个函数找不到?估计没包进来。

追了一下代码,这几个函数是在drivers/bus/softspi.c中定义的,还是需要在makefile中把这个文件加进来。但这个文件中引用了了mp_pin_hal_xxxx的一堆宏函数。

算了,洗洗睡了,明天再搞。


先做一点体力活,把F3270的SPI引脚清单做出来,逐步进入状态。

查阅pin_info.xlsx文件,在mm32/boards/mb_f3270/machine_pin_board_pins.c中添加关于SPI的记录。搞定。

在stm32的makefile中,查找“softspi”关键字,找到了“driver_src_c”的定义,看来需要显式在makefile 增加drivers 目录下的文件。


DRIVERS_SRC_C += $(addprefix drivers/,\bus/softspi.c \)SRC_C += \main.c \...$(DRIVERS_SRC_C) \

再编一次。果然开始报错说找不到mp_hal_pin_xxx的一堆函数了。

在这里插入图片描述
参照mimxrt在mphalport.h的实现,也适配一下。

这里看到还有mp_hal_pin_open_drain()的实现,暂时不用管,可能是给后面SoftI2C用的。


#define mp_hal_pin_write(p, value) (GPIO_WriteBit(p->gpio_port, (1u << p->gpio_pin), value))
#define mp_hal_pin_read(p)  (GPIO_ReadInputDataBit(p->gpio_port, (1u << p->gpio_pin)))
#define mp_hal_pin_output(p) machine_pin_set_mode(p, PIN_MODE_OUT_PUSHPULL);
#define mp_hal_pin_input(p)  machine_pin_set_mode(p, PIN_MODE_IN_PULLUP);...#define mp_hal_delay_us_fast(us) mp_hal_delay_us(us)

在mm32/mphalport.h文件中添加了一些代码,同时在machine_pin.c文件中添加了machine_pin_set_mode()函数的实现。

再编一次。编通了。
在这里插入图片描述
顺便启用了SoftSPI。重新编译,现在到板子里试一下。

在这里插入图片描述
可以导入SPI模块,并且能dir到内部属性,但实例化的语句会触发hardfault。

初步分析是machine_hw_spi_make_new()函数中的动态内存分配代码导致的hardfault。

    machine_hw_spi_obj_t *self = m_new_obj(machine_hw_spi_obj_t);self->base.type = &machine_hw_spi_type;

这个m_new_obj()函数最终调用了m_malloc()函数。。。关于动态内存分配的事情,这里暂时先不考虑。

按照以往的做法,用hw_spi_find()函数取代这里的new函数。

const machine_hw_spi_obj_t *hw_spi_find(mp_obj_t user_obj)
{/* 如果传入参数本身就是一个uart的实例,则直接送出这个UART。 */if ( mp_obj_is_type(user_obj, &machine_hw_spi_type) ){return user_obj;}/* 如果传入参数是一个uart通道号,则通过索引在UART清单中找到这个通道,然后送出这个通道。 */if ( mp_obj_is_small_int(user_obj) ){uint8_t idx = MP_OBJ_SMALL_INT_VALUE(user_obj);if ( idx < MACHINE_HW_SPI_NUM ){return machine_hw_spi_objs[idx];}}mp_raise_ValueError(MP_ERROR_TEXT("HW SPI doesn't exist"));
}

然后创建了一些函数用以完成硬件SPI的适配。。。到凌晨了,刚准备好代码,勉强调通了,还没测。不想写笔记了,洗洗先睡,明天再搞。


昨天午夜完成了一个版本,今天到办公室用逻辑分析仪测了一下,修复了micropython中SPI的相位设置同mm32底层驱动设置不一致的问题。

目前可以使用SPI模块的write()和read()方法进行读写。但是readinto()和write_readinto()函数在工作时仍然会抛异常,说是需要提供带buffer的协议。

>>> arr2=[]
>>> spi0.write_readinto(arr, arr2)
Traceback (most recent call last):File "<stdin>", line 1, in <module>
TypeError: object with buffer protocol required

我估计还是在移植micropython内核时没处理好动态内存分配malloc的问题。抽空还是要专门解决掉。

END

这篇关于在mm32f3270上为micropython创建SPI模块的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/755637

相关文章

Node.js 中 http 模块的深度剖析与实战应用小结

《Node.js中http模块的深度剖析与实战应用小结》本文详细介绍了Node.js中的http模块,从创建HTTP服务器、处理请求与响应,到获取请求参数,每个环节都通过代码示例进行解析,旨在帮... 目录Node.js 中 http 模块的深度剖析与实战应用一、引言二、创建 HTTP 服务器:基石搭建(一

手把手教你idea中创建一个javaweb(webapp)项目详细图文教程

《手把手教你idea中创建一个javaweb(webapp)项目详细图文教程》:本文主要介绍如何使用IntelliJIDEA创建一个Maven项目,并配置Tomcat服务器进行运行,过程包括创建... 1.启动idea2.创建项目模板点击项目-新建项目-选择maven,显示如下页面输入项目名称,选择

python中的与时间相关的模块应用场景分析

《python中的与时间相关的模块应用场景分析》本文介绍了Python中与时间相关的几个重要模块:`time`、`datetime`、`calendar`、`timeit`、`pytz`和`dateu... 目录1. time 模块2. datetime 模块3. calendar 模块4. timeit

Python模块导入的几种方法实现

《Python模块导入的几种方法实现》本文主要介绍了Python模块导入的几种方法实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学... 目录一、什么是模块?二、模块导入的基本方法1. 使用import整个模块2.使用from ... i

python: 多模块(.py)中全局变量的导入

文章目录 global关键字可变类型和不可变类型数据的内存地址单模块(单个py文件)的全局变量示例总结 多模块(多个py文件)的全局变量from x import x导入全局变量示例 import x导入全局变量示例 总结 global关键字 global 的作用范围是模块(.py)级别: 当你在一个模块(文件)中使用 global 声明变量时,这个变量只在该模块的全局命名空

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

在cscode中通过maven创建java项目

在cscode中创建java项目 可以通过博客完成maven的导入 建立maven项目 使用快捷键 Ctrl + Shift + P 建立一个 Maven 项目 1 Ctrl + Shift + P 打开输入框2 输入 "> java create"3 选择 maven4 选择 No Archetype5 输入 域名6 输入项目名称7 建立一个文件目录存放项目,文件名一般为项目名8 确定

Java 创建图形用户界面(GUI)入门指南(Swing库 JFrame 类)概述

概述 基本概念 Java Swing 的架构 Java Swing 是一个为 Java 设计的 GUI 工具包,是 JAVA 基础类的一部分,基于 Java AWT 构建,提供了一系列轻量级、可定制的图形用户界面(GUI)组件。 与 AWT 相比,Swing 提供了许多比 AWT 更好的屏幕显示元素,更加灵活和可定制,具有更好的跨平台性能。 组件和容器 Java Swing 提供了许多

【STM32】SPI通信-软件与硬件读写SPI

SPI通信-软件与硬件读写SPI 软件SPI一、SPI通信协议1、SPI通信2、硬件电路3、移位示意图4、SPI时序基本单元(1)开始通信和结束通信(2)模式0---用的最多(3)模式1(4)模式2(5)模式3 5、SPI时序(1)写使能(2)指定地址写(3)指定地址读 二、W25Q64模块介绍1、W25Q64简介2、硬件电路3、W25Q64框图4、Flash操作注意事项软件SPI读写W2