RT-Thread GD32F4xx实现SD卡热插拔检测功能

news2024/11/19 15:13:14

GD32F470移植RT-Thread操作系统添加SD卡功能,增加SD卡热插拔检测

    • 一、RT-Thread移植sd卡功能
    • 二、实现SD卡热插拔检测原理
    • 三、软件实现过程
    • 四、延展之ASSERT ERROR,即RT-Thread断言错误
    • 五、延展之STM32 SD卡热插拔检测
    • 六、结束语

一、RT-Thread移植sd卡功能

RT-Thread官方下载gd32f470的源代码,根据自己的情况裁剪一下无关的文件,配置好工程,打开env配置工具,使能SDIO,使能DFS虚拟文件系统,使能elm-chan fatfs文件系统,将注册的sd0块设备以elm fatfs形式注册到DFS虚拟文件系统中。文件系统需要用到RTC,在env中使能RTC功能,配置一下系统时间。
env配置如下图:
在这里插入图片描述
env配置完之后,打开keil编译工程,下载到板子里,用console查看sd设备是否挂载成功(默认sd0,块设备),如下图挂载成功:
在这里插入图片描述
当设备列表中有sd0块设备时表明sd卡注册成功了,此时在应用层调用dfs_mount挂载sd0,然后就可以使用文件系统了。
SD卡的流程大概就这样,这里不做详细描述了。RT-Thread官方下载的源代码工程裁剪以及env工具配置使用等可以参考我另一篇博文。工程裁剪及env配置

二、实现SD卡热插拔检测原理

添加好sd卡驱动后,挂载sd0块设备后就能用文件系统操作了。刚开始调试好时,测试发现如果上电前未插入sd卡,系统会直接死机;另外在sd卡挂载成功后,中途拔卡也会出现系统故障;如果支持热插拔会好很多,提升用卡体验感。但是gd32f470的sd驱动中不包含卡的热插拔功能,需要自己实现。
实现方式为:
通过检测SD卡的check pin脚的电平边沿变化来检测SD卡时候插入和拔出,当插入SD卡的时候,该脚变为低电平,当拔掉SD卡的时候,该脚变为高电平。
不过该方式有一定的局限性,因为有些板子的check pin是悬空的,并没有接在mcu的任何脚上,这种情况下,使用上面所述的方法进行热插拔检测就不可行了,比方说,野火的F429挑战者开发板,它的SD卡的check pin就是悬空的,如图:
在这里插入图片描述
不过我所用的板子,checkpin脚是有接到mcu的管脚上的,如下图:
在这里插入图片描述
所以只需在sd卡应用代码里增加这个管脚的信号检测就可以实现sd卡的热插拔功能了!

三、软件实现过程

1、定义热插拔检测管脚

/* SD Card hot plug detection pin */
#define SD_CHECK_PIN        GET_PIN(A, 15)

2、创建sd卡挂载线程,sd卡挂载线程代码如下:

static void sd_mount(void *parameter)
{
    rt_uint8_t re_sd_check_pin = 1;

    while (1)
    {
        rt_thread_mdelay(200);
        if(re_sd_check_pin && (re_sd_check_pin = rt_pin_read(SD_CHECK_PIN)) == 0)
        {
            if (_sdcard_mount() == RT_EOK)
            {
                LOG_I("sd mount ok!");
                rt_event_send(&sd_mount_event, eEVENT_SD_MOUNT);
            }
        }

        if (!re_sd_check_pin && (re_sd_check_pin = rt_pin_read(SD_CHECK_PIN)) != 0)
        {
            if (_sdcard_unmount() == RT_EOK)
            {
                LOG_I("sd unmount ok!");
                rt_event_send(&sd_mount_event, eEVENT_SD_UNMOUNT);
            }
        }
    }
}

该线程只是检测SD卡插拔的状态。
“re_sd_check_pin = 1"的作用是为了让SD卡在未上电的时候已经插进去了这种情况下制造电平边沿用的。因为如果SD卡一直插着,是没有电平变化的。“re_sd_check_pin = 1"就是为了手动制造一个电平变化出来。否则无法通过if语言的电平边沿检测,也就是说无法执行”_sdcard_mount()”。需要注意的是,这段代码中的两个if语句之后都会跟着一个"re_sd_check = rt_pin_read(SD_CHECK_PIN)"去记录上次的的电平值。

在上电后拔卡时,检测SD_CHECK_PIN管脚为高电平,此时第二个if条件成立,执行"_sdcard_unmount()",卸载完sd卡后我加了一个卸载事件发送,告知sd卡应用线程,此时sd卡已拔出,关闭所有对sd卡的操作。

在上电后插卡时,检测SD_CHECK_PIN管脚为高电平,此时第一个if条件成立,执行"_sdcard_mount()",挂载完sd卡后我加了一个挂载事件发送,告知sd卡应用线程,此时sd卡已插入,可以对sd卡进行操作。

3、SD卡挂载函数_sdcard_mount()代码如下:

static rt_int32_t _sdcard_mount(void)
{
    rt_device_t device;

    device = rt_device_find("sd0");
    if (device == RT_NULL)
    {
        if (sd_device_register() != RT_EOK) return -RT_ERROR;
        device = rt_device_find("sd0");
    }
    if (device != RT_NULL)
    {
        if (dfs_mount("sd0", "/", "elm", 0, 0) == RT_EOK)
        {
            LOG_I("sd card mount to '/'");
            return RT_EOK;
        }
        else
        {
            LOG_W("sd card mount to '/' failed!");
            return -RT_ERROR;
        }
    }
    return -RT_ERROR;
}

这里主要就是sd卡的挂载,当发现sd0设备找不到的时候,执行sd_device_register()注册设备,注册完之后find sd0设备,然后mount该sd0设备到dfs虚拟文件系统中。

4、SD卡卸载函数_sdcard_unmount()代码如下:

static rt_int32_t _sdcard_unmount(void)
{
    rt_thread_mdelay(200);
    if (dfs_unmount("/") != RT_EOK) return -RT_ERROR;
    LOG_I("Unmount \"/\"");
    if (sd_device_remove() != RT_EOK) return -RT_ERROR;
    return RT_EOK;
}

这里主要就是sd卡的卸载,先卸载文件系统,再移除sd0设备。

5、sd_device_register()

rt_err_t sd_device_register(void)
{
    rt_hw_sdcard_init();
    return RT_EOK;
}

sd设备驱动就是调用rt_hw_sdcard_init()进行初始化的,检测到重新插卡后就再次初始化一下sd卡。

6、sd_device_remove()

rt_err_t sd_device_remove(void)
{
    rt_sem_detach(&sd.sem);
    rt_mutex_detach(&sd.sd_lock);
    rt_device_unregister(&sd.sdcard_device);
    return RT_EOK;
}

当时调试的时候,这里导致了一个ASSERT错误,查了一天才找到原因。。。
这里需要分离semaphore和mutex,然后unregister设备。一开始我没有直接分离semaphore和mutex,导致拔卡之后再插卡就提示ASSERT错误,然后系统奔溃。
为啥是分离semaphore和mutex而不是删除,因为在rt_hw_sdcard_init()里面初始化了静态的semaphore和mutex。如果是创建的动态semaphore和mutex,则是执行删除,然后rt_free内存,否则也是会运行出错。

7、运行效果如下
在这里插入图片描述

四、延展之ASSERT ERROR,即RT-Thread断言错误

在这次调试中,有遇到ASSERT错误,导致系统直接死机。即我前面说到的未删除原来初始化的信号量及互斥量,再次初始化时导致ASSERT错误。
当时错误的提示:
在这里插入图片描述
RT-Thread系统还挺好用的,直接把错误给打印出来了,而且还很详细。首先obj != object是断言的内容,在rt_object_init函数里面断言出错的,而且告知了是在383行!我理解的就是:初始化object的时候,系统检测初始化的object和obj列表中的一致,意思是已经初始化了、已经存在该对象信息。
查看代码找到ASSERT错误的地方:
在这里插入图片描述

常见RT-Thread ASSERT错误原因:
1、使用的对象未创建或初始化,如:事件、信号量、消息队列等等。
2、对象还未初始化,在别的线程调用了,如:rt_sem_take等等。
3、初始化函数使用错误,init是静态初始化,先定义好控制块存储地址;create是动态初始化,由动态内存分配,create返回的是控制块的句柄,即指针。
4、RT-Thread默认只需要init一次内核对象,第二次默认不初始化。对未删除或分离的对象,再次初始化也会导致assert错误。

五、延展之STM32 SD卡热插拔检测

最初是使用ST的bsp库来开发sd卡功能的,但是发现在sd卡初始化的时候,读取scr寄存器一直没响应,导致sd卡初始化失败。随后在网上找到一种解决办法:

在这里插入图片描述
在rthw_sdio_send_command()里,去掉接收等待。
在cmd51发送完之后,hw_sdio->sta的值为0x20a400,与上HW_SDIO_IT_RXACT为真,导致超时,此时判断始终返回错误。注释掉cm->err = -RT_ERROR后,挂载成功,可以读写sd卡文件。目前尝试二十几次,均正常。现在始终不明白是读寄存器错误还是其他原因。 同样的程序在st下运行,sta返回值是0x400。

但是我调试发现偶尔有那么一两次能成功,但是概率很低,几乎99.9%初始化失败。对比找了两个芯片的驱动差异,没找到具体原因。。。最后无奈用gd32f470的bsp工程来调试sd卡,发现env配置好下载到板子里就能挂载sd设备!

用STM32的芯片驱动实现SD卡热插拔功能,原理一样,只是驱动库不一样,其实就是_sdcard_mount()和_sdcard_unmount()不一样,具体看代码:

1、_sdcard_mount()

static void _sdcard_mount(void)
{
    rt_device_t device;

    device = rt_device_find("sd0");
    if (device == NULL)
    {
        mmcsd_wait_cd_changed(0);
        stm32_mmcsd_change();
        mmcsd_wait_cd_changed(RT_WAITING_FOREVER);
        device = rt_device_find("sd0");
    }
    if (device != RT_NULL)
    {
        if (dfs_mount("sd0", "/", "elm", 0, 0) == RT_EOK)
        {
            LOG_I("sd card mount to '/'");
        }
        else
        {
            LOG_W("sd card mount to '/' failed!");
        }
    }
}

2、_sdcard_unmount()

static void _sdcard_unmount(void)
{
    rt_thread_mdelay(200);
    dfs_unmount("/");
    LOG_I("Unmount \"/\"");

    mmcsd_wait_cd_changed(0);
    stm32_mmcsd_change();
    mmcsd_wait_cd_changed(RT_WAITING_FOREVER);
}

stm32_mmcsd_change()是drv_sdio里面提供的一个接口,该函数实际是调用mmcsd_change()函数,触发mmcsd检测线程,检测线程按流程检测各类卡(SD卡、MMC卡),继续查看源代码可以发现,在init_sd里面有块设备探测失败的情况,该情况下会进入remove card的流程:
在这里插入图片描述

继续查看rt_mmcsd_blk_remove做了什么:
在这里插入图片描述
可以看到这里面起始也是删除当时初始化SD卡时创建的sem,然后unregister设备,再从对象信息列表中移除之前创建的object,最后rt_free该设备块。

六、结束语

最近调试GD32F470的sd卡功能,对RT-Thread操作系统还不是很熟悉,都是不断摸索的过程,特写此博文记录一下。也给正在调试或使用RT-Thread的朋友一点帮助。文中如有描述不对的地方还望提醒纠正。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1384537.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java研学-过滤与监听

一 过滤器 Filter 1 介绍 Java Web 组件之一(Servlet 的功能),可改变一个request和修改一个response。Filter不是Servlet,不能产生一个response,它是在一个request 到达Servlet之前预处理 request,也可以在response离开Servlet 后…

新一代数字原住民:市场痛点与“繁”思维应对之道

随着科技的迅速发展,尤其是互联网的普及,新一代数字原住民经营者已经逐渐成为市场的主力军。不同于传统的消费者,有着独特的消费习惯和心理需求。企业要在这激烈的市场竞争中获得优势,深入了解这一群体的特征和心理、行为&#xf…

【EMC专题】浪涌的成因与ICE 61000-4-5标准

什么是浪涌? 浪涌是一种无法预料的瞬态电压或电流尖峰,由附近的电子产品或是环境导致。 了解浪涌非常重要,因为浪涌有可能会导致设备的电气过应力损坏,造成系统故障等。 对于系统设计来说,重要的一点是我们如果无法控制浪涌的产生,那么只能通过将瞬态峰值电流导入到地,…

Pyside6/PyQt6中的QTimer类:轻松实现定时任务

文章目录 📖 介绍 📖🏡 环境 🏡📒 使用方法 📒📝 参数说明📝 常用方法⚓️ 相关链接 ⚓️📖 介绍 📖 在PySide/PyQt框架中,QTimer是一个核心类,主要用于在指定的间隔时间后触发某些事件。QTimer为开发者提供了一种处理和调度重复或单次动作的简便方式。 …

CSP网络结构实战 - 降低计算量的特征融合方式

CSP网络结构实战 - 降低计算量的特征融合方式 CSP网络结构实战 - 降低计算量的特征融合方式0. 引言1. CSP网络结构简介1.1 核心思想1.2 解决的问题 2. 实验验证2.1 CSP网络模型构建2.2 数据读取与预处理2.3 模型训练与验证 3. 对比实验4. 结果与总结 CSP网络结构实战 - 降低计算…

change事件传递多个参数

1.传递value页面参数 change"handleChange($event,123)" 2.传递选中的keyvalue或是选中的item 我用的是a-auto-complete,试验了用a-select也可以 就是在option里面,:value"JSON.stringify(d)" 然后在eval(( value ))转化就可…

Python中如何简化if...else...语句

一、引言 我们通常在Python中采用if...else..语句对结果进行判断,根据条件来返回不同的结果,如下面的例子。这段代码是一个简单的Python代码片段,让用户输入姓名并将其赋值给变量user_input。我们能不能把这几行代码进行简化,优化…

CTFhub-HTTP响应包源代码查看

CTFhub-Web-Web前置技能-“HTTP响应包源代码查看” 题目分析 页面空白,想到flag也许在源代码中 解题过程 F12,在element中,看到html代码,在其body中找到flag

对闭包的理解

概念: 一个函数对周围状态的引用捆绑在一起,闭包让开发者可以从内部函数访问外部 函数的作用域 简单理解:闭包 内层函数 外层函数的变量 一个函数对周围状态的引用捆绑在一起,闭包让开发者可以从内部函数访问外部 函数的作…

10.9.2 std::function 存储函数对象 Page184

41行,pending只是inc的复制品,所以43,44行,不会改变inc()的值 demo_function2()的运行结果: 59行,pending是inc的引用,所以61,62行将会改变inc()的值

如何在Windows 10/11的防火墙中禁止和允许某个应用程序,这里提供详细步骤

想阻止应用程序访问互联网吗?以下是如何通过简单的步骤阻止和允许Windows防火墙中的程序。​ 一般来说,大多数用户永远不需要担心应用程序访问互联网。然而,在某些情况下,你需要限制应用程序访问互联网。 例如,有问题…

webstorm最新版 激活 成功了

使用webstorm开发工具 很完美,第一次用webstorm IDE 开发工具就完美的激活了,你也不妨试试 链接地址:http://mano100.cn/thread-1942-1-1.html 激活后如下

Django数据库选移的preserve_default=False是什么意思?

有下面的迁移命令: migrations.AddField(model_namemovie,namemov_group,fieldmodels.CharField(defaultdjango.utils.timezone.now, max_length30),preserve_defaultFalse,),迁移命令中的preserve_defaultFalse是什么意思呢? 答:如果模型定…

Vue-Router 路径匹配与重定向

一、效果与描述 通过设置路由匹配同时设置重定向,让输错的网址重定向到指定页面,例如在网页输入网页地址把路径进行任意修改,重定向到登录页面。 二、Vue-Router代码 import { createRouter, createWebHashHistory } from vue-routerimport …

二、MySQL安装

目录 1、双击mysql8的安装向导 2、分为首次安装和再安装 1)、首次安装 (1)如果是首次安装mysql系列的产品,需要先安装mysql产品的安装向导 (2)选择安装模式 2)、不是首次安装 &#xff0…

在加载第三方库过程中,无法加载到库的问题(使用readelf, patchelf命令)

无法加载到库问题 问题及分析过程readelf 命令patchelf命令 问题及分析过程 在开发一个程序过程中,需要加载第三方库iTapTradeAPI, 在CMakeList.txt中已经设置了CMAKE_INSTALL_RPATH,但是发布到生产之后由于目录问题无法加载到libiTapTradeAPI库了 下面…

RH850P1X芯片学习笔记-Flash Memory

文章目录 FeaturesClock Supply Block DiagramFlash SizeMemory ConfigurationRegistersRegister Base AddressList of RegistersRegister Reset Condition 与Flash Memory相关的操作模式Functional OverviewOption BytesOPBT0 — Option Byte 0OPBT1 — Option Byte 1OPBT2 —…

超简单的简历模板精选5篇

HR浏览一份简历也就25秒左右,如果你连「好简历」都没有,怎么能找到好工作呢? 如果你不懂得如何在简历上展示自己,或者觉得怎么改简历都不出彩,那请你一定仔细读完。 个人求职简历第 1 篇 男 22 本科 AI简历 市场营…

ZZULIOJ 1112: 进制转换(函数专题)

题目描述 输入一个十进制整数n,输出对应的二进制整数。常用的转换方法为“除2取余,倒序排列”。将一个十进制数除以2,得到余数和商,将得到的商再除以2,依次类推,直到商等于0为止,倒取除得的余数…

零零鸡生态养殖农场“出圈”,有“智”更有“质”,助力本土品牌高质量发展

什么是生态农场?不同于常规农场,它对农业生产经营单元的各个关键环节有着极为严格的要求,强调整体、协调、循环、再生、多样,产品质量自然更好,附加值也更高,更能满足日趋多样化的巨大市场。零零鸡生态农场…