树莓派Pico与MicroSD卡模块接口及MicroPython编制读写MicroSD存储卡程序

news2024/10/6 12:29:32

介绍树莓派(RPi)Pico开发板(或树莓派Pico W无线开发板)与MicroSD卡模块SPI接口技术原理及SPI接口硬件连接的具体步骤,讲述采用MicroPython和SDCard类编制程序读写MicroSD存储卡的方法,给出读写MicroSD存储卡文件的测试程序。
一、所需硬件材料
1.树莓派Pico开发板(或树莓派Pico W无线开发板)×1
2.MicroSD卡模块×1
3.8GB MicroSD卡×1
4.MicroUSB数据线×1
5.面包板×1
6.公对母杜邦线×6
7.MicroSD读卡器(可选)
二、MicroSD卡模块
MicroSD(Micro Secure Digital flash memory)卡也叫TF(Trans Flash memory)卡。图1(a)是一种MicroSD卡模块外观图,该模块提供了SDIO接口方式和SPI接口方式。以SPI接口方式为例,MicroSD 卡模块包括1个用于插入MicroSD卡的卡槽和6根用以与MCU微控制器连接的SPI接口信号:包括MISO、MOSI、SCK 、CS 四根SPI接口引脚,以及VDD电源和GND接地引脚。图1(b)是一种8GB MicroSD存储卡外观图(实践时,可选用2GB~128GB 范围容量的MicroSD存储卡)。
在这里插入图片描述
(a) MicroSD卡模块及其引脚配置 (b) MicroSD存储卡
图1 MicroSD卡模块和MicroSD存储卡外观图
三、MicroSD卡模块与树莓派Pico接口
树莓派Pico(RPi Pico)开发板或树莓派Pico W(RPi Pico W)无线开发板扩展硬件接口提供了两个SPI接口–SPI0和SPI1,且SPI接口信号引脚完全兼容。本文仅以RPi Pico的SPI1接口为例进行介绍。
MicroSD 卡模块与RPi Pico开发板SPI1接口连接原理图及外观图如图2所示,硬件具体连接说明如下:
1.将MicroSD卡模块的 MOSI、MISO、SCLK 和 CS 引脚分别 与 RPi Pico的 SPI1 TX(SPI1 MOSI)、SPI1 RX(SPI1 MISO)、SPI1 SCK和 SPI1 CS 引脚连接,这些引脚对应于RPi Pico的SPI1接口信号。SPI1接口信号使用的是RPi Pico的GP10(SPI1 SCK)、GP11(SPI1 TX)、GP12(SPI1 RX)、GP13(SPI1 CS)引脚信号。
2.将MicroSD卡模块的 VCC 和 GND 引脚分别与RPi Pico的 3.3V(3V3 OUT)电源和GND接地引脚连接。
在这里插入图片描述
(a) MicroSD卡模块与RPi Pico的SPI接口连接原理图
在这里插入图片描述
(b) MicroSD卡模块与RPi Pico的SPI接口连接外观图
图2 MicroSD卡模块与RPi Pico的SPI接口连接
关于RPi Pico开发板(RP2040 MCU)SPI接口技术原理与相关实践,可参考相关技术文献,此处不再赘述。
四、MicroPython的machine模块
MicroPython的machine 模块包含与专用开发板硬件相关的特定功能。machine模块包括控制或管理GPIO、PWM、ADC、UART、SPI、I2C、I2S、Timer、RTC、Watchdog定时器及SD存储卡等硬件相关的类。machine模块的SDcard类(class SDCard)支持读取SD、MicroSD、MMC和eMMC等存储卡。
五、SDCard类
SDCard 类通过指定的SPI接口或SD/MMC SDIO接口访问 SD或MMC存储卡。在 MicroPython语言中使用SDCard类,须先下载sdcard.py程序文件并将其烧写到RPi Pico开发板板载Flash中,然后在MicroPython应用程序中导入sdcard。
sdcard.py程序文件可以从网址 https://github.com/micropython/micropython-lib/blob/master/micropython/drivers/storage/sdcard/sdcard.py或网址 https://github.com/micropython/micropython-infineon/blob/master/drivers/sdcard/sdcard.py下载。
sdcard.py程序清单如下:

from micropython import const
import time


_CMD_TIMEOUT = const(100)

_R1_IDLE_STATE = const(1 << 0)
# R1_ERASE_RESET = const(1 << 1)
_R1_ILLEGAL_COMMAND = const(1 << 2)
# R1_COM_CRC_ERROR = const(1 << 3)
# R1_ERASE_SEQUENCE_ERROR = const(1 << 4)
# R1_ADDRESS_ERROR = const(1 << 5)
# R1_PARAMETER_ERROR = const(1 << 6)
_TOKEN_CMD25 = const(0xFC)
_TOKEN_STOP_TRAN = const(0xFD)
_TOKEN_DATA = const(0xFE)


class SDCard:
    def __init__(self, spi, cs, baudrate=1320000):
        self.spi = spi
        self.cs = cs

        self.cmdbuf = bytearray(6)
        self.dummybuf = bytearray(512)
        self.tokenbuf = bytearray(1)
        for i in range(512):
            self.dummybuf[i] = 0xFF
        self.dummybuf_memoryview = memoryview(self.dummybuf)

        # initialise the card
        self.init_card(baudrate)

    def init_spi(self, baudrate):
        try:
            master = self.spi.MASTER
        except AttributeError:
            # on ESP8266
            self.spi.init(baudrate=baudrate, phase=0, polarity=0)
        else:
            # on pyboard
            self.spi.init(master, baudrate=baudrate, phase=0, polarity=0)

    def init_card(self, baudrate):
        # init CS pin
        self.cs.init(self.cs.OUT, value=1)

        # init SPI bus; use low data rate for initialisation
        self.init_spi(100000)

        # clock card at least 100 cycles with cs high
        for i in range(16):
            self.spi.write(b"\xff")

        # CMD0: init card; should return _R1_IDLE_STATE (allow 5 attempts)
        for _ in range(5):
            if self.cmd(0, 0, 0x95) == _R1_IDLE_STATE:
                break
        else:
            raise OSError("no SD card")

        # CMD8: determine card version
        r = self.cmd(8, 0x01AA, 0x87, 4)
        if r == _R1_IDLE_STATE:
            self.init_card_v2()
        elif r == (_R1_IDLE_STATE | _R1_ILLEGAL_COMMAND):
            self.init_card_v1()
        else:
            raise OSError("couldn't determine SD card version")

        # get the number of sectors
        # CMD9: response R2 (R1 byte + 16-byte block read)
        if self.cmd(9, 0, 0, 0, False) != 0:
            raise OSError("no response from SD card")
        csd = bytearray(16)
        self.readinto(csd)
        if csd[0] & 0xC0 == 0x40:  # CSD version 2.0
            self.sectors = ((csd[8] << 8 | csd[9]) + 1) * 1024
        elif csd[0] & 0xC0 == 0x00:  # CSD version 1.0 (old, <=2GB)
            c_size = (csd[6] & 0b11) << 10 | csd[7] << 2 | csd[8] >> 6
            c_size_mult = (csd[9] & 0b11) << 1 | csd[10] >> 7
            read_bl_len = csd[5] & 0b1111
            capacity = (c_size + 1) * (2 ** (c_size_mult + 2)) * (2**read_bl_len)
            self.sectors = capacity // 512
        else:
            raise OSError("SD card CSD format not supported")
        # print('sectors', self.sectors)

        # CMD16: set block length to 512 bytes
        if self.cmd(16, 512, 0) != 0:
            raise OSError("can't set 512 block size")

        # set to high data rate now that it's initialised
        self.init_spi(baudrate)

    def init_card_v1(self):
        for i in range(_CMD_TIMEOUT):
            time.sleep_ms(50)
            self.cmd(55, 0, 0)
            if self.cmd(41, 0, 0) == 0:
                # SDSC card, uses byte addressing in read/write/erase commands
                self.cdv = 512
                # print("[SDCard] v1 card")
                return
        raise OSError("timeout waiting for v1 card")

    def init_card_v2(self):
        for i in range(_CMD_TIMEOUT):
            time.sleep_ms(50)
            self.cmd(58, 0, 0, 4)
            self.cmd(55, 0, 0)
            if self.cmd(41, 0x40000000, 0) == 0:
                self.cmd(58, 0, 0, -4)  # 4-byte response, negative means keep the first byte
                ocr = self.tokenbuf[0]  # get first byte of response, which is OCR
                if not ocr & 0x40:
                    # SDSC card, uses byte addressing in read/write/erase commands
                    self.cdv = 512
                else:
                    # SDHC/SDXC card, uses block addressing in read/write/erase commands
                    self.cdv = 1
                # print("[SDCard] v2 card")
                return
        raise OSError("timeout waiting for v2 card")

    def cmd(self, cmd, arg, crc, final=0, release=True, skip1=False):
        self.cs(0)

        # create and send the command
        buf = self.cmdbuf
        buf[0] = 0x40 | cmd
        buf[1] = arg >> 24
        buf[2] = arg >> 16
        buf[3] = arg >> 8
        buf[4] = arg
        buf[5] = crc
        self.spi.write(buf)

        if skip1:
            self.spi.readinto(self.tokenbuf, 0xFF)

        # wait for the response (response[7] == 0)
        for i in range(_CMD_TIMEOUT):
            self.spi.readinto(self.tokenbuf, 0xFF)
            response = self.tokenbuf[0]
            if not (response & 0x80):
                # this could be a big-endian integer that we are getting here
                # if final<0 then store the first byte to tokenbuf and discard the rest
                if final < 0:
                    self.spi.readinto(self.tokenbuf, 0xFF)
                    final = -1 - final
                for j in range(final):
                    self.spi.write(b"\xff")
                if release:
                    self.cs(1)
                    self.spi.write(b"\xff")
                return response

        # timeout
        self.cs(1)
        self.spi.write(b"\xff")
        return -1

    def readinto(self, buf):
        self.cs(0)

        # read until start byte (0xff)
        for i in range(_CMD_TIMEOUT):
            self.spi.readinto(self.tokenbuf, 0xFF)
            if self.tokenbuf[0] == _TOKEN_DATA:
                break
            time.sleep_ms(1)
        else:
            self.cs(1)
            raise OSError("timeout waiting for response")

        # read data
        mv = self.dummybuf_memoryview
        if len(buf) != len(mv):
            mv = mv[: len(buf)]
        self.spi.write_readinto(mv, buf)

        # read checksum
        self.spi.write(b"\xff")
        self.spi.write(b"\xff")

        self.cs(1)
        self.spi.write(b"\xff")

    def write(self, token, buf):
        self.cs(0)

        # send: start of block, data, checksum
        self.spi.read(1, token)
        self.spi.write(buf)
        self.spi.write(b"\xff")
        self.spi.write(b"\xff")

        # check the response
        if (self.spi.read(1, 0xFF)[0] & 0x1F) != 0x05:
            self.cs(1)
            self.spi.write(b"\xff")
            return

        # wait for write to finish
        while self.spi.read(1, 0xFF)[0] == 0:
            pass

        self.cs(1)
        self.spi.write(b"\xff")

    def write_token(self, token):
        self.cs(0)
        self.spi.read(1, token)
        self.spi.write(b"\xff")
        # wait for write to finish
        while self.spi.read(1, 0xFF)[0] == 0x00:
            pass

        self.cs(1)
        self.spi.write(b"\xff")

    def readblocks(self, block_num, buf):
        # workaround for shared bus, required for (at least) some Kingston
        # devices, ensure MOSI is high before starting transaction
        self.spi.write(b"\xff")

        nblocks = len(buf) // 512
        assert nblocks and not len(buf) % 512, "Buffer length is invalid"
        if nblocks == 1:
            # CMD17: set read address for single block
            if self.cmd(17, block_num * self.cdv, 0, release=False) != 0:
                # release the card
                self.cs(1)
                raise OSError(5)  # EIO
            # receive the data and release card
            self.readinto(buf)
        else:
            # CMD18: set read address for multiple blocks
            if self.cmd(18, block_num * self.cdv, 0, release=False) != 0:
                # release the card
                self.cs(1)
                raise OSError(5)  # EIO
            offset = 0
            mv = memoryview(buf)
            while nblocks:
                # receive the data and release card
                self.readinto(mv[offset : offset + 512])
                offset += 512
                nblocks -= 1
            if self.cmd(12, 0, 0xFF, skip1=True):
                raise OSError(5)  # EIO

    def writeblocks(self, block_num, buf):
        # workaround for shared bus, required for (at least) some Kingston
        # devices, ensure MOSI is high before starting transaction
        self.spi.write(b"\xff")

        nblocks, err = divmod(len(buf), 512)
        assert nblocks and not err, "Buffer length is invalid"
        if nblocks == 1:
            # CMD24: set write address for single block
            if self.cmd(24, block_num * self.cdv, 0) != 0:
                raise OSError(5)  # EIO

            # send the data
            self.write(_TOKEN_DATA, buf)
        else:
            # CMD25: set write address for first block
            if self.cmd(25, block_num * self.cdv, 0) != 0:
                raise OSError(5)  # EIO
            # send the data
            offset = 0
            mv = memoryview(buf)
            while nblocks:
                self.write(_TOKEN_CMD25, mv[offset : offset + 512])
                offset += 512
                nblocks -= 1
            self.write_token(_TOKEN_STOP_TRAN)

    def ioctl(self, op, arg):
        if op == 4:  # get number of blocks
            return self.sectors
        if op == 5:  # get block size in bytes
            return 512

除此之外,还必须导入os模块和machine模块。
必须导入的一些模块语句如下:

import uos
import machine
import sdcard

SDCard类的SD对象需使用构造器进行初始化,SDCard类的构造器调用格式如下:

class machine.SDCard(slot=1, width=1, cd=None, wp=None, sck=None, miso=None, mosi=None, cs=None, freq=20000000)

构造器参数说明:
•slot: 选择一个可用SPI/SDIO接口或使用默认接口
•width: 选择SD/MMC接口的总线宽度(对于SD/MicroSD卡适配器,宽度为“1”;一般不需要显式指定,除非SD卡硬件可以访问存储卡的并行接口)
•cd: 指定SD卡检测引脚
•wp: 指定写保护引脚
•sck: 指定SPI CLK引脚
•miso: 指定SPI MISO引脚
•mosi: 指定SPI MOSI引脚
•cs: 指定SPI CS片选引脚
•freq: 选择 SD/MMC 接口工作频率(单位:Hz)
RPi Pico开发板扩展硬件接口提供了两个SPI接口:SPI0和SPI1。
以RPi Pico开发板SPI1接口连接MicroSD卡模块为例,假设SPI1接口信号使用上面介绍的GP10~GP13端口(GP10: SPI1 SCK; GP11: SPI1 TX/SPI1 MOSI; GP12: SPI1 RX/ SPI1 MISO; GP13: SPI1 CS),启动SD卡对象语句序列如下:

spi = SPI(1, sck=Pin(10), mosi=Pin(11), miso=Pin(12))
cs = Pin(13)
sd = sdcard.SDCard(spi, cs)

六、os/uos模块
MicroPython os/uos模块提供了对文件系统访问和挂载的功能。下面是访问MicroSD 卡的几个常用os对象的方法。
1.os.VfsFat()
os.VfsFat()方法创建使用 FAT 文件系统格式的文件系统对象。块设备实现块协议,提供FAT文件系统存储。 本质上,os.VfsFat()方法使设备能够支持 MicroPython文件系统。 物理硬件由文件系统类表示。 当使用 os.VfsFat()方法创建对象时,表示使用FAT文件系统,我们可以用下面的os.mount()方法挂载该对象。
2.os.mount()
os.mount()方法将指定的文件系统对象挂载到由字符串参数指定的虚拟文件系统位置。文件系统对象可以是具有 mount() 方法的虚拟对象或块设备。 若是块设备,则自动检测文件系统类型;否则,将抛出异常。
指定位置的字符串可以是“/”以便将文件系统对象挂载在根目录下,或者是“/”将其挂载在根目录下的子目录中。 os.mount()方法有3个参数:文件系统对象、指定位置的字符串和只读参数。如果只读参考为“True”,则文件系统以只读方式挂载。 如果指定位置已挂载,则会抛出OSError(EPERM)。
3.os.listdir()
如果调用os.listdir()方法不带参数,表示将列出当前目录;否则,列出指定的目录。
4.os.umount()
os.umount()方法用于“卸载”文件系统。位置可以是所命名挂载位置的字符串,也可以是事先安装的文件系统对象。在卸载过程中,将调用文件系统对象的os.umount()方法。
下面是 MicroSD卡 FAT文件系统的示例程序,它将文件系统对象挂载到内存中。

vfs = os.VfsFat(sd)
os.mount(vfs,/fc”)
print(“文件系统检测”)
print(os.listdir(/fc”))

七、MicroSD存储卡文件操作
1.创建文本文件
将纯文本文件添加到 MicroSD 存储卡十分简单。我们可以在MicroSD卡上创建多个纯文本文件,创建时只需指定该文本文件的路径字符串即可。
下面是在MicroSD 卡上创建一个“log.txt”文件名的纯文本文件:
fn =“/fc/log.txt”
2.写入单个块
单个块是指一行文本。必须使用字符串变量指定文件,用open()方法打开指定的文件路径才可以写入块,“w”参数是以写入方式打开文件。通过调用write()方法来写入单个块。
下面是在 MicroSD 卡的文本文件中写入单个块的代码示例:

fn =/fc/one-line-log.txt”
print()
print(“写单个块”)
with open(fn, “w”) as f:
            n = f.write(“abcdefgh\n”)  # 单个块
            print(n, “字节被写入”)

3.读取单个块
读取文件须调用open()方法打开字符串变量指定的文件,并将文件打开方式指定为“r”。 通过调用read()方法读取文件。
下面是从MicroSD卡读取文本文件的代码示例:

fn =/fc/one-line-log.txt”
print()
print(“读取单个块”)
with open(fn, “r”) as f:
            result = f.read()
            print(len(result2), “字节被读取”)
            print()
            print(result)

4.写入多个块
在文本文件中写入多个块与写入单个块类似,只是写入的字符串对象包含多行文本。
下面是在MicroSD卡上的文本文件中写入多个块的代码示例:

line = “ABCDEFGHIJKLMNOPQRSTUVWXYZ\n”
lines = line * 200  # 5400个字符
fn =/fc/multi-line-log.txt”
print()
print(“写入多上块”)
with open(fn, “w”) as f:
            n = f.write(lines)
            print(n, “字节被写入”)

5.读取多个块
在文本文件中读取多个块(多行读取方式)与读取单个块(单行读取方式)类似。
下面是从MicroSD卡读取多块文本文件的代码示例:

fn =/fc/multi-line-log.txt”
print()
print(“读取多个块”)
with open(fn, “r”) as f:
            result = f.read()
            print(len(result2), “字节被读取”)
            print()
            print(result)

八、MicroSD卡存储测试
MicroSD卡存储测试程序(程序文件名为“tfcard_test.py”)清单如下:

# Filename: tfcard_test.py
import uos  # os/uos
import machine
import sdcard
from machine import SPI, Pin

spi = SPI(1, sck=Pin(10), mosi=Pin(11), miso=Pin(12))
cs = Pin(13)
sd = sdcard.SDCard(spi, cs)

# 挂载文件到sd
uos.mount(sd,"/sd")
# 列出MicroSD/TF卡中的目录文件
print(uos.listdir('/sd'))

# 写文件测试
f = open('/sd/test.txt','w',encoding='utf-8')
f.write('MicroSD/TF存储卡访问测试!')
f.close()

# 读文件测试
f = open('/sd/test.txt','r')
print(f.read())
f.close()

MicroSD卡存储测试步骤归纳如下:
Step 1:下载并烧写RPi Pico MicroPython固件到RPi Pico开发板,具体方法可参考文献[1]或文献[2]。
Step 2:准备一块FAT32文件格式化好的MicroSD(TF)存储卡{若存储卡为非FAT32文件格式,可将MicroSD(TF)卡插入到MicroSD读卡器;MicroSD读卡器接入电脑USB口后,使用FAT32文件格式对MicroSD卡进行格式化}。
Step 3:将MicroSD卡插入MicroSD卡模块的卡槽中。
Step 4:按照图2所示的MicroSD卡模块与RPi Pico的SPI接口连接原理图和外观图连接MicroSD卡模块与RPi Pico开发板。
Step 4:将连接电脑与RPi Pico开发板的Micro USB线接入RPi Pico开发板和电脑USB口。
Step 5:运行Thonny IDE,点击[Tools]→[Options…]打开Thonny options对话框,在对话框中分别选择[MicroPython(Raspberry Pi Pico)]和RPi Pico开发板所使用端口Port的USB串行设备,如图3所示。
在这里插入图片描述
图3 Thonny options对话框RPi Pico开发板相关设置
Step 6:复制粘贴上面的“sdcard.py”程序到Thonny编辑器窗口,点击选择[File]→[Save as…]打开Where to save to?对话框,选择[Raspberry Pi Pico]后将程序以“sdcard.py”程序名烧写到RPi Pico开发板。
Step 7:新建一个编辑窗口,复制粘贴上面的“tfcard_test.py”程序到编辑器窗口,点击选择[File]→[Save as…]打开Where to save to?对话框,选择[Raspberry Pi Pico]后将程序以“tfcard_test.py程序名烧写到RPi Pico开发板,如图4所示。
在这里插入图片描述
图4 RPi Pico开发板存储的程序文件
Step 8:点击 [View],选中 [Files],可看到RPi Pico开发板中的两个程序文件,如图5所示。
在这里插入图片描述
图5 Thonny [Files]标签窗口显示RPi Pico开发板中的程序文件
Step 9:选中Thonny中“tfcard_test.py”程序所在的编辑器窗口,点击图6Thonny中的运行按钮(红色框)执行程序,Thonny Shell窗口将显示MicroSD卡上的目录文件信息及读取前面写入到”test.txt“文件中的“MicroSD/TF存储卡访问测试!”字符串。
在这里插入图片描述
图6 MicroSD存储卡读写测试

参考文献:
[1]袁志勇.《AI嵌入式系统技术与实践-基于树莓派RP2040和MicroPython》. 北京: 北京航空航天大学出版社, 2023年4月。
[2] Raspberry Pi Pico实践系列1-Windows环境下树莓派Pico迷你开发板MicroPython快速上手实践

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

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

相关文章

创建数据库Market、Team,按要求完成指定操作

创建数据库Market&#xff0c;在Market中创建数据表customers&#xff0c;customers表结构如表4.6所示&#xff0c;按要求进行操作。 代码如下&#xff1a; #(1&#xff09;创建数据库Market mysql> create database Market; Query OK, 1 row affected (0.00 sec)mysql>…

Windows系统中将markdown文件批量转化为PDF

需要将一个文件夹下的多个md文件转化为PDF 下载安装pandoc 官网下载地址&#xff1a;Pandoc &#xff0c;下载位置如下图。 下载后按照默认文件路径安装完成 使用everything软件查找pandoc.exe文件路径&#xff0c;如下图&#xff1a; 安装完成之后就可以在cmd窗口或Window…

Cyclo(L-Asp-L-Gly),52661-97-9,定制含D型与L型氨基酸

&#xff08;文章资料汇总来源于&#xff1a;陕西新研博美生物科技有限公司小编MISSwu&#xff09; 试剂基团反应特点&#xff08;Reagent group reaction characteristics&#xff09;&#xff1a; Cyclo(L-Asp-L-Gly)&#xff0c;52661-97-9&#xff0c;&#xff08;活性药物…

荧光染料92557-81-8,FAM NHS ester,6-isomer,用于标记核苷酸的荧光试剂

6-羧基荧光素琥珀酰亚胺酯 6-FAM&#xff0c; 用于标记核苷酸的荧光试剂&#xff0c;6-羧基荧光素琥珀酰亚胺酯是一种化学物质。荧光标记染料在生物分析中发挥越来越重要的作用&#xff0c;特别是6-羧基荧光素已经成为检测蛋白质&#xff0c;DNA序列的国际通用的荧光染料。 6-F…

变压器绝缘电阻测试试验

试验目的 电力变压器是发电厂、 变电站和用电部门最主要的电力设备之一&#xff0c; 是输变电能的电器。 测量绕组绝缘电阻、 吸收比和极化指数&#xff0c; 能有效地检查出变压器绝缘整体受潮&#xff0c; 部件表面受潮脏污&#xff0c; 以及贯穿性的集中行缺陷&#xff0c; …

Revit柱的绘制:陶立克柱绘制方法和生成柱

​  一、Revit陶立克柱的绘制方法 BIM等级考试一级第十期第四题陶立克柱该如何绘制呢?接下来我演示给大家。 陶立克柱看似很复杂&#xff0c;其实很简单&#xff0c;只需要先绘制好柱身&#xff0c;会用到阵列工具的使用再绘制柱子上部或下部分&#xff0c;最后采用镜像工具…

spring系列所有漏洞vulhub复现CVE-2022-22978、CVE-2022-22963、CVE-2022-22965、CVE-2018-1273

文章目录 CVE-2022-22978 Spring-security 认证绕过漏洞漏洞描述:复现&#xff1a; CVE-2022-22963漏洞描述:复现&#xff1a; 提提神Spring框架Data Binding与JDK 9导致的远程代码执行漏洞&#xff08;CVE-2022-22965&#xff09;漏洞描述:复现&#xff1a; Spring Data Commo…

智慧水务信息化建设——看“厂、站、网”一体化综合监管

平台概述 智慧水务信息化平台是以物联感知技术、大数据、智能控制、云计算、人工智能、数字孪生、AI算法、虚拟现实技术为核心&#xff0c;以监测仪表、通讯网络、数据库系统、数据中台、模型软件、前台展示、智慧运维等产品体系为支撑&#xff0c;以城市水资源、水生态、水环…

Intellij IDEA突然无法启动问题

遇到的情况&#xff1a;Intellij IDEA 双击或者鼠标右键右键单击都无法启动&#xff0c;打开任务管理器也没有Intellij IDEA线程启动。 解决方法&#xff1a; 第一步&#xff1a;以管理员身份打开命令提示符&#xff0c;输入命令(“ netsh winsock reset ”)&#xff0c;关闭…

机器学习15:神经网络-Neural Networks

神经网络是特征交叉的更复杂版本。本质上&#xff0c;神经网络会学习适当的特征组合。本文主要介绍神经网络的结构、隐藏层、激活函数等内容。 目录 1.神经网络&#xff1a;结构 2.隐藏层 3.激活函数 3.1 常用激活函数 3.2 小结 4.神经网络小练习 4.1 第一个神经网络 …

异步请求(Ajax,axios,json)

同步/异步请求 表单&#xff08;前端&#xff09;向后端发送请求,属于同步请求 同步: 发一个请求, 给一个回应, 会用回应的内容覆盖掉浏览器中内容&#xff0c;这样会打断前端其他的正常操作&#xff0c;在现在的前端中&#xff0c;显得不太友好。 异步: 不同步 前端正常输入时…

腾讯云部署vitepress,高颜值文档博客

首先上官方网站。https://vitepress.dev/ 先看两张效果图。 一定要用这个官网&#xff0c;之前看了一个翻译版本&#xff0c;好像翻了一半不弄了&#xff0c;坑了半天时间也解决不了。目前看起来还没有官翻。 目前使用到的是腾讯云的轻量应用服务器&#xff0c;效果还是非常好…

【CSDN新星计划】初阶牛C/C++赛道——顺序程序设计(数据的表现形式及其运算)

目录 &#x1f34a;1.数据的表现形式及其运算 &#x1f349;1.1常量和变量 &#x1f340;1.1.1常量 &#x1f340;1.1.2变量 &#x1f340;1.1.3常变量 &#x1f340;1.1.4标识符 &#x1f349;1.2数据类型 &#x1f349;1.3整型数据 &#x1f340;1.3.1整型数据的分类…

FCPX插件Stupid Raisins Split Pop 2(视频照片动画拆分效果插件)

Stupid RAIsins Split Pop是一个视频照片动画拆分效果插件&#xff0c;它允许您在Final Cut Pro&#xff0c;Motion&#xff0c;Premiere Pro和After Effects中使用。fcpx插件Split Screen非常适合用视频和照片创建动画分割&#xff0c;专为4K UHD和高清视频而设计&#xff0c;…

RAID5重建失败的服务器数据恢复案例

服务器数据恢复环境&#xff1a; 一台IBM某型号服务器&#xff0c;4块SAS磁盘组建了一组RAID5磁盘阵列。服务器安装的windows server操作系统&#xff0c;上面运行了一个Oracle单节点&#xff0c;数据存储为文件系统&#xff0c;无归档。该oracle数据库的数据量不大&#xff0c…

Python爬虫——批量下载微信公众号图片

目标&#xff1a; 微信公众号是现代社交媒体中最受欢迎的平台之一&#xff0c;每天都有数以百万计的人在浏览不同的公众号&#xff0c;其中大部分都包含了图片内容。如果你是一位公众号的管理员或者粉丝&#xff0c;你可能想要在本地保存一些感兴趣的图片。但是&#xff0c;微…

GIT下载安装

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

函数中的 static int 变量

#include <iostream>using namespace std;void function( ) {static int a 3 ; // 不赋值的话是 a 等于0&#xff1b;cout<<"a"<<a<<endl;aa3;cout<<"a"<<a<<endl;}int main(int argc, char** argv) {cout&l…

Apache DolphinScheduler 荣获“掘进技术引力榜”「2023 年度 ROBUST 开源项目」奖项!

经过紧张激烈的投票和严格的专家评审环节&#xff0c;“掘进技术引力榜”活动在上周的稀土掘金开发者大会上公布了「2023 年度 ROBUST 开源项目」奖项的获奖名单&#xff0c;Apache DolphinScheduler 名列其中。 Apache DolphinScheduler 代表上台领奖&#xff08;右三&#x…

蓝桥杯专题-真题版含答案-【蚂蚁感冒】【地宫取宝】【波动数列】【李白打酒】

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列点击跳转>蓝桥系列 &#x1f449;关于作者 专注于Android/Unity和各种游…