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

  • 22
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值