Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
NAND-FLASH
UFFS
文件系统
mt29f4g08驱动
发布于 2022-03-10 11:26:03 浏览:1114
订阅该版
[tocm] 参考realboard-stm32f4下的k9f2g08u0b和原子的F429驱动,realboard-stm32f4链接(https://gitee.com/chuweiteng/realboard-stm32f4/tree/master/software/examples/drivers) # CUBE配置  # 源码 ``` /* * File : mt29f4g08.c * Description: nandflash 驱动 * * Change Logs: * Date Author Notes * 2022-02-12 zyx first implementation */ #include
#include "mt29f4g08.h" #define NAND_DEBUG rt_kprintf //#define NAND_DEBUG(...) //#define RT_UFFS_USE_CHECK_MARK_FUNCITON /* nandflash confg */ static struct nand_attriute nand_dev; NAND_HandleTypeDef hnand1; void nand_cmd(rt_uint8_t cmd) { /* write to CMD area */ *(volatile rt_uint8_t*)(NAND_ADDRESS | CMD_AREA) = cmd; } void nand_addr(rt_uint8_t addr) { /* write to address area */ *(volatile rt_uint8_t*)(NAND_ADDRESS | ADDR_AREA) = addr; } rt_uint8_t nand_read8(void) { /* read 1Byte */ return (*(volatile rt_uint8_t*)(NAND_ADDRESS)); } void nand_write8(rt_uint8_t data) { /* write 1Byte */ *(volatile rt_uint8_t*)(NAND_ADDRESS) = data; } uint8_t nand_readstatus(void) { nand_cmd(NAND_READSTA); return (nand_read8()); } uint8_t nand_waitforready(void) { uint8_t status = 0; volatile uint32_t time = 0; while(1) //等待ready { status = nand_readstatus(); //获取状态值 if(status & NSTA_READY) break; time++; if(time >= 0X1FFFF) return NSTA_TIMEOUT;//超时 } return NSTA_READY;//准备好 } uint8_t nand_modeset(uint8_t mode) { nand_cmd(NAND_FEATURE); nand_addr(0x01); nand_write8(mode); nand_write8(0); nand_write8(0); nand_write8(0); if(nand_waitforready() == NSTA_READY) return 0;//成功 else return 1;//失败 } uint32_t nand_readid(void) { uint8_t deviceid[5] = {0}; uint32_t id = 0; nand_cmd(NAND_READID); nand_addr(0x00); deviceid[0] = nand_read8(); deviceid[1] = nand_read8(); deviceid[2] = nand_read8(); deviceid[3] = nand_read8(); deviceid[4] = nand_read8(); //镁光的NAND FLASH的ID一共5个字节,但是为了方便我们只取4个字节组成一个32位的ID值 //根据NAND FLASH的数据手册,只要是镁光的NAND FLASH,那么一个字节ID的第一个字节都是0X2C //所以我们就可以抛弃这个0X2C,只取后面四字节的ID值。 id = ((uint32_t)deviceid[1])<<24 | ((uint32_t)deviceid[2])<<16 | ((uint32_t)deviceid[3])<<8 | deviceid[4]; return id; } uint8_t nand_reset(void) { nand_cmd(NAND_RESET); if(nand_waitforready() == NSTA_READY) return 0;//复位成功 else return 1;//复位失败 } uint8_t nand_waitrb(uint8_t rb) { uint16_t time = 0; while(time < 10000) { time++; if(rt_pin_read(NAND_RB) == rb) return 0; } return 1; } void nand_delay(uint16_t i) { while(i > 0) { i--; } } //获取ECC的奇数位/偶数位 //oe:0,偶数位 // 1,奇数位 //eccval:输入的ecc值 //返回值:计算后的ecc值(最多16位) uint16_t nand_ecc_get_OE(uint8_t oe, uint32_t eccval) { uint8_t i = 0; uint16_t ecctemp = 0; for(i = 0; i < 24; i++) { if((i % 2) == oe) { if((eccval>>i) & 0x01) ecctemp += 1<<(i>>1); } } return ecctemp; } //ECC校正函数 //eccrd:读取出来,原来保存的ECC值 //ecccl:读取数据时,硬件计算的ECC只 //返回值:0,错误已修正 // 其他,ECC错误(有大于2个bit的错误,无法恢复) uint8_t nand_ecc_correction(uint8_t* data_buf, uint32_t eccrd, uint32_t ecccl) { uint16_t eccrdo, eccrde, eccclo, ecccle; uint16_t eccchk = 0; uint16_t errorpos = 0; uint32_t bytepos = 0; eccrdo = nand_ecc_get_OE(1,eccrd); //获取eccrd的奇数位 eccrde = nand_ecc_get_OE(0,eccrd); //获取eccrd的偶数位 eccclo = nand_ecc_get_OE(1,ecccl); //获取ecccl的奇数位 ecccle = nand_ecc_get_OE(0,ecccl); //获取ecccl的偶数位 eccchk = eccrdo^eccrde^eccclo^ecccle; if(eccchk == 0XFFF) //全1,说明只有1bit ECC错误 { errorpos = eccrdo^eccclo; NAND_DEBUG("errorpos:%d\r\n", errorpos); bytepos = errorpos / 8; data_buf[bytepos] ^= 1<<(errorpos % 8); } else //不是全1,说明至少有2bit ECC错误,无法修复 { NAND_DEBUG("2bit ecc error or more\r\n"); return 1; } return 0; } static rt_err_t nandflash_readid(struct rt_mtd_nand_device *mtd) { uint32_t id = 0; id = nand_readid(); NAND_DEBUG("ID[%X]\n", id); if (id == MT29F4G08ABADA) { return (RT_EOK); } return (RT_ERROR); } static rt_err_t nandflash_readpage(struct rt_mtd_nand_device* device, rt_off_t page, rt_uint8_t *data, rt_uint32_t data_len, rt_uint8_t *spare, rt_uint32_t spare_len) { volatile uint16_t i = 0; uint8_t res = 0; uint8_t eccnum = 0; //需要计算的ECC个数,每NAND_ECC_SECTOR_SIZE字节计算一个ecc uint8_t errsta = 0; uint8_t *p = NULL; page = page + device->block_start * device->pages_per_block; if (page/device->pages_per_block > device->block_end) { return -RT_MTD_EIO; } errsta = RT_MTD_EOK; if (data && data_len) { nand_cmd(NAND_AREA_A); nand_addr(0); nand_addr(0); nand_addr(page); nand_addr(page >> 8); nand_addr(page >> 16); nand_cmd(NAND_AREA_TRUE1); res = nand_waitrb(0); //等待RB=0 if(res) return RT_MTD_EBUSY; //超时退出 nand_delay(1000); if(data_len != nand_dev.page_mainsize)//不是NAND_ECC_SECTOR_SIZE的整数倍,不进行ECC校验 { //读取NAND FLASH中的值 for(i = 0; i < data_len; i++) { *(volatile uint8_t*)data++ = nand_read8(); } } else { eccnum = data_len / NAND_ECC_SECTOR_SIZE; //得到ecc计算次数 p = data; for(res = 0; res < eccnum; res++) { FMC_Bank2_3->PCR3 |= 1<<6; //使能ECC校验 for(i = 0; i < NAND_ECC_SECTOR_SIZE; i++) //读取NAND_ECC_SECTOR_SIZE个数据 { *(volatile uint8_t*)data++ = nand_read8(); } while(!(FMC_Bank2_3->SR3 & (1<<6))); //等待FIFO空 nand_dev.ecc_hdbuf[res] = FMC_Bank2_3->ECCR3;//读取硬件计算后的ECC值 FMC_Bank2_3->PCR3 &= ~(1<<6); //禁止ECC校验 } i = nand_dev.page_mainsize; //从spare区读取之前存储的ecc值 nand_delay(30);//等待tADL nand_cmd(0x05);//随机读指令 nand_addr((uint8_t)i);//发送地址 nand_addr((uint8_t)(i >> 8)); nand_cmd(0xE0);//开始读数据 nand_delay(30);//等待tADL data = (uint8_t*)&nand_dev.ecc_rdbuf[0]; for(i = 0; i < 4 * eccnum; i++) //读取保存的ECC值 { *(volatile uint8_t*)data++ = nand_read8(); } for(i = 0; i < eccnum; i++) //检验ECC { if(nand_dev.ecc_rdbuf[i] != nand_dev.ecc_hdbuf[i] && nand_dev.ecc_rdbuf[i] != 0xFFFFFFFF)//不相等,需要校正 { NAND_DEBUG("err hd,rd:0x%x,0x%x\r\n",nand_dev.ecc_hdbuf[i],nand_dev.ecc_rdbuf[i]); NAND_DEBUG("eccnum:%d\r\n",eccnum); NAND_DEBUG("PageNum:%d\r\n",page); res = nand_ecc_correction(p+NAND_ECC_SECTOR_SIZE*i,nand_dev.ecc_rdbuf[i],nand_dev.ecc_hdbuf[i]);//ECC校验 if(res) errsta = RT_MTD_EECC; //标记2BIT及以上ECC错误 else errsta = RT_MTD_EECC_CORRECT; //标记1BIT ECC错误 } } } if(nand_waitforready() != NSTA_READY) return RT_MTD_EBUSY;//失败 } if (spare && spare_len) { nand_cmd(NAND_AREA_A); nand_addr(0);//NAND_ECC_SIZE nand_addr(8); nand_addr(page); nand_addr(page >> 8); nand_addr(page >> 16); nand_cmd(NAND_AREA_TRUE1); res = nand_waitrb(0); //等待RB=0 if(res) return RT_MTD_EBUSY; //超时退出 nand_delay(1000); //读取NAND FLASH中的值 for(i = 0; i < spare_len; i++) { *(volatile uint8_t*)spare++ = nand_read8(); } if(nand_waitforready() != NSTA_READY) return RT_MTD_EBUSY;//失败 } return errsta; } static rt_err_t nandflash_writepage(struct rt_mtd_nand_device* device, rt_off_t page, const rt_uint8_t *data, rt_uint32_t data_len, const rt_uint8_t *spare, rt_uint32_t spare_len) { volatile uint16_t i = 0; uint8_t res=0; uint8_t eccnum=0; //需要计算的ECC个数,每NAND_ECC_SECTOR_SIZE字节计算一个ecc page = page + device->block_start * device->pages_per_block; if (page/device->pages_per_block > device->block_end) { return -RT_MTD_EIO; } if (data && data_len) { nand_cmd(NAND_WRITE0); nand_addr(0); nand_addr(0); nand_addr(page); nand_addr(page >> 8); nand_addr(page >> 16); nand_delay(30); if(data_len % NAND_ECC_SECTOR_SIZE)//不是NAND_ECC_SECTOR_SIZE的整数倍,不进行ECC校验 { for(i = 0; i < data_len; i++) //写入数据 { nand_write8(*(volatile uint8_t*)data++); } } else { eccnum = data_len / NAND_ECC_SECTOR_SIZE; //得到ecc计算次数 for(res = 0; res < eccnum; res++) { FMC_Bank2_3->PCR3 |= 1<<6; //使能ECC校验 for(i = 0; i < NAND_ECC_SECTOR_SIZE; i++) //写入NAND_ECC_SECTOR_SIZE个数据 { nand_write8(*(volatile uint8_t*)data++); } while(!(FMC_Bank2_3->SR3 & (1<<6))); //等待FIFO空 nand_dev.ecc_hdbuf[res] = FMC_Bank2_3->ECCR3;//读取硬件计算后的ECC值 FMC_Bank2_3->PCR3 &= ~(1<<6); //禁止ECC校验 } i = nand_dev.page_mainsize; //计算写入ECC的spare区地址 nand_delay(30);//等待 nand_cmd(0x85);//随机写指令 nand_addr((uint8_t)i);//发送地址 nand_addr((uint8_t)(i >> 8)); nand_delay(30);//等待tADL data = (uint8_t*)&nand_dev.ecc_hdbuf[0]; for(i = 0; i < eccnum; i++) //写入ECC { for(res = 0; res < 4; res++) { nand_write8(*(volatile uint8_t*)data++); } } } nand_cmd(NAND_WRITE_TURE1); if(nand_waitforready() != NSTA_READY) return RT_MTD_EBUSY;//失败 } if (spare && spare_len) { nand_cmd(NAND_WRITE0); nand_addr(0);//NAND_ECC_SIZE nand_addr(0x08); nand_addr(page); nand_addr(page >> 8); nand_addr(page >> 16); nand_delay(30); for(i = 0; i < spare_len; i++) //写入数据 { nand_write8(*(volatile uint8_t*)spare++); } nand_cmd(NAND_WRITE_TURE1); if(nand_waitforready() != NSTA_READY) return RT_MTD_EBUSY;//失败 } return RT_MTD_EOK; } rt_err_t nandflash_eraseblock(struct rt_mtd_nand_device* device, rt_uint32_t block) { rt_uint32_t page = 0; block = block + device->block_start; page = block * 64; nand_cmd(NAND_CMD_ERASE0); nand_addr(page); nand_addr(page >> 8); nand_addr(page >> 16); nand_cmd(NAND_CMD_ERASE1); if(nand_waitforready() != NSTA_READY) return -RT_MTD_EIO;//失败 return RT_MTD_EOK; } static rt_err_t nandflash_pagecopy(struct rt_mtd_nand_device *device, rt_off_t src_page, rt_off_t dst_page) { uint8_t res = 0; uint16_t source_block = 0, dest_block = 0; src_page = src_page + device->block_start * device->pages_per_block; dst_page = dst_page + device->block_start * device->pages_per_block; source_block = src_page / nand_dev.block_pagenum; dest_block = dst_page / nand_dev.block_pagenum; if((source_block % 2) != (dest_block % 2)) return -RT_MTD_EIO;//判断源页和目的页是否在同一个plane中 nand_cmd(NAND_MOVEDATA_CMD0); nand_addr(0); nand_addr(0); nand_addr(src_page); nand_addr(src_page >> 8); nand_addr(src_page >> 16); nand_cmd(NAND_MOVEDATA_CMD1); res = nand_waitrb(0); //等待RB=0 if(res) return RT_MTD_EBUSY; //超时退出 //下面2行代码是真正判断NAND是否准备好的 // res = nand_waitrb(1); //等待RB=1 // if(res) return RT_MTD_EBUSY; //超时退出 nand_delay(1000); nand_cmd(NAND_MOVEDATA_CMD2); nand_addr(0); nand_addr(0); nand_addr(dst_page); nand_addr(dst_page >> 8); nand_addr(dst_page >> 16); nand_cmd(NAND_MOVEDATA_CMD3); if(nand_waitforready() != NSTA_READY) return -RT_MTD_EIO;//失败 return RT_MTD_EOK; } static struct rt_mtd_nand_driver_ops ops = { nandflash_readid, nandflash_readpage, nandflash_writepage, nandflash_pagecopy, nandflash_eraseblock, #if defined(RT_USING_DFS_UFFS) && !defined(RT_UFFS_USE_CHECK_MARK_FUNCITON) RT_NULL, RT_NULL, #else nandflash_checkblock, nandflash_markbad #endif }; static struct rt_mtd_nand_device _partition[1]; void rt_hw_mtd_nand_init(void) { FMC_NAND_PCC_TimingTypeDef ComSpaceTiming = {0}; FMC_NAND_PCC_TimingTypeDef AttSpaceTiming = {0}; hnand1.Instance = FMC_NAND_DEVICE; hnand1.Init.NandBank = FMC_NAND_BANK2; hnand1.Init.Waitfeature = FMC_NAND_PCC_WAIT_FEATURE_DISABLE; hnand1.Init.MemoryDataWidth = FMC_NAND_PCC_MEM_BUS_WIDTH_8; hnand1.Init.EccComputation = FMC_NAND_ECC_DISABLE; hnand1.Init.ECCPageSize = FMC_NAND_ECC_PAGE_SIZE_2048BYTE; hnand1.Init.TCLRSetupTime = 0; hnand1.Init.TARSetupTime = 1; ComSpaceTiming.SetupTime = 2; ComSpaceTiming.WaitSetupTime = 3; ComSpaceTiming.HoldSetupTime = 2; ComSpaceTiming.HiZSetupTime = 1; AttSpaceTiming.SetupTime = 2; AttSpaceTiming.WaitSetupTime = 3; AttSpaceTiming.HoldSetupTime = 2; AttSpaceTiming.HiZSetupTime = 1; if (HAL_NAND_Init(&hnand1, &ComSpaceTiming, &AttSpaceTiming) != HAL_OK) { Error_Handler( ); } nand_reset(); rt_thread_mdelay(100); nand_dev.id = nand_readid(); nand_modeset(4);//设置为MODE4,高速模式 nand_dev.page_totalsize = 2112; //nand一个page的总大小(包括spare区) nand_dev.page_mainsize = 2048; //nand一个page的有效数据区大小 nand_dev.page_sparesize = 64; //nand一个page的spare区大小 nand_dev.block_pagenum = 64; //nand一个block所包含的page数目 nand_dev.plane_blocknum = 2048; //nand一个plane所包含的block数目 nand_dev.block_totalnum = 4096; //nand的总block数目 _partition[0].page_size = 2048; _partition[0].pages_per_block = 64; _partition[0].block_total = 4096; _partition[0].oob_size = 64; _partition[0].oob_free = 48; _partition[0].block_start = 0; _partition[0].block_end = 4095; _partition[0].ops = &ops; rt_mtd_nand_register_device("nand0", &_partition[0]); } //功能测试 void nread(void) { int i; rt_uint8_t spare[64]; rt_uint8_t *data_ptr; struct rt_mtd_nand_device *device; device = &_partition[0]; data_ptr = (rt_uint8_t*) rt_malloc(nand_dev.page_mainsize); if (data_ptr == RT_NULL) { rt_kprintf("no memory\n"); return; } rt_memset(spare, 0, sizeof(spare)); rt_memset(data_ptr, 0, nand_dev.page_mainsize); nandflash_readpage(device, 0, data_ptr, nand_dev.page_mainsize, spare, sizeof(spare)); for(uint16_t j = 0; j < nand_dev.page_mainsize / 64; j++) { for (i = 0; i < 64; i ++) { rt_kprintf("0x%X,",data_ptr[j * 64 + i]); } rt_kprintf("\r\n"); } rt_kprintf("\n spare\n"); for (i = 0; i < sizeof(spare); i ++) { rt_kprintf("0x%X,",spare[i]); } rt_kprintf("\n\n"); /* release memory */ rt_free(data_ptr); } void nwrite(void) { int i; rt_uint8_t spare[64]; rt_uint8_t *data_ptr; struct rt_mtd_nand_device *device; device = &_partition[0]; data_ptr = (rt_uint8_t*) rt_malloc (nand_dev.page_mainsize); if (data_ptr == RT_NULL) { rt_kprintf("no memory.\n"); return; } /* Need random data to test ECC */ for (i = 0; i < nand_dev.page_mainsize; i ++) data_ptr[i] = i % 255; rt_memset(spare, 0xdd, sizeof(spare)); // nandflash_writepage(device, 0, data_ptr, PAGE_DATA_SIZE, spare, sizeof(spare)); nandflash_writepage(device, 0, data_ptr, nand_dev.page_mainsize, NULL, NULL); rt_free(data_ptr); } void nerase_all(void) { rt_uint32_t index; struct rt_mtd_nand_device *device; device = &_partition[0]; for (index = 0; index < device->block_total; index ++) { nandflash_eraseblock(device, index); } } void nid(void) { nandflash_readid(&_partition[0]); } MSH_CMD_EXPORT(nid, nand id); MSH_CMD_EXPORT(nerase_all, erase all blocks of a partition); MSH_CMD_EXPORT(nwrite, nand write page); MSH_CMD_EXPORT(nread, nand read page); ``` ``` #ifndef __MT29F4G08_H__ #define __MT29F4G08_H__ #include
#include "board.h" #define NAND_ECC_SIZE 16//16字节 #define NAND_MAX_PAGE_SIZE 4096 //定义NAND FLASH的最大的PAGE大小(不包括SPARE区),默认4096字节 #define NAND_ECC_SECTOR_SIZE 512 //执行ECC计算的单元大小,默认512字节 #define NAND_RB GET_PIN(D, 6) #define NAND_ADDRESS 0X80000000 //nand flash的访问地址,接NCE3,地址为:0X8000 0000 #define NAND_CMD 1<<16 //发送命令 #define NAND_ADDR 1<<17 //发送地址 //NAND FLASH命令 #define NAND_READID 0X90 //读ID指令 #define NAND_FEATURE 0XEF //设置特性指令 #define NAND_RESET 0XFF //复位NAND #define NAND_READSTA 0X70 //读状态 #define NAND_AREA_A 0X00 #define NAND_AREA_TRUE1 0X30 #define NAND_WRITE0 0X80 #define NAND_WRITE_TURE1 0X10 #define NAND_ERASE0 0X60 #define NAND_ERASE1 0XD0 #define NAND_MOVEDATA_CMD0 0X00 #define NAND_MOVEDATA_CMD1 0X35 #define NAND_MOVEDATA_CMD2 0X85 #define NAND_MOVEDATA_CMD3 0X10 //NAND FLASH状态 #define NSTA_READY 0X40 //nand已经准备好 #define NSTA_ERROR 0X01 //nand错误 #define NSTA_TIMEOUT 0X02 //超时 #define NSTA_ECC1BITERR 0X03 //ECC 1bit错误 #define NSTA_ECC2BITERR 0X04 //ECC 2bit以上错误 //NAND FLASH型号和对应的ID号 #define MT29F4G08ABADA 0XDC909556 //MT29F4G08ABADA #define MT29F16G08ABABA 0X48002689 //MT29F16G08ABABA struct nand_attriute { uint16_t page_totalsize; //每页总大小,main区和spare区总和 uint16_t page_mainsize; //每页的main区大小 uint16_t page_sparesize; //每页的spare区大小 uint8_t block_pagenum; //每个块包含的页数量 uint16_t plane_blocknum; //每个plane包含的块数量 uint16_t block_totalnum; //总的块数量 uint32_t id; //NAND FLASH ID uint32_t ecc_hard; //硬件计算出来的ECC值 uint32_t ecc_hdbuf[NAND_MAX_PAGE_SIZE/NAND_ECC_SECTOR_SIZE];//ECC硬件计算值缓冲区 uint32_t ecc_rdbuf[NAND_MAX_PAGE_SIZE/NAND_ECC_SECTOR_SIZE];//ECC读取的值缓冲区 }; void rt_hw_mtd_nand_init(void); #endif ``` # 应用 ``` /* * Function Name : nand_init * Description : 挂载nand * Parameter : * return : */ int nand_init(void) { rt_hw_mtd_nand_init(); /* mount nand flash partition 0 as root directory */ if(dfs_mount("nand0", "/", "uffs", 0, 0) == 0) rt_kprintf("uffs initialized!\n"); else rt_kprintf("uffs initialzation failed!\n"); return 0; } INIT_ENV_EXPORT(nand_init); ``` # 注意 只进行过简单测试,可能有问题
0
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
下雨了
这家伙很懒,什么也没写!
文章
2
回答
0
被采纳
0
关注TA
发私信
相关文章
1
【文件系统】目录查询
2
文件系统Posix 接口 的close API疑问
3
dfs_mount挂载文件系统路径的路径必须为‘/’才能成功
4
SD卡连续读写文件报错
5
文件系统挂载断言机制
6
文件系统是否支持挂载NFS网络文件系统
7
文件系统挂载失败!!!
8
dfs_filesystem_lookup() 返回NULL
9
webnet 是否可以做全动态网页,使用内存池来加快速度
10
“文件系统装在表”报错
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
DMA
USB
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
rt-smart
FAL
I2C_IIC
UART
ESP8266
cubemx
WIZnet_W5500
ota在线升级
PWM
BSP
flash
freemodbus
packages_软件包
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
编译报错
中断
Debug
rt_mq_消息队列_msg_queue
keil_MDK
ulog
SFUD
msh
C++_cpp
MicroPython
本月问答贡献
RTT_逍遥
10
个答案
3
次被采纳
xiaorui
3
个答案
2
次被采纳
winfeng
2
个答案
2
次被采纳
三世执戟
8
个答案
1
次被采纳
KunYi
8
个答案
1
次被采纳
本月文章贡献
catcatbing
3
篇文章
5
次点赞
lizimu
2
篇文章
9
次点赞
swet123
1
篇文章
4
次点赞
Days
1
篇文章
4
次点赞
YZRD
1
篇文章
2
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部