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

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的朋友一点帮助。文中如有描述不对的地方还望提醒纠正。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

代码随想录算法训练营第三十一天|理论基础、455.分发饼干、376. 摆动序列、53. 最大子序和

题目:理论基础 解释:贪心的本质是选择每一阶段的局部最优,从而达到全局最优 题目:455.分发饼干 文章链接:代码随想录 视频链接:LeetCode:455.分发饼干 题目链接:力扣题目链接 图释&#x…

C语言用函数指针实现计算器

一、运行结果&#xff1b; 二、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>//实现目录函数&#xff1b; void menum() {//打印目录&#xff1b;printf("***********************************************\n");printf("***…

公司官网,选全站定制还是模板建站?

最近更新了公司网站&#xff0c;总算了了一件大事。 虽然很久以前也做网站&#xff0c;但随着技术的发展&#xff0c;以前经常用的dreamwaver、table等形式&#xff0c;不知道被升级了多少代。现在前端同事说起的各种架构&#xff0c;对我来说是云里雾里。只能看懂一点点。 这…

【cmu15445c++入门】(5)c++中的模板类

一、template模板类 除了模板方法【cmu15445c入门】(4)c中的模板方法 模板也可以用来实现类 二、代码 /*** file templated_classes.cpp* author Abigale Kim (abigalek)* brief Tutorial code for templated classes.*/// Includes std::cout (printing). #include <io…

Dubbo的SPI机制

Dubbo SPI的基本工作流程&#xff1a; 加载扩展点配置&#xff1a;Dubbo SPI首先会加载所有的扩展点配置&#xff0c;这些配置通常是在META-INF/dubbo目录下的properties文件中定义的。每个配置文件的名称就是扩展点接口的全限定名&#xff0c;文件内容是扩展点实现的键值对&am…

BSP视频教程第29期:J1939协议栈CAN总线专题,源码框架,执行流程和应用实战解析,面向车通讯,充电桩,模组通信等(2024-01-08)

视频教程汇总帖&#xff1a;【学以致用&#xff0c;授人以渔】2024视频教程汇总&#xff0c;DSP第12期&#xff0c;ThreadX第9期&#xff0c;BSP驱动第29期&#xff0c;USB实战第5期&#xff0c;GUI实战第3期&#xff08;2024-01-08&#xff09; - STM32F429 - 硬汉嵌入式论坛 …

【2024系统架构设计】 系统架构设计师第二版-面向服务架构设计理论与实践

目录 一 概述 二 SOA的参考架构 三 SOA主要协议和规范 四 SOA设计标准和原则 五 SOA的设计模式 六 SOA的构建和实施 ​

《豫鄂烽火燎原大小焕岭》:一部穿越时空的历史史诗

《豫鄂烽火燎原大小焕岭》&#xff1a;一部穿越时空的历史史诗 一部赓续红色血脉的生动教材 一部讴歌时代英雄和人民精神宝典 当历史的烽烟渐渐远去&#xff0c;留下的是一页页泛黄的记忆和无数英雄的壮丽诗篇。李传铭的力作《豫鄂烽火燎原大小焕岭》正是这样一部深情的回望&am…

AIGC必备知识点:你不可不知的CNN(卷积神经网络)-知识全解析!

Look&#xff01;&#x1f440;我们的大模型商业化落地产品&#x1f4d6;更多AI资讯请&#x1f449;&#x1f3fe;关注Free三天集训营助教在线为您火热答疑&#x1f469;&#x1f3fc;‍&#x1f3eb; 大家在谈论的卷积神经网络究竟是什么&#xff1f;(Convolutional Neural Ne…

uniapp h5 发行后 微信第二次打开网址 页面白屏

发行后把网址给客户&#xff0c;第一次可以正常登录打开&#xff0c;第二次打开白屏 原因&#xff1a;第一次打开时没有token&#xff0c;所以跳转登录页&#xff0c;可以正常访问 第二次打开时有token&#xff0c;但是网址根目录没有配置默认页面&#xff0c;所以白屏 解决…

视频转为序列图的软件,让视频批量转为序列图

你是否曾经遇到过这样的困境&#xff1a;需要将一段视频转为一系列的图片&#xff0c;但却没有合适的工具来完成&#xff1f;或许你曾经手动截图&#xff0c;或者用其他方式&#xff0c;但结果往往不尽如人意&#xff0c;图片质量差、色彩失真、画面不清晰。现在&#xff0c;让…

Java实现获取两个时间节点之间的日期、月份、年份列表

我们在做一个需求的时候需要后端返回一个选中时间内的时间日期、月份、年份列表&#xff1a; 如&#xff1a;我想查询2024-01-01到2024-01-20这个时间里面的所有日期。 下面来看看代码 /*** 根据日期格式不同计算两个时间内的日期、月份、年* param beginTime 开始时间* para…

怎么把workspace的数据导入到simulink进行FFT分析?

怎么把数据导入到simulink在这篇博客已经阐述了&#xff0c;那么如何把数据导入到simulink还能进行FFT分析呢&#xff1f; 首先我们看simulink的FFT分析界面&#xff0c;&#xff08;前置步骤&#xff1a;导入powergui模块&#xff0c;双击powergui模块&#xff0c;Tool选项卡…

怎么使用好爬虫IP代理?爬虫代理IP有哪些使用技巧?

在互联网时代&#xff0c;爬虫技术被广泛应用于数据采集和处理。然而&#xff0c;在使用爬虫技术的过程中&#xff0c;经常会遇到IP被封禁的问题&#xff0c;这给数据采集工作带来了很大的困扰。因此&#xff0c;使用爬虫IP代理成为了解决这个问题的有效方法。本文将介绍如何使…

C++ Webserver从零开始:基础知识(一)——Linux网络编程基础API

前言 本专栏将从零开始制作一个C Webserver&#xff0c;用以记录笔者学习的过程 如果你想要跟着我这个专栏制作一个C Webserver,你需要掌握以下前置基础课程知识&#xff1a; 1.C/C的语法&#xff08;在Leetcode刷100~200题的程度即可&#xff09; 2.计算机网络基础知识 3…

Jmerer之FTP测试

1、文件上传下载测试&#xff0c;可以使用sample:FTP请求&#xff0c;当然也可以使用HTTP Request采样器中的File Upload向服务器上传文件 2、本章重点介绍FTP请求进行文件的上传下载测试&#xff0c;添加 FTP请求&#xff0c;界面主要配置如下&#xff1a; Server Name or I…

5G前装搭载率即将迈过10%大关,车载通讯进入多层次增长通道

对于智能化来说&#xff0c;车载通讯性能的提升&#xff0c;对于相关功能的用户体验优化、进一步减少通讯时延以及打开应用新空间&#xff0c;至关重要。 目前&#xff0c;2G/3G正在进入运营商逐步关闭运营的阶段&#xff0c;4G依然是主力&#xff0c;但5G也在迎来新的增长机会…

【深度学习 | 风格迁移】神经网络风格迁移,原理详解附详细案例源码

&#x1f935;‍♂️ 个人主页: AI_magician &#x1f4e1;主页地址&#xff1a; 作者简介&#xff1a;CSDN内容合伙人&#xff0c;全栈领域优质创作者。 &#x1f468;‍&#x1f4bb;景愿&#xff1a;旨在于能和更多的热爱计算机的伙伴一起成长&#xff01;&#xff01;&…

将Python程序打包成exe文件

一、什么是exe可执行文件&#xff1f; **exe文件英文全名是executable file&#xff0c;翻译为可执行文件&#xff08;但它不等于可执行文件&#xff09;&#xff0c;可执行文件包含两种&#xff0c;文件扩展名为.exe的是其中的一种。exe文件可以在Windows平台上直接双击运行&a…