FLASH 模拟 EEPROM 实验

news2025/2/22 12:39:13

STM32 本身没有自带 EEPROM,但是 STM32 具有 IAP(在应用编程)功能,所以我们可 以把它的 FLASH 当成 EEPROM 来使用。本章,我们将利用 STM32 内部的 FLASH 来实现NOR FLASH(EEPROM)(实验类似的效果,不过这次我们是将数据直接存放在 STM32 内部,而不是存放在 NOR FLASH。

STM32 FLASH 简介

不同型号的 STM32,其 FLASH 容量也有所不同,最小的只有 16K 字节,最大的则达到了 1024K 字节。我们的精英 STM32 开发板选择的是 STM32F103ZET6 的 FLASH 容量为 512K 字 节,属于大容量产品,大容量产品的闪存模块组织如表 43.1.1 所示:STM32 的闪存模块由主存储器、信息块和闪存存储器接口寄存器等 3 部分组成。

主存储器,该部分用来存放代码和数据常数(如 const 类型的数据)。对于大容量产品,其 被划分为 256 页,每一页 2K 字节(注意,小容量和中容量产品则每页只有 1K 字节)。从上图可 以看出主存储器的起始地址就是 0x08000000,B0、B1 都接 GND 的时候,就是从 0x08000000 开始运行代码的。

信息块,该部分分为 2 个小部分,其中启动程序代码,用来存储 ST 自带的启动程序,用来 串口下载代码,当 B0 接 3V3,B1 接 GND 的时候,运行的就是这部分代码。用户选中字节, 则一般用于配置写保护、读保护等功能,本章不作介绍了。

闪存存储器接口寄存器,该部分用于控制闪存读写等,是整个闪存模块的控制结构。

对主存储器和信息块的写入由内嵌的闪存编程/擦除控制器(FPEC)管理;编程与擦除的高 电压由内部产生。

在执行闪存写操作时,任何对闪存的读操作都会锁住总线,在写操作完成后读操作才能正确地进行。既在进行写或擦除操作时,不能进行代码或数据的读取操作。

闪存的读取

内置闪存模块可以在通用地址空间直接寻址,任何 32 位数据的读操作都能访问闪存模块 的内容并得到相应的数据。读接口在闪存端包含一个读控制器,还包含一个 AHB 接口与 CPU 衔接。这个接口的主要工作是产生读内存的控制信号并预取 CPU 要求的指令块,预取指令块仅 用于在 I-Code 总线上的取指操作,数据常量是通过 D-Code 总线访问的。这两条总线的访问目 标是相同的闪存模块,访问 D-Code 将比预取指令优先级高。

这里要特别留意一个闪存等待时间,因为 CPU 运行速度比 FLASH 快得多,STM32F103 的 FLASH 最快访问速度≤24Mhz,如果 CPU 频率超过这个速度,那么必须加入等待时间,比如 我们一般使用 72Mhz 的主频,那么 FLASH 等待周期就必须设置为 2,该设置通过 FLASH_ACR 寄存器设置。

例如,我们要从地址 addr,读取一个半字(半字为 16 位,字为 32 位),可以通过如下的语 句读取:data = *(vu16*)addr;

将 addr 强制转换为 vu16 指针,然后取该指针所指向的地址的值,即得到了 addr 地址的值。 类似的,将上面的 vu16 改为 vu8,即可读取指定地址的一个字节。相对 FLASH 读取来说, STM32 FLASH 的写就复杂一点了。下面我们介绍 STM32 闪存的编程和擦除。

闪存的编程和擦除

STM32 的闪存编程是由 FPEC(闪存编程和擦除控制器)模块处理的,这个模块包含 7 个 32 位寄存器,它们分别是:

⚫ FPEC 键寄存器(FLASH_KEYR)

⚫ 选择字节键寄存器(FLASH_OPTKEYR)

⚫ 闪存控制寄存器(FLASH_CR)

⚫ 闪存状态寄存器(FLASH_SR)

⚫ 闪存地址寄存器(FLASH_AR)

⚫ 选择字节寄存器(FLASH_WRPR)

其中 FPEC 键寄存器总共有 3 个键值:

RDPRT 键 = 0X0000 00A5

KEY1 = 0X4567 0123

KEY2 = 0XCDEF 89AB

STM32 复位后,FPEC 模块是被保护的,不能写入 FLASH_CR 寄存器;通过写入特定的序 列到 FLASH_KEYR 寄存器可以打开 FPEC 模块(即写入 KEY1 和 KEY2),只有在写保护被解 除后,我们才能操作相关寄存器。

STM32 闪存的编程每次必须写入 16 位(不能单纯的写入 8 位数据),当 FLASH_CR 寄存 器的 PG 位为‘1’时,在一个闪存地址写入一个半字将启动一次编程;写入任何非半字的数据, FPEC 都会产生总线错误。在编程过程中(BSY 位为’1’),任何读写内存的操作都会使 CPU 暂停,直到此次闪存编程结束。

同样,STM32 的 FLASH 在编程的时候,也必须要求其写入地址的 FLASH 是被擦除了的 (其值必须是 0xFFFF),否则无法写入,在 FLASH_SR 寄存器的 PGERR 位将得到一个警告。

STM32 的 FLASH 编程过程如图 43.1.2.1 所示:

从上图可以得到闪存的编程顺序如下:

1)检查 FLASH_CR 的 LOCK 是否解锁,如果没有则先解锁 

2)检查 FLASH_SR 寄存器的 BSY 位,以确认没有其他正在进行的编程操作

3)设置 FLASH_CR 寄存器的 PG 位为‘1’

4)在指定的地址写入要编程的半字

5)等待 BSY 位变为‘0’

6)读出写入地址并验证数据

前面提到,我们在 STM32 的 FLASH 编程的时候,要先判断缩写地址是否被擦出了,所以, 我们有必要再介绍一下 STM32 的闪存擦除,STM32 的闪存擦除分为两种:页擦除和整片擦除。 页擦除过程如图 43.1.2.2 所示:从上图可以看出,STM32 的页擦除顺序为:

1)检查 FLASH_CR 和 LOCK 是否解锁,如果没有则先解锁

2)检查 FLASH_SR 寄存器的 BSY 位,以确认没有其他正在进行的闪存操作

3)设置 FLASH_CR 寄存器的 PER 位为‘1’

4)用 FLASH_AR 寄存器选择要擦除的页

5)设置 FLASH_CR 寄存器的 STRT 位为‘1’

6)等待 BSY 位变为‘0’

7)读出被擦除的页并做验证

本章我们只用到了 STM32 页擦除功能,整片擦除功能我们在这里就不介绍了。

HAL库函数介绍

代码

#ifndef __STMFLASH_H
#define __STMFLASH_H

#include "./SYSTEM/sys/sys.h"

/* FLASH起始地址 */
#define STM32_FLASH_SIZE        0x80000         /* STM32 FLASH 总大小 */
#define STM32_FLASH_BASE        0x08000000      /* STM32 FLASH 起始地址 */

 /* STM32F103 扇区大小 */
#if STM32_FLASH_SIZE < 256 * 1024
#define STM32_SECTOR_SIZE   1024                /* 容量小于256K的 F103, 扇区大小为1K字节 */
#else
#define STM32_SECTOR_SIZE   2048                /* 容量大于等于于256K的 F103, 扇区大小为2K字节 */
#endif

/* FLASH解锁键值 */
#define STM32_FLASH_KEY1        0X45670123
#define STM32_FLASH_KEY2        0XCDEF89AB

/* 静态函数(仅限stmflash.c调用) */
static void stmflash_unlock(void);                  /* 解锁STM32 内部FLASH */
static void stmflash_lock(void);                    /* 锁定STM32 内部FLASH */
static uint8_t stmflash_get_error_status(void);     /* 获取FLASH错误状态 */
static uint8_t stmflash_wait_done(uint32_t time);   /* 等待操作完成 */
static uint8_t stmflash_erase_sector(uint32_t saddr);                   /* 擦除扇区 */
static uint8_t stmflash_write_halfword(uint32_t faddr, uint16_t data);  /* FLASH写半字 */

/* 接口函数(外部可调用) */
uint16_t stmflash_read_halfword(uint32_t faddr);                        /* FLASH读半字 */
void stmflash_read(uint32_t raddr, uint16_t *pbuf, uint16_t length);    /* 从指定地址开始读出指定长度的数据 */
void stmflash_write(uint32_t waddr, uint16_t *pbuf, uint16_t length);   /* 在FLASH 指定位置, 写入指定长度的数据(自动擦除) */

#endif

STM32_FLASH_BASE 和 STM32_FLASH_SIZE 分别是 FLASH 的起始地址和 FLASH 总大 小,这两个宏定义随着芯片是固定的,我们开发板 F103 芯片的 FLASH 是 512K 字节,所以 STM32_FLASH_SIZE 宏定义值为 0x80000。

#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/STMFLASH/stmflash.h"

/**
 * @brief       从指定地址读取一个半字 (16位数据)
 * @param       faddr   : 读取地址 (此地址必须为2的倍数!!)
 * @retval      读取到的数据 (16位)
 */
uint16_t stmflash_read_halfword(uint32_t faddr)
{
    return *(volatile uint16_t *)faddr;
}

/**
 * @brief       从指定地址开始读出指定长度的数据
 * @param       raddr : 起始地址
 * @param       pbuf  : 数据指针
 * @param       length: 要读取的半字(16位)数,即2个字节的整数倍
 * @retval      无
 */
void stmflash_read(uint32_t raddr, uint16_t *pbuf, uint16_t length)
{
    uint16_t i;

    for (i = 0; i < length; i++)
    {
        pbuf[i] = stmflash_read_halfword(raddr);/* 读取2个字节 */
        raddr += 2; /* 偏移2个字节 */
    }
}

/**
 * @brief       不检查的写入
                这个函数的假设已经把原来的扇区擦除过再写入
 * @param       waddr   : 起始地址 (此地址必须为2的倍数!!,否则写入出错!)
 * @param       pbuf    : 数据指针
 * @param       length  : 要写入的 半字(16位)数
 * @retval      无
 */
void stmflash_write_nocheck(uint32_t waddr, uint16_t *pbuf, uint16_t length)
{
    uint16_t i;
    for (i = 0; i < length; i++)
    {
        HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, waddr, pbuf[i]);
        waddr += 2; /* 指向下一个半字 */
    }
}

/**
 * @brief       在FLASH 指定位置, 写入指定长度的数据(自动擦除)
 *   @note      该函数往 STM32 内部 FLASH 指定位置写入指定长度的数据
 *              该函数会先检测要写入的扇区是否是空(全0XFFFF)的?, 如果
 *              不是, 则先擦除, 如果是, 则直接往扇区里面写入数据.
 *              数据长度不足扇区时,自动被回擦除前的数据
 * @param       waddr   : 起始地址 (此地址必须为2的倍数!!,否则写入出错!)
 * @param       pbuf    : 数据指针
 * @param       length  : 要写入的 半字(16位)数
 * @retval      无
 */
uint16_t g_flashbuf[STM32_SECTOR_SIZE / 2]; /* 最多是2K字节 */
void stmflash_write(uint32_t waddr, uint16_t *pbuf, uint16_t length)
{
    uint32_t secpos;        /* 扇区地址 */
    uint16_t secoff;        /* 扇区内偏移地址(16位字计算) */
    uint16_t secremain;     /* 扇区内剩余地址(16位字计算) */
    uint16_t i;
    uint32_t offaddr;       /* 去掉0X08000000后的地址 */
    FLASH_EraseInitTypeDef flash_eraseop;
    uint32_t erase_addr;    /* 擦除错误,这个值为发生错误的扇区地址 */

    if (waddr < STM32_FLASH_BASE || (waddr >= (STM32_FLASH_BASE + 1024 * STM32_FLASH_SIZE)))
    {
        return;     /* 非法地址 */
    }

    HAL_FLASH_Unlock();                         /* FLASH解锁 */

    offaddr = waddr - STM32_FLASH_BASE;         /* 实际偏移地址. */
    secpos = offaddr / STM32_SECTOR_SIZE;       /* 扇区地址  0~255 for STM32F103ZET6 */
    secoff = (offaddr % STM32_SECTOR_SIZE) / 2; /* 在扇区内的偏移(2个字节为基本单位.) */
    secremain = STM32_SECTOR_SIZE / 2 - secoff; /* 扇区剩余空间大小 */
    if (length <= secremain)
    {
        secremain = length; /* 不大于该扇区范围 */
    }

    while (1)
    {
        stmflash_read(secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE, g_flashbuf, STM32_SECTOR_SIZE / 2); /* 读出整个扇区的内容 */
        for (i = 0; i < secremain; i++)     /* 校验数据 */
        {
            if (g_flashbuf[secoff + i] != 0XFFFF)
            {
                break;      /* 需要擦除 */
            }
        }
        if (i < secremain)  /* 需要擦除 */
        { 
            flash_eraseop.TypeErase = FLASH_TYPEERASE_PAGES;        /* 选择页擦除 */
            flash_eraseop.Banks = FLASH_BANK_1;
            flash_eraseop.NbPages = 1;
            flash_eraseop.PageAddress = secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE;  /* 要擦除的扇区 */
            HAL_FLASHEx_Erase( &flash_eraseop, &erase_addr);

            for (i = 0; i < secremain; i++)                         /* 复制 */
            {
                g_flashbuf[i + secoff] = pbuf[i];
            }
            stmflash_write_nocheck(secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE, g_flashbuf, STM32_SECTOR_SIZE / 2); /* 写入整个扇区 */
        }
        else
        {
            stmflash_write_nocheck(waddr, pbuf, secremain);         /* 写已经擦除了的,直接写入扇区剩余区间. */
        }
        if (length == secremain)
        {
            break; /* 写入结束了 */
        }
        else       /* 写入未结束 */
        {
            secpos++;               /* 扇区地址增1 */
            secoff = 0;             /* 偏移位置为0 */
            pbuf += secremain;      /* 指针偏移 */
            waddr += secremain * 2; /* 写地址偏移(16位数据地址,需要*2) */
            length -= secremain;    /* 字节(16位)数递减 */
            if (length > (STM32_SECTOR_SIZE / 2))
            {
                secremain = STM32_SECTOR_SIZE / 2; /* 下一个扇区还是写不完 */
            }
            else
            {
                secremain = length; /* 下一个扇区可以写完了 */
            }
        }
    }

    HAL_FLASH_Lock();   /* 上锁 */
}

该函数用于在 STM32 的指定地址写入指定长度的数据。函数的实现基本类似 SPI 章节的 norflash_write 函数,不过该函数对于写入地址是有要求,必须保证以下两点:

1,写入地址必须是用户代码区以外的地址

2,写入地址必须是 2 的倍数。 

第 1 点比较好理解,如果把用户代码给擦了,可想而知你运行的程序可能就被废了,从而 很可能出现死机的情况。第 2 点则是 STM32 FLASH 的要求,每次必须写入 16 位,如果你写的 地址不是 2 的倍数,那么写入的数据,可能就不是写在你要写的地址了。

另外,该函数的 g_flashbuf 数组,也是根据所用 STM32 的 FLASH 容量来确定的,精英 STM32 开发板的 FLASH 是 512K 字节,所以 STM_SECTOR_SIZE 的值为 2048,故该数组大小 为 2K 字节。

stmflash_write 函数实质是调用 stmflash_write_nocheck 函数进行实现,下面再来看一下 stmflash_write 函数代码。

该函数的实现依靠 flash 的 HAL 库驱动 HAL_FLASH_Program 进行实现。由于前面已经对 HAL_FLASH_Program 进行说明,这里就不作展开说明了。

接下来,讲解一下 STMFLASH 读相关的函数,写函数也有调用到读函数。

前面也提及到 STM32 对 FLASH 写入,其写入地址的值必须是 0xFFFFFFFF,所以读函数 主要是读取地址的值,以给写函数调用检验,确保能写入成功。读函数实现比较简单,这里就 不做展开了。

#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./BSP/STMFLASH/stmflash.h"

/* 要写入到STM32 FLASH的字符串数组 */
const uint8_t g_text_buf[] = {"STM32F FLASH TEST"};

#define TEXT_LENTH sizeof(g_text_buf) /* 数组长度 */

/*SIZE表示半字长(2字节), 大小必须是2的整数倍, 如果不是的话, 强制对齐到2的整数倍 */
#define SIZE TEXT_LENTH / 2 + ((TEXT_LENTH % 2) ? 1 : 0)

#define FLASH_SAVE_ADDR 0X08070000 /* 设置FLASH 保存地址(必须为偶数,且其值要大于本代码所占用FLASH的大小 + 0X08000000) */

int main(void)
{
    uint8_t key = 0;
    uint16_t i = 0;
    uint8_t datatemp[SIZE];

    HAL_Init();                         /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    delay_init(72);                     /* 延时初始化 */
    usart_init(115200);                 /* 串口初始化为115200 */
    led_init();                         /* 初始化LED */
    lcd_init();                         /* 初始化LCD */
    key_init();                         /* 初始化按键 */

    lcd_show_string(30,  50, 200, 16, 16, "STM32", RED);
    lcd_show_string(30,  70, 200, 16, 16, "FLASH EEPROM TEST", RED);
    lcd_show_string(30,  90, 200, 16, 16, "ATOM@ALIENTEK", RED);
    lcd_show_string(30, 110, 200, 16, 16, "KEY1:Write  KEY0:Read", RED);

    while (1)
    {
        key = key_scan(0);

        if (key == KEY1_PRES) /* KEY1按下,写入STM32 FLASH */
        {
            lcd_show_string(30, 150, 200, 16, 16, "Start Write FLASH....", RED);
            stmflash_write(FLASH_SAVE_ADDR, (uint16_t *)g_text_buf, SIZE);
            lcd_show_string(30, 150, 200, 16, 16, "FLASH Write Finished!", RED); /* 提示传送完成 */
        }

        if (key == KEY0_PRES) /* KEY0按下,读取字符串并显示 */
        {
            lcd_show_string(30, 150, 200, 16, 16, "Start Read FLASH.... ", RED);
            stmflash_read(FLASH_SAVE_ADDR, (uint16_t *)datatemp, SIZE);
            lcd_show_string(30, 150, 200, 16, 16, "The Data Readed Is:  ", RED); /* 提示传送完成 */
            lcd_show_string(30, 170, 200, 16, 16, (char *)datatemp, BLUE);       /* 显示读到的字符串 */
        }

        i++;
        delay_ms(10);

        if (i == 20)
        {
            LED0_TOGGLE(); /* 提示系统正在运行 */
            i = 0;
        }
    }
}

主函数代码逻辑比较简单,当检测到按键 KEY1 按下后往 FLASH 指定地址开始的连续地 址空间写入一段数据,当检测到按键 KEY0 按下后读取 FLASH 指定地址开始的连续空间数据。

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

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

相关文章

BigDecimal的使用全面总结

BigDecimal BigDecimal可以表示任意大小&#xff0c;任意精度的有符号十进制数。所以不用怕精度问题&#xff0c;也不用怕大小问题&#xff0c;放心使用就行了。就是要注意的是&#xff0c;使用的时候有一些注意点。还有就是要注意避免创建的时候存在精度问题&#xff0c;尤其…

【办公软件】电脑开机密码忘记了如何重置?

这个案例是家人的电脑&#xff0c;已经使用多年&#xff0c;又是有小孩操作过的&#xff0c;所以电脑密码根本不记得是什么了&#xff1f;那难道这台电脑就废了吗&#xff1f;需要重新装机吗&#xff1f;那里面的资料不是没有了&#xff1f; 为了解决以上问题&#xff0c;一般…

【深度学习】概率图模型(一)概率图模型理论简介

文章目录 一、概率图模型1. 联合概率表2. 条件独立性假设3. 三个基本问题 二、模型表示1. 有向图模型&#xff08;贝叶斯网络&#xff09;2. 无向图模型&#xff08;马尔可夫网络&#xff09; 三、学习四、推断 概率图模型&#xff08;Probabilistic Graphical Model&#xff0…

常见树种(贵州省):019滇白珠、杜茎山、苍山越桔、黄背越桔、贵州毛柃、半齿柃、钝叶柃、细枝柃、细齿叶柃木、土蜜树、山矾、胡颓子、檵木

摘要&#xff1a;本专栏树种介绍图片来源于PPBC中国植物图像库&#xff08;下附网址&#xff09;&#xff0c;本文整理仅做交流学习使用&#xff0c;同时便于查找&#xff0c;如有侵权请联系删除。 图片网址&#xff1a;PPBC中国植物图像库——最大的植物分类图片库 一、滇白珠…

如何回收利用将废弃电脑当监控摄像头用?或者...监视?

项目:https://github.com/MartinxMax/J0ker/releases/tag/V1.0 视频 J0ker说明 功能RTSP推流实时播放实时录屏实时直播 Windows平台 准备材料 一台废弃的64位Windows系统电脑,要求有摄像头 配置废弃电脑 我这里直接远程桌面连接过去了 启动RTSP服务 #J0ker.exe -server …

(1/2)敏捷实践指南 Agile Practice Guide ([美] Project Management institute 著)

电子工业出版社 Publishing House Of Electronics Industry 北京BeiJing 版次&#xff1a;2018年10月第1版 印次&#xff1a;2023年2月第22次印刷 定价&#xff1a;68元 声明 作为项目管理协会&#xff08;PMI&#xff09;的标准和指南&#xff0c;本指南是通过相关人员的…

什么是半监督学习

1 概述 1.1 定义 半监督学习&#xff08;Semi-Supervised Learning&#xff09;是机器学习中的一个重要分支&#xff0c;它介于监督学习和无监督学习之间。半监督学习利用少量标注数据和大量未标注数据共同训练模型&#xff0c;旨在充分挖掘未标注数据中潜在的信息和模式&…

【电路笔记】-快速了解电阻

快速了解电阻 文章目录 快速了解电阻1、概述2、电阻器的组成类型2.1 碳电阻器2.2 薄膜电阻器2.3 绕线电阻器 3、总结 电阻器是所有电子元件中最基本、最常用的元件&#xff0c;人们几乎认为电阻器是理所当然的&#xff0c;但它们在电路中起着至关重要的作用。 1、概述 有许多不…

Virsorter2-病毒组序列分析工具安装及使用20231126

在使用之前大家还是要好好了解一下文章介绍&#xff1a;VirSorter: mining viral signal from microbial genomic data [PeerJ] VirSorter2: a multi-classifier, expert-guided approach to detect diverse DNA and RNA viruses - PubMed Github访问正常的和英语功底还可以的…

『亚马逊云科技产品测评』活动征文|AWS 域名注册、启动与连接 EC2 新实例、端口开放详细教程

授权声明&#xff1a;本篇文章授权活动官方亚马逊云科技文章转发、改写权&#xff0c;包括不限于在 Developer Centre, 知乎&#xff0c;自媒体平台&#xff0c;第三方开发者媒体等亚马逊云科技官方渠道 目录 一、AWS 域名注册 二、AWS 域名解析 三、个人网站 ICP 备案 …

算法基础之合并集合

合并集合 核心思想:并查集: 1.将两个集合合并2.询问两个元素是否在一个集合当中 基本原理:每个集合用一棵树表示 树根的编号就是整个集合的编号 每个节点存储其父节点&#xff0c;p[x]表示x的父节点 #include<iostream>using namespace std;const int N100010;int p[N];…

IDEA出现cannot download sources解决方案

IDEA出现cannot download sources解决方案 问题描述 当我想看第三方库的源码的注释时需要下载源码。 点击Dodnload Sources后可能会出现cannot download sources的问题。 解决方案 这时我们只需在根目录下打开Terminal后执行下面一行代码 mvn dependency:resolve -Dclassi…

dm数据库的spool导出

我们经常使用oracle的spool功能导出csv或者txt文本&#xff0c;达梦数据库也有类似功能&#xff0c;且语法基本类似&#xff0c;现总结导出脚本如下&#xff1a; set pagesize 0 set trimspool on set linesize 500 set lineshow off set feedback off set verify off set ech…

Django之admin页面样式定制(Simpleui)

好久不见&#xff0c;各位it朋友们&#xff01; 本篇文章我将向各位介绍Django框架中admin后台页面样式定制的一个插件库&#xff0c;名为Simpleui。 一&#xff09;简介 SimpleUI是一款简单易用的用户界面&#xff08;UI&#xff09;库&#xff0c;旨在帮助开发人员快速构建…

SpringBoot进阶——解释springboot的自动配置原理

相关的博客文章如下&#xff1a; SpringBootApplication注解的理解——如何排除自动装配 & 分布式情况下如何自动加载 & nacos是怎么被发现的 引出 1.spring.factories文件存储能够进行自动配置的Bean信息&#xff1b; 2.EnableAutoConfiguration关闭数据源的自动配置…

.net 8 发布了,试下微软最近强推的MAUI

先看下实现的效果&#xff1a; 下面发下XAML文件&#xff1a; <?xml version"1.0" encoding"utf-8" ?> <ContentPage xmlns"http://schemas.microsoft.com/dotnet/2021/maui"xmlns:x"http://schemas.microsoft.com/winfx/2009/…

本地运行“李开复”的零一万物 34B 大模型

这篇文章&#xff0c;我们来聊聊如何本地运行最近争议颇多的&#xff0c;李开复带队的国产大模型&#xff1a;零一万物 34B。 写在前面 零一万物的模型争议有很多&#xff0c;不论是在海外的社交媒体平台&#xff0c;还是在国内的知乎和一种科技媒体上&#xff0c;不论是针对…

vue3通过v-model实现父子组件通信

单一值传递 父组件 <template><div ><h1>v-model实现父子组件通讯</h1><hr><child1 v-model"num"></child1><!-- 上下两个是等价的 --><child1 :modelValue"num" update:modelValue"handle&quo…

【Spring Boot】如何集成Swagger

Swagger简单介绍 Swagger是一个规范和完整的框架&#xff0c;用于生成、描述、调用和可视化RESTful风格的Web服务。功能主要包含以下几点&#xff1a; 可以使前后端分离开发更加方便&#xff0c;有利于团队协作接口文档可以在线自动生成&#xff0c;有利于降低后端开发人员编写…

springboot打印启动信息

打印启动信息 转载自:www.javaman.cn 1 spring Bean实例化流程 基本流程&#xff1a; 1、Spring容器在进行初始化时&#xff0c;会将xml或者annotation配置的bean的信息封装成一个BeanDefinition对象&#xff08;每一个bean标签或者bean注解都封装成一个BeanDefinition对象&a…