tag 标签: SD NAND

相关帖子
相关博文
  • 2024-12-17 17:37
    165 次阅读|
    0 个评论
      前言   作为一名电子专业的学生,半导体存储显然是绕不过去的一个坎,今天聊一聊关于Nand Flash的一些小知识。   这里十分感谢深圳雷龙发展有限公司为博主提供的两片CS创世SD NAND的存储芯片,同时也给大家推荐该品牌的相关产品。   一、定义   存储芯片根据断电后是否保留存储的信息可分为易失性存储芯片(RAM)和非易失性存储芯片(ROM)。   非易失性存储器芯片在断电后亦能持续保存代码及数据,分为闪型存储器 (Flash Memory)与只读存储器(Read-OnlyMemory),其中闪型存储器是主流,而闪型存储器又主要是NAND Flash 和NOR Flash。   NAND Flash 存储单元尺寸更小,存储密度更高,单位容量成本更低,块擦/写速度快, 具有更长的寿命,多应用于大容量数据存储,如智能手机、PC、平板电脑、U 盘、固态硬盘、服务器等领域。   NOR Flash 读取速度更快,具备可在芯片内执行程序(XIP)的特点,在传输效率、稳定性和可靠性方面更具优势,通常用于小容量数据存储,适宜中等容量代码存储(通常在 1Mb~1Gb),在计算机、消费电子(智能手机、TV、TWS 耳机、可 穿戴设备)、安防设备、汽车电子(ADAS、车窗控制、仪表盘)等领域均有应用。   二、NAND Flash   NAND Flash的存储单元是数据存储的最小单位,目前闪存已经由数千亿个存储单元组成,通过将电子移入和移出封闭在绝缘体中的电荷存储膜来存储数据。   NAND Flash存储器使用浮栅晶体管,它能在没有电源的情况下存储信息。所有的电路都依赖于某种能量来使整个电池的电荷产生差异,这种能量迫使电子穿过栅极,Nand闪存的浮动栅极系统通过使用第二个栅极在电子穿过电池时收集和捕获一些电子, 这使得粘在浮栅上的电子在没有电压的情况下保持原位,在这一过程中不管是否有电源连 接,芯片都能继续存储下一个值。   NAND Flash 为大容量数据存储的实现提供了廉价有效的解决方案,是目前全球市场大容量非易失存储的主流技术方案。   三、NAND Flash分类   NAND闪存卡的主要分类以NAND闪存颗粒的技术为主,NAND闪存颗粒根据存储原理分为SLC、MLC、TLC和QLC,从结构上又可分为2D、3D两大类。   Flash按技术主要分为SLC、MLC、TLC和QLC四大类,对应不同的空间结构,这四类技术可又分为2D结构和3D结构两大类;2D结构的存储单元仅布置在芯片的XY平面中,为了提高存储密度,制造商开发了3D NAND或V-NAND(垂直NAND)技术,该技术将Z平面中的存储单元堆叠在同一晶圆上。   3D NAND, 即立体堆叠技术,如果把2D NAND看成平房,那么3D NAND就是高楼大厦,建筑面积成倍扩增,理论上可以无限堆叠,可以摆脱对先进制程工艺的束缚,同时也不依赖于极紫外光刻(EUV)技术。   与2D NAND缩小Cell提高存储密度不同的是,3D NAND只需要提高堆栈层数,目前多种工艺架构并存。从2013年三星推出了第一款24层SLC/MLC 3D V-NAND,到现在层数已经迈进200+层,并即将进入300+层阶段。目前,三星/西部数据/海力士/美光/铠 侠等几乎垄断了所有市场份额,并且都具有自己的特殊工艺架构,韩系三星/海力士的CTF,美系镁光/英特尔的FG,国内长江存储的X-tacking。   随着堆栈层数的增加,工艺也面临越来越多的挑战,对制造设备和材料也提出了更多的要求。主要包括以下几个方面:   1)ONON薄膜应力:随着器件层数增加,薄膜应力问题越发凸显,会影响后续光刻对准精度;   2)高深宽比通孔刻蚀:随着深宽比增加,刻蚀难度会显著增加,容易出现刻蚀不完全、通孔结构扭曲等问题;   3)WL台阶的设计与刻蚀:垂直管状环栅结构的器件需要刻蚀出精确的台阶结构,保障CT能打到对应位置,而随着层数增加, 工艺难度加大,需要重新设计WL台阶结构。   四、品牌推荐——雷龙发展代理CS创世SD NAND   对于电子这一专业来说,仅仅从书面上了解一款电子元件是远远不够格的,上手实践才是第一要义。   这里十分感谢深圳雷龙发展有限公司为博主提供的两片SD NAND的存储芯片,同时也给大家推荐该品牌的相关产品。   博主拿到手上的芯片型号为:CSNP4GCR01-AMW,其性能如下   这款存储芯片作为博主正在完成的物联网项目中表现优异,性能良好,是作为存储工具的不二选择。
  • 2024-8-16 16:04
    0 个评论
      在当今这个数字化飞速发展的时代,电子设备已成为我们日常生活中不可或缺的一部分。从智能手表到智能手机,从无人机到智能家居,各种高科技产品层出不穷,而这些设备的续航能力与性能表现直接关系到用户的使用体验。在此背景下,CS创世半导体推出的8GB SD NAND芯片,以其卓越的低功耗特性,成为了市场上的一颗璀璨明星,为电子设备的续航与性能提升开辟了新的路径。 低功耗:延长设备 生命 的秘密武器   在电子设备领域,低功耗一直是研发人员追求的目标之一。CS创世8GB SD NAND芯片以其惊人的低功耗表现,为这一难题提供了有效的解决方案。其读写电流仅为15mA,相较于市场上同类产品,这一数值显著降低了功耗水平。这意味着,在相同条件下,搭载创世8GB SD NAND芯片的设备能够拥有更长的使用时间,减少了频繁充电的烦恼,极大地提升了用户体验。特别是在运动耳机、相机等需要长时间运行且对电池寿命有严格要求的设备上,这一低功耗特性更是显得尤为重要。   小巧设计:空间与成本的双重优化   除了低功耗之外,CS创世8GB SD NAND芯片在设计上也展现出了非凡的创意与实用性。其封装尺寸仅为7*8.5毫米,仅有8个管脚,这种紧凑的设计使得它在各种小型设备中都能轻松找到立足之地。对于设计师而言,这意味着更大的布局灵活性,可以更加自由地规划电路板空间,实现产品的轻薄化设计。同时,小巧的封装也降低了生产成本,使得这款芯片在价格上更具竞争力,进一步推动了其在市场上的普及。   高性能:稳定与速度的双重保障   CS创世8GB SD NAND芯片不仅在功耗和设计上表现出色,在性能方面也同样令人瞩目。其内置的控制核心和高稳定性的存储单元,确保了数据传输的高效与稳定。在HD TUNE实测中,这款芯片的小文件读取速度可达到1.4MB/S,这一速度在同类产品中处于领先地位。对于需要快速数据传输的应用场景,如高清摄影、视频录制等,CS创世8GB SD NAND芯片无疑能够提供更流畅、更高效的体验。此外,其高稳定性和可靠性也保证了数据的安全存储,避免了因数据丢失或损坏而带来的损失。   广泛应用:满足多元化需求   CS创世8GB SD NAND芯片的低功耗、小巧设计及高性能特性,使其在各种电子设备中都有着广泛的应用前景。除了运动耳机和相机产品外,它还可以被广泛应用于智能穿戴设备、便携式医疗设备、无人机等领域。在这些领域中,低功耗和高效能成为了不可或缺的关键因素,而创世8GB SD NAND芯片正好满足了这些需求。   结语   综上所述,CS创世8GB SD NAND芯片以其卓越的低功耗特性、小巧的设计以及高性能表现,在电子设备领域展现出了强大的竞争力。它不仅延长了设备的续航时间,降低了对电池的依赖,还提升了数据传输的效率和稳定性。随着科技的不断进步和市场需求的日益多样化,CS创世8GB SD NAND芯片有望在未来发挥更加重要的作用,为电子设备的续航与性能提升贡献更多的力量。在这个追求高效、节能的时代里,CS创世8GB SD NAND芯片无疑将成为推动行业发展的重要力量之一。
  • 热度 1
    2024-7-24 18:14
    173 次阅读|
    0 个评论
    目录 前言: 简介: 对照: 测试: 使用: 照片存储: 基于卷积神经网络的数字识别: ———————————————— 前言: 感谢深圳雷龙公司寄送的样品,其中包括两张2代的4gbit和32gbit的SD NAND FLASH芯片以及一份测试板卡。 简介: 根据官方文档的描述,这款芯片采用LGA-8封装,具有标准SDIO接口,并同时兼容SPI和SD接口。因此,可以直接移植标准驱动代码,支持使用SD NAND FLASH的SOC也可以用于TF卡启动。 以下是该芯片的主要参数(以CSNP32GCR01-BOW手册为准): 接口:符合标准SD Specification Version 2.0规范,包括1-I/O和4-I/O两种模式。 默认模式:在默认模式下,时钟频率可变范围为0-25 MHz,接口速度高达12.5 MB/sec(使用4条并行数据线路)。 高速模式:在高速模式下,时钟频率可变范围为0-50 MHz,接口速度高达25 MB/sec(使用4条并行数据线路)。 对照: 下面是SD NAND芯片和传统TF卡的一些对比: 目前,一些树莓派和一些国产的微处理器经常通过SD卡进行系统的移植,但一些设计不合理的卡槽经常不能保护SD卡,反而会损坏折断。相比之下,SD NAND可以通过贴片直接嵌入嵌入式设备中,更适合嵌入式环境的开发。同时,裸露的SD卡槽和松动的SD卡时常会影响系统的稳定性,因此一个可以反复擦拭的稳定存储芯片显得十分重要。 通过将测试板和芯片进行简单的焊接,我们可以像使用SD卡一样对SD NAND FLASH进行测试。 测试: 首先,我们使用CrystalDiskMark 8.0.4c对这款储存器进行了测试: 本次测试的是512MB的容量的产品,容量是真实的。我们可以看出,在包括顺序读取、顺序写入、随机读取和随机写入的四个测试方式下,SD NAND取得了不错的测试结果,接近官方数据,可以成功进行高速存储。 使用: 此外,我们还利用k210与SD NAND进行了照片的存储和基于卷积神经网络的数字识别。 1.照片存储: 通过向SD NAND内烧录micropython代码,实现了k210对照片的拍摄和存储。存储速度非常快。 import sensor, lcd from Maix import GPIO from fpioa_manager import fm from board import board_info import os, sys import time import image #### image size #### set_windowing = (224, 224) #### sensor config #### sensor.reset(freq=22000000, dual_buff=False) sensor.set_pixformat(sensor.RGB565) sensor.set_framesize(sensor.QVGA) # 320x240 try: sensor.set_jb_quality(95) # for IDE display quality except Exception: pass # no IDE support if set_windowing: sensor.set_windowing(set_windowing) # sensor.set_auto_gain(False) # sensor.set_auto_whitebal(False, rgb_gain_db=(0x52,0x40,0x4d)) # sensor.set_saturation(0) # sensor.set_brightness(4) # sensor.set_contrast(0) # sensor.set_hmirror(True) # image horizonal mirror # sensor.set_vflip(True) # image vertical flip # sensor.set_auto_whitebal(False) sensor.skip_frames() #### lcd config #### lcd.init(type=1, freq=15000000) lcd.rotation(2) #### boot key #### boot_pin = 16 # board_info.BOOT_KEY fm.register(boot_pin, fm.fpioa.GPIOHS0) key = GPIO(GPIO.GPIOHS0, GPIO.PULL_UP) ###################################################### #### main #### def capture_main(key): def draw_string(img, x, y, text, color, scale, bg=None , full_w = False): if bg: if full_w: full_w = img.width() else: full_w = len(text)*8*scale+4 img.draw_rectangle(x-2,y-2, full_w, 16*scale, fill=True, color=bg) img = img.draw_string(x, y, text, color=color,scale=scale) return img def del_all_images(): os.chdir("/sd") images_dir = "cap_images" if images_dir in os.listdir(): os.chdir(images_dir) types = os.listdir() for t in types: os.chdir(t) files = os.listdir() for f in files: os.remove(f) os.chdir("..") os.rmdir(t) os.chdir("..") os.rmdir(images_dir) # del_all_images() os.chdir("/sd") dirs = os.listdir() images_dir = "cap_images" last_dir = 0 for d in dirs: if d.startswith(images_dir): 11: n = int(d ) last_dir: last_dir = n images_dir = "{}_{}".format(images_dir, last_dir+1) print("save to ", images_dir) if images_dir in os.listdir(): img = image.Image() img = draw_string(img, 2, 200, "please del cap_images dir", color=lcd.WHITE,scale=1, bg=lcd.RED) lcd.display(img) sys.exit(1) os.mkdir(images_dir) last_cap_time = 0 last_btn_status = 1 save_dir = 0 save_count = 0 os.mkdir("{}/{}".format(images_dir, save_dir)) while(True): img0 = sensor.snapshot() if set_windowing: img = image.Image() img = img.draw_image(img0, (img.width() - set_windowing )//2, img.height() - set_windowing ) else: img = img0.copy() # img = img.resize(320, 240) if key.value() == 0: time.sleep_ms(30) 500): last_btn_status = 0 last_cap_time = time.ticks_ms() else: 5000: img = draw_string(img, 2, 200, "release to change type", color=lcd.WHITE,scale=1, bg=lcd.RED) else: img = draw_string(img, 2, 200, "release to capture", color=lcd.WHITE,scale=1, bg=lcd.RED) 2000: img = draw_string(img, 2, 160, "keep push to change type", color=lcd.WHITE,scale=1, bg=lcd.RED) else: time.sleep_ms(30) if key.value() == 1 and (last_btn_status == 0): 5000: img = draw_string(img, 2, 200, "change object type", color=lcd.WHITE,scale=1, bg=lcd.RED) lcd.display(img) time.sleep_ms(1000) save_dir += 1 save_count = 0 dir_name = "{}/{}".format(images_dir, save_dir) os.mkdir(dir_name) else: draw_string(img, 2, 200, "capture image {}".format(save_count), color=lcd.WHITE,scale=1, bg=lcd.RED) lcd.display(img) f_name = "{}/{}/{}.jpg".format(images_dir, save_dir, save_count) img0.save(f_name, quality=95) save_count += 1 last_btn_status = 1 img = draw_string(img, 2, 0, "will save to {}/{}/{}.jpg".format(images_dir, save_dir, save_count), color=lcd.WHITE,scale=1, bg=lcd.RED, full_w=True) lcd.display(img) del img del img0 def main(): try: capture_main(key) except Exception as e: print("error:", e) import uio s = uio.StringIO() sys.print_exception(e, s) s = s.getvalue() img = image.Image() img.draw_string(0, 0, s) lcd.display(img) main() 2.基于卷积神经网络的数字识别: 我们向SD NAND内烧录了功能代码、模型参数和模型结构。SD NAND可以很好地存储以上内容,并通过k210正确加载模型。在使用过程中,SD NAND表现出了出色的稳定性,没有出现崩溃或弹出的情况。 # generated by maixhub, tested on maixpy3 v0.4.8 # copy files to TF card and plug into board and power on import sensor, image, lcd, time import KPU as kpu import gc, sys input_size = (224, 224) labels = anchors = def lcd_show_except(e): import uio err_str = uio.StringIO() sys.print_exception(e, err_str) err_str = err_str.getvalue() img = image.Image(size=input_size) img.draw_string(0, 10, err_str, scale=1, color=(0xff,0x00,0x00)) lcd.display(img) def main(anchors, labels = None, model_addr="/sd/m.kmodel", sensor_window=input_size, lcd_rotation=0, sensor_hmirror=False, sensor_vflip=False): sensor.reset() sensor.set_pixformat(sensor.RGB565) sensor.set_framesize(sensor.QVGA) sensor.set_windowing(sensor_window) sensor.set_vflip(1) sensor.run(1) lcd.init(type=1) lcd.rotation(lcd_rotation) lcd.clear(lcd.WHITE) if not labels: with open('labels.txt','r') as f: exec(f.read()) if not labels: print("no labels.txt") img = image.Image(size=(320, 240)) img.draw_string(90, 110, "no labels.txt", color=(255, 0, 0), scale=2) lcd.display(img) return 1 try: img = image.Image("startup.jpg") lcd.display(img) except Exception: img = image.Image(size=(320, 240)) img.draw_string(90, 110, "loading model...", color=(255, 255, 255), scale=2) lcd.display(img) try: task = None task = kpu.load(model_addr) kpu.init_yolo2(task, 0.5, 0.3, 5, anchors) # threshold: , nms_value: while(True): img = sensor.snapshot() t = time.ticks_ms() objects = kpu.run_yolo2(task, img) t = time.ticks_ms() - t if objects: for obj in objects: pos = obj.rect() img.draw_rectangle(pos) img.draw_string(pos , pos , "%s : %.2f" %(labels , obj.value()), scale=2, color=(255, 0, 0)) img.draw_string(0, 200, "t:%dms" %(t), scale=2, color=(255, 0, 0)) lcd.display(img) except Exception as e: raise e finally: if not task is None: kpu.deinit(task) if __name__ == "__main__": try: # main(anchors = anchors, labels=labels, model_addr=0x300000, lcd_rotation=0) main(anchors = anchors, labels=labels, model_addr="/sd/model-54796.kmodel") except Exception as e: sys.print_exception(e) lcd_show_except(e) finally: gc.collect() 通过以上两个实验,SD NAND代替传统的SD/TF卡进行数据存储表现出了极大的优势和稳定性。
  • 热度 3
    2024-3-13 12:05
    601 次阅读|
    0 个评论
    文章目录 【嵌入式】基于FATFS/Littlefs文件系统的日志框架实现 1. 概述 2. 设计概要 3. 设计实现 3.1 初始化 `init` 3.2 日志写入 `write` 3.3 日志读取 `read` 3.4 注销 `deinit` 3.5 全部代码汇总 4. 测试 5. 总结 1. 概述 那么在移植好了文件系统之后,我们又应该如何应用文件系统呢? 很多人会说,这个简单,就操作文件嘛!open、read、write、close不就行了吗!当然对于简单的使用,掌握open、read、write、close,去存储一两个文件或者从一两个文件中简单的读取下数据这确实没有什么难度。但在实际应用中,特别是产品开发过程中,往往不只是简单的操作一两个文件就可以的,如果真是这样,那费那么大劲移植文件系统多少有点浪费! 在实际项目开发中,往往需要依托文件系统操作诸多的文件,操作诸多的数据。如通过配置文件配置机器设备信息、通过升级文件进行产品升级、通过存放字库文件实现多语言支持等等,这些都是比较简单的操作,读写不是很频繁,相对来说实现比较简单,还有一类需求读写会相当频繁,且大多数产品内都希望存在的,那便是日志文件,通过日志文件来记录设备的运行数据。日志文件不同于其他功能,其往往需要具备几个基本特性需求: 单个文件大小限制 日志总大小空间占用限制 自动循环覆盖 网上也有一些开源的日志框架,如 Log4j,不过大都是基于 java / c ++ 实现的,虽然功能比较全面,但比较繁杂,且也难以移植应用于嵌入式开发中。而在嵌入式开发中,可能也受限于资源限制,并没有发现不错的基于文件系统的开源日志框架(至少博主目前没有发现,有的话欢迎大家评论区讨论 ),对于如何实现一个日志框架很多人一下子可能没有头绪,综上,本文将分享一个简单的基于文件系统的日志程序以供大家思考。 2. 设计概要 我们需要实现的日志模块的核心需求为: 单个文件大小限制 日志总大小空间占用限制 自动循环覆盖 对于一个模块,对外仅需提供其操作的接口即可,内部的算法实现均无需对外开放,而对于此日志模块,对外只需提供基本的以下四个接口即可: 初始化 init 写日志 write 读日志 read 注销 deinit 关于日志存储的核心思想如下: 写数据之前先判断当前操作的文件是否超出单个文件大小限制,如超出大小限制则进行日志轮转,创建一个新的日志文件并判断日志文件总大小是否超出限制,如果超出则删除最早的那一份日志文件 关于日志存储的详细设计如下: 日志文件格式采用: .log ,当当前文件达到单个文件大小之后,进行文件轮转; 假定当前限制日志每个日志文件大小为2048Byte,最多存储10个文件; 当当前文件达到单个文件大小之后,迭代修改日志文件名: .log.0 .log.1 .log.2 … .log.9 删除 .log.9 ps:注意实际代码操作的时候,文件修改顺序是反过来的,也就是先 删除 . log .9 再将 . log .8 . log .9 3. 设计实现 3.1 初始化 init 初始化部分代码主要功能是完成日志数据结构体的构造,并通过传入参数 log_file_cfg_t cfg 配置日志文件的配置信息,如单个日志文件大小、日志文件名、最多存放的日志文件数等内容,日志模块初始化部分代码如下: log_file_t log_storage_init(log_file_cfg_t cfg) { log_file_t log = NULL; log_file_cfg_t log_cfg = NULL; log_file_read_t log_read = NULL; log = (log_file_t)malloc(sizeof(struct log_file_config)); if (log == NULL) goto error; log_cfg = (log_file_cfg_t)malloc(sizeof(struct log_file_config)); if (log_cfg == NULL) { free(log); log = NULL; goto error; } log_read = (log_file_read_t)malloc(sizeof(struct log_file_read)); if (log_read == NULL) { free(log); log = NULL; free(log_cfg); log_cfg = NULL; goto error; } memcpy(log_cfg, cfg, sizeof(struct log_file_config)); rotate_index = 0; file_offset = 0; cfg = log_cfg; read = log_read; user_data = NULL; error: return log; } 3.2 日志写入 write 日志写入部分代码主要分为两大部分,一部分是正常写入,另一部分是文件轮转;当写入的文件超过单个文件大小限制时,即会触发文件轮转操作。 在文件轮转中,主要做的是:创建一个新的日志文件并判断日志文件总大小是否超出限制,如果超出则删除最早的那一份日志文件,具体设计细节可参考上文设计概要中的详细设计部分。 实现代码如下: static int log_rotate(log_file_t log) { int ret = 0; FILE *fp; char old_filename = {0}; char new_filename = {0}; 0; i --) { memset(old_filename, 0, sizeof(old_filename)); memset(new_filename, 0, sizeof(new_filename)); filename, i - 1); filename, i); printf("old:%s new:%s\n", old_filename, new_filename); if ((fp = fopen(new_filename, "r")) != NULL) { if (fclose(fp) != 0) { ret = -1; goto error; } if (remove(new_filename) != 0) { ret = -2; goto error; } } if ((fp = fopen(old_filename, "r")) != NULL) { if (fclose(fp) != 0) { ret = -1; goto error; } if (rename(old_filename, new_filename) != 0) { ret = -3; goto error; } } } error: return ret; } int log_storage_write(log_file_t log, const unsigned char *buf, unsigned int len) { int ret = 0; int file_size = 0; char full_filename = {0}; FILE *fp = NULL; read == NULL || buf == NULL || len == 0) { ret = -1; goto param_error; } filename); printf("fullfilename:%s\n", full_filename); log_file_lock(); fp = fopen(full_filename, "a+b"); if (fp == NULL) { ret = -2; goto error; } fseek(fp, 0L, SEEK_END); file_size = ftell(fp); printf("file_size:%d\n", file_size); max_size) { if (fclose(fp) != 0) { ret = -3; goto error; } int j = 0; j = log_rotate(log); printf("log rotate:%d\n", j); fp = fopen(full_filename, "a+b"); if (fp == NULL) { ret = -2; goto error; } } if (fwrite(buf, len, 1, fp) != 1) { fclose(fp); ret = -4; goto error; } error: if (fp != NULL) { if (fclose(fp) != 0) { ret = -3; goto error; } } log_file_unlock(); param_error: return ret; } 3.3 日志读取 read 此处日志读取在本文主题中非重点设计内容,因此此处做简单设计,通过传入参数判断应该读取哪一份文件之后进行直接读取。设计代码如下: int log_storage_read(log_file_t log, unsigned int rotate_num, unsigned char *buf, unsigned int *len) { int ret = 0; int file_size = 0; char full_filename = {0}; FILE *fp = NULL; read == NULL || buf == NULL || len == 0) { ret = -1; goto param_error; } if (rotate_num == 0) filename); else filename, rotate_num); log_file_lock(); fp = fopen(full_filename, "a+b"); if (fp == NULL) { ret = -2; goto error; } /* check file length. */ fseek(fp, 0L, SEEK_END); file_size = ftell(fp); printf("file_size:%d\n", file_size); if (file_size < *len) *len = file_size; fseek(fp, 0L, SEEK_SET); if (fread(buf, *len, 1, fp) != 1) { ret = -3; fclose(fp); goto error; } error: if (fp != NULL) { if (fclose(fp) != 0) { ret = -4; goto error; } } log_file_unlock(); param_error: return ret; } 3.4 注销 deinit 注销的主要功能是将我们在 init 时创建的数据结构进行回收,如果模块内部有功能处于打开装填,也应关闭模块的功能,此处我们仅需对 init 时创建的 log_file_t log 数据结构体进行注销、内存回收即可,具体代码实现如下: int log_storage_deinit(log_file_t log) { if (log == NULL) return -1; cfg != NULL) cfg); read != NULL) read); user_data != NULL) user_data); free(log); return 0; } 3.5 全部代码汇总 日志模块内核头文件: simple_storage.h #ifndef __SIMPLE_STORAGE_H__ #define __SIMPLE_STORAGE_H__ #define NAME_MAX 40 struct log_file_config { const char filename ; /* Filename of this type. */ int max_size; /* single file max size. */ int rotate_num; /* The number of files that support rotate. */ }; typedef struct log_file_config* log_file_cfg_t; struct log_file_read { int rotate_index; /* The rotate file index. */ int file_offset; /* The offset of the currently read file. */ }; typedef struct log_file_read* log_file_read_t; struct log_file { log_file_cfg_t cfg; log_file_read_t read; void *user_data; }; typedef struct log_file* log_file_t; log_file_t log_storage_init(log_file_cfg_t cfg); int log_storage_write(log_file_t log, const unsigned char *buf, unsigned int len); int log_storage_read(log_file_t log, unsigned int rotate_num, unsigned char *buf, unsigned int *len); int log_storage_deinit(log_file_t log); #endif /* __SIMPLE_STORAGE_H__ */ 日志模块内核文件: simple_storage.c #include "simple_storage.h" #include "simple_storage_port.h" #include #include log_file_t log_storage_init(log_file_cfg_t cfg) { log_file_t log = NULL; log_file_cfg_t log_cfg = NULL; log_file_read_t log_read = NULL; log = (log_file_t)malloc(sizeof(struct log_file_config)); if (log == NULL) goto error; log_cfg = (log_file_cfg_t)malloc(sizeof(struct log_file_config)); if (log_cfg == NULL) { free(log); log = NULL; goto error; } log_read = (log_file_read_t)malloc(sizeof(struct log_file_read)); if (log_read == NULL) { free(log); log = NULL; free(log_cfg); log_cfg = NULL; goto error; } memcpy(log_cfg, cfg, sizeof(struct log_file_config)); rotate_index = 0; file_offset = 0; cfg = log_cfg; read = log_read; user_data = NULL; error: return log; } static int log_rotate(log_file_t log) { int ret = 0; FILE *fp; char old_filename = {0}; char new_filename = {0}; 0; i --) { memset(old_filename, 0, sizeof(old_filename)); memset(new_filename, 0, sizeof(new_filename)); filename, i - 1); filename, i); printf("old:%s new:%s\n", old_filename, new_filename); if ((fp = fopen(new_filename, "r")) != NULL) { if (fclose(fp) != 0) { ret = -1; goto error; } if (remove(new_filename) != 0) { ret = -2; goto error; } } if ((fp = fopen(old_filename, "r")) != NULL) { if (fclose(fp) != 0) { ret = -1; goto error; } if (rename(old_filename, new_filename) != 0) { ret = -3; goto error; } } } error: return ret; } int log_storage_write(log_file_t log, const unsigned char *buf, unsigned int len) { int ret = 0; int file_size = 0; char full_filename = {0}; FILE *fp = NULL; read == NULL || buf == NULL || len == 0) { ret = -1; goto param_error; } filename); printf("fullfilename:%s\n", full_filename); log_file_lock(); fp = fopen(full_filename, "a+b"); if (fp == NULL) { ret = -2; goto error; } fseek(fp, 0L, SEEK_END); file_size = ftell(fp); printf("file_size:%d\n", file_size); max_size) { if (fclose(fp) != 0) { ret = -3; goto error; } int j = 0; j = log_rotate(log); printf("log rotate:%d\n", j); fp = fopen(full_filename, "a+b"); if (fp == NULL) { ret = -2; goto error; } } if (fwrite(buf, len, 1, fp) != 1) { fclose(fp); ret = -4; goto error; } error: if (fp != NULL) { if (fclose(fp) != 0) { //TODO: check the amount of disk space, delete if there is not enough space. ret = -3; goto error; } } log_file_unlock(); param_error: return ret; } int log_storage_read(log_file_t log, unsigned int rotate_num, unsigned char *buf, unsigned int *len) { int ret = 0; int file_size = 0; char full_filename = {0}; FILE *fp = NULL; read == NULL || buf == NULL || len == 0) { ret = -1; goto param_error; } if (rotate_num == 0) filename); else filename, rotate_num); log_file_lock(); fp = fopen(full_filename, "a+b"); if (fp == NULL) { ret = -2; goto error; } /* check file length. */ fseek(fp, 0L, SEEK_END); file_size = ftell(fp); printf("file_size:%d\n", file_size); if (file_size < *len) *len = file_size; fseek(fp, 0L, SEEK_SET); if (fread(buf, *len, 1, fp) != 1) { ret = -3; fclose(fp); goto error; } error: if (fp != NULL) { if (fclose(fp) != 0) { ret = -4; goto error; } } log_file_unlock(); param_error: return ret; } int log_storage_deinit(log_file_t log) { if (log == NULL) return -1; cfg != NULL) cfg); read != NULL) read); user_data != NULL) user_data); free(log); return 0; } 在日志模块源文件的代码中,我们可以看到实际每次操作文件的时候,都有调用一个函数锁操作,考虑到不同平台的锁操作实现不一样,因此将此部分通过函数导出来,放置在模块的端口文件中。不同的平台、系统根据各自的平台和系统的情况进行实现,如像裸机编程这类不需要进行锁操作的不进行函数实现即可。 日志模块端口头文件: simple_storage_port.c #ifndef __SIMPLE_STORAGE_PORT_H__ #define __SIMPLE_STORAGE_PORT_H__ int log_file_init(void); int log_file_lock(void); int log_file_unlock(void); #endif /* __SIMPLE_STORAGE_PORT_H__ */ 日志模块端口源文件: simple_storage_port.h #include "simple_storage_port.h" int log_file_init(void) { return 0; } int log_file_lock(void) { return 0; } int log_file_unlock(void) { return 0; } 4. 测试 将以上代码进行运行测试,硬件平台如下: 控制器: stm32f103vet6,野火指南者开发板 存储芯片: CS创世 SD nand,型号:CSNP4GCR01-AMW 文件系统: FATFS,注意此日志不受文件系统限制 操作系统: RT-Thread,此模块与操作系统无关,此处只是方便使用故自行移植了rtthread 应用层代码如下: int main(void) { /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_SDIO_SD_Init(); MX_USART1_UART_Init(); MX_FATFS_Init(); /* USER CODE BEGIN 2 */ struct log_file_config log_cfg = { .filename = "test", .max_size = 2048, .rotate_num = 10, }; log_file_t log = NULL; log = log_storage_init(&log_cfg); if (log == NULL) return; /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ unsigned char buf = {0}; int len = 0; while (1) { // ... 省略用户代码 /* 写入测试 */ for (int i = 0; i < 2048; i++) { log_storage_write(log, "hello world", sizeof("hello world")); rt_thread_mdelay(100); } /* 读取测试 */ len = sizeof(buf); memset(buf, 0, sizeof(buf)); log_storage_read(log, 1, buf, &len); for (int i = 0; i < len; i ++) rt_kprintf("%c", buf ); rt_thread_mdelay(1000); } } 测试结果如下: hello worldhello world hello world hello world hello world hello world hello world hello world hello world ...省略 ls test.log 2046 test.log.0 2046 test.log.1 2046 test.log.2 2046 test.log.3 2046 test.log.4 2046 5. 总结 综上便是基于文件系统的简易日志模块设计的全部内容了,虽然简陋了点,但相信对于大部分没有接触过日志系统设计的人来说提供了很好的一条设计思路。 也正因为简易,给大家对于日志系统设计的优化留足了大量的优化空间。比如: 文件轮转的时候需要对每个文件的文件名进行修改,是否可以有更好的方式不用每个文件都修改呢? 文件名的设计是不方便阅读的,是否可以引入时间参数? 文件名设计如何引入了时间参数,当设备RTC备用电池掉电的时候又如何保证文件不会被错误覆盖? 文件的读取显然优化空间更大,实际上用户不应该传入rotate_num 参数,因为这是模块内部的参数,用户不可感知的 文件读取如何做到分多次读取一个文件的内容,且不会重复,是顺序读取? 等等,以上只是我简单想到的几点内容,大家不妨思考下如何实现方案更好呢?当然又还有哪些需求是需要引入的呢,也欢迎大家在评论区留言,关注我,后续抽时间再分享下改良版日志系统!!!
  • 热度 3
    2024-1-5 18:08
    362 次阅读|
    0 个评论
    前言   大家好,我们一般在STM32项目开发中或者在其他嵌入式开发中,经常会用到存储芯片存储数据。今天我和大家来介绍一款存储芯片,我这里采用(雷龙) CS创世 SD NAND 。   SD NAND介绍   什么是SD NAND?它俗称贴片式T卡,贴片式TF卡,贴片式SD卡,贴片式内存卡,贴片式闪存卡,贴片式卡...等等。虽然SD NAND 和TF卡称呼上有些类似,但是SD NAND和TF卡有着本质上的区别。   SD NAND 与 TF卡的区别:(看图表)   SD和TF区别   LGA-8封装   什么是LGA-8封装?   LGA-8封装是一种将芯片引脚通过电路板的层间连接到器件底部的封装形式,也称为Land Grid Array封装。与其他封装形式不同,LGA-8封装没有凸出的引脚,而是在芯片的底部设计了一些小的焊盘,用于与电路板焊接连接。   LGA-8封装的由来与SDNAND芯片其实并没有直接关系,但它提供了更好的电性、机械性和热性能,因而被广泛应用于存储芯片、微处理器、芯片组等器件中。   在SDNAND芯片的应用中,LGA-8封装的优点包括:   更好的电性能:LGA-8封装底部的焊盘与电路板连接时可提供更好的电性能,减少因接插件导致的信号损失,实现更高速率和更远距离的数据传输。   更好的机械性能:LGA-8封装通过焊接连接,可以获得更高的机械强度和稳定性。同时,由于没有凸出的引脚,LGA-8封装使得SDNAND芯片的外形更加紧凑,可适应更小尺寸的应用场景,提高产品设计自由度。   更好的热性能:焊接连接可提供更好的热传递性能,减少因热量集中导致的器件失效或故障。   因此,LGA-8封装的设计为SDNAND芯片的应用提供了更好的技术支持,更好的性能表现和更广泛的应用领域。   产品介绍   CSNP4GCR01-AMW   不用写驱动程序自带坏块管理的NAND Flash(贴片式TF卡),尺寸小巧,简单易用,兼容性强,稳定可靠,固件可定制,LGA-8封装,标准SDIO接口,兼容SPI/SD接口,兼容各大MCU平台,可替代普通TF卡/SD卡,尺寸6x8mm毫米,内置SLC晶圆擦写寿命10万次,通过1万次随机掉电测试耐高低温,支持工业级温度-40°~+85°,机贴手贴都非常方便,速度级别Class10(读取速度23.5MB/S写入速度12.3MB/S)标准的SD 2.0协议使得用户可以直接移植标准驱动代码,省去了驱动代码编程环节。支持TF卡启动的SOC都可以用SD NAND,提供STM32参考例程及原厂技术支持,主流容量:128MB/512MB/2GB/4GB/8GB,比TF卡稳定,比eMMC便宜,样品免费试用。   CSNP64GCR01-AOW   不用写驱动程序自带坏块管理的NAND Flash(贴片式TF卡),尺寸小巧,简单易用,兼容性强,稳定可靠,固件可定制,LGA-8封装,标准SDIO接口,兼容SPI,兼容拔插式TF卡/SD卡,可替代普通TF卡/SD卡,尺寸7x8.5mm,内置平均读写算法,通过1万次随机掉电测试,耐高低温,机贴手贴都非常方便,速度级别Class10(读取速度23.5MB/S写入速度12.3MB/S)标准的SD 2.0协议使得用户可以直接移植标准驱动代码,省去了驱动代码编程环节。支持TF卡启动的SOC都可以用SD NAND,提供STM32参考例程及原厂技术支持,容量:8GB,比TF卡稳定,比eMMC易用,样品免费试用。   SD NAND和eMMC的区别   SD NAND和eMMC都是存储设备,但它们有一些不同之处:   SD NAND优点   SD NAND是一种基于NAND闪存技术的存储设备,与其他存储设备相比,它具有以下几个显著的优点:   高可靠性:SD NAND针对嵌入式系统的特殊需求进行了设计,具有更高的可靠性。它内置了闪存控制器和NAND闪存芯片,支持 ECC (Error Correcting Code) 算法和坏块管理,能有效地降低数据丢失的风险。   高速读写:SD NAND的读写速度比硬盘慢,但通常比SD卡和SPI闪存等其他存储设备更快。在使用SDIO接口时,SD NAND的速度更高。其读取时延较低,比 SD 卡等其他媒体被访问时更加实时。   内置控制器:SD NAND内置了控制器,不必额外添置控制器,可以实现单芯片解决方案,降低了芯片的成本和复杂度。   低功耗:SD NAND使用了新型的SD ver5.1总线模式,并支持DDR传输。除 SDIO 接口外,还支持 SPI 接口,并且具有低功耗特点。它可以在连接到其它设备时不占用过量的功率。   高度集成:SD NAND可实现高度集成。闪存、控制器、内部SDRAM可以打在一片芯片上。   基于这些优点,SD NAND适用于许多嵌入式应用,如智能家居、工业控制、移动设备等。在这些应用中, SD NAND能提供高可靠性、高速读写、低功耗和高度集成等特点, 为新一代的嵌入式存储解决方案引领市场趋势。   总结   对这款产品优点总结为一下几点,其尺寸小巧,可以达到6*8mm,简单易用而且稳定可靠。 ———————————————— 【本文转载自CSDN,作者:爱吃饼干的小白鼠】 深圳市雷龙发展专注存储行业13年,专业提供小容量存储解决方案。