STM32單片機入門學習——第40節: [11-5] 硬件SPI讀寫W25Q64

寫這個文章是用來學習的,記錄一下我的學習過程。希望我能一直堅持下去,我只是一個小白,只是想好好學習,我知道這會很難,但我還是想去做!

本文寫于:2025.04.18

STM32開發板學習——第一節: [1-1]課程簡介第40節: [11-5] 硬件SPI讀寫W25Q64

  • 前言
  • 開發板說明
  • 引用
  • 解答和科普
  • 一、硬件接線
  • 二、W25Q64
  • 問題
  • 總結

前言

? ?本次筆記是用來記錄我的學習過程,同時把我需要的困難和思考記下來,有助于我的學習,同時也作為一種習慣,可以督促我學習,是一個激勵自己的過程,讓我們開始32單片機的學習之路。
? ?歡迎大家給我提意見,能給我的嵌入式之旅提供方向和路線,現在作為小白,我就先學習32單片機了,就跟著B站上的江協科技開始學習了.
? ?在這里會記錄下江協科技32單片機開發板的配套視頻教程所作的實驗和學習筆記內容,因為我之前有一個開發板,我大概率會用我的板子模仿著來做.讓我們一起加油!
? ?另外為了增強我的學習效果:每次筆記把我不知道或者問題在后面提出來,再下一篇開頭作為解答!

開發板說明

? ?本人采用的是慧凈的開發板,因為這個板子是我N年前就買的板子,索性就拿來用了。另外我也購買了江科大的學習套間。
? ?原理圖如下
1、開發板原理圖
在這里插入圖片描述
2、STM32F103C6和51對比
在這里插入圖片描述
3、STM32F103C6核心板
在這里插入圖片描述

視頻中的都用這個開發板來實現,如果有資源就利用起來。另外也計劃實現江協科技的套件。

下圖是實物圖
在這里插入圖片描述

引用

【STM32入門教程-2023版 細致講解 中文字幕】
還參考了下圖中的書籍:
STM32庫開發實戰指南:基于STM32F103(第2版)
在這里插入圖片描述
數據手冊
在這里插入圖片描述

解答和科普

一、硬件接線

在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
默認情況下,是作為JATG調試端口使用的,都需要先接觸調試端口的復用。
SCK(PA5),MISO(PA6),MOSI(PA7),NSS(PA4或其他)。
我們的任務就是修改底層的這個MySPI.c文件,把這些初始化,和時序的執行步驟,由軟件實現變成硬件實現。所以,我們把底層的實現,由軟件改成硬件,不會影響到上層代碼的。
這個就不移出了。
在這里插入圖片描述
第一步,開啟時鐘,開啟SPI和GPIO的時鐘;
第二步,初始化GPIO口,其中SCK、和MOSI,是由硬件外設控制的輸出信號,所以配置為復用推挽輸出,MISO是硬件外設的輸入信號,我們可以配置為上拉輸入;因為輸入設備可以有多個,不存在復用輸入這個東西,直接上拉輸入就可,普通GPIO口可以輸入,外設也可以輸入;SS引腳,是軟件控制的輸出信號,所以配置為通用推挽輸出;
第三步,配置SPI外設,這一塊,使用一個結構體選參數即可,調用一下SPI_Init,這里的各種參數就都配置好了;
第四步,開關控制,SPI_Cmd,給SPI使能。
寫DR、讀DR、獲取標志位。

庫文件
void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);
寫DR寄存器,寫數據到發送數據寄存器TDR.
uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx);
讀DR寄存器,接收數據寄存器RDR.
FlagStatus SPI_I2S_GetFlagStatus(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG);
獲取TXE和RXNE標志位的狀態;
這樣就可以控制時序產生。

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);GPIO_InitTypeDef   GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin=GPIO_Pin_4;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5|GPIO_Pin_7;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);
void MySPI_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);GPIO_InitTypeDef   GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin=GPIO_Pin_4;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5|GPIO_Pin_7;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);SPI_InitTypeDef   SPI_InitStructure;SPI_InitStructure.SPI_Mode=SPI_Mode_Master;				//模式:主機SPI_InitStructure.SPI_Direction=SPI_Direction_2Lines_FullDuplex;	//單雙向模式SPI_InitStructure.SPI_DataSize=SPI_DataSize_8b;						//數據幀長度SPI_InitStructure.SPI_FirstBit=SPI_FirstBit_MSB;					//高位先行SPI_InitStructure.SPI_BaudRatePrescaler=SPI_BaudRatePrescaler_128;	//外設時鐘128分頻SPI_InitStructure.SPI_CPOL=SPI_CPOL_Low;			//模式0SPI_InitStructure.SPI_CPHA=SPI_CPHA_1Edge;SPI_InitStructure.SPI_NSS=SPI_NSS_Soft;				//軟件SPI_InitStructure.SPI_CRCPolynomial=7;				//CRC校驗7SPI_Init(SPI1,&SPI_InitStructure);SPI_Cmd(SPI1,ENABLE);MySPI_W_SS(1);		//默認不選中從機
}

SS

void MySPI_W_SS(uint8_t BitValue)
{GPIO_WriteBit(GPIOA,GPIO_Pin_4,(BitAction)BitValue);
}
void MySPI_Start(void)
{MySPI_W_SS(0);
}void MySPI_Stop(void)
{MySPI_W_SS(1);
}

第一步,等待TXE為1,第二步,寫入發送的數據至TDR,第三步,等待RXNE為1,第四步,讀取RDR接收的數據,之后交換第二個字節,重復這4步。

uint8_t MySPI_SwapByte(uint8_t ByteSend)
{while (SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE)!=SET);SPI_I2S_SendData(SPI1,ByteSend);while (SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE)!=SET);return SPI_I2S_ReceiveData(SPI1);}

硬件SPI初始化

void MySPI_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);GPIO_InitTypeDef   GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin=GPIO_Pin_4;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5|GPIO_Pin_7;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);SPI_InitTypeDef   SPI_InitStructure;SPI_InitStructure.SPI_Mode=SPI_Mode_Master;				//模式:主機SPI_InitStructure.SPI_Direction=SPI_Direction_2Lines_FullDuplex;	//單雙向模式SPI_InitStructure.SPI_DataSize=SPI_DataSize_8b;						//數據幀長度SPI_InitStructure.SPI_FirstBit=SPI_FirstBit_MSB;					//高位先行SPI_InitStructure.SPI_BaudRatePrescaler=SPI_BaudRatePrescaler_128;	//外設時鐘128分頻SPI_InitStructure.SPI_CPOL=SPI_CPOL_Low;			//模式0SPI_InitStructure.SPI_CPHA=SPI_CPHA_1Edge;SPI_InitStructure.SPI_NSS=SPI_NSS_Soft;				//軟件SPI_InitStructure.SPI_CRCPolynomial=7;				//CRC校驗7SPI_Init(SPI1,&SPI_InitStructure);SPI_Cmd(SPI1,ENABLE);MySPI_W_SS(1);		//默認不選中從機
}
#ifndef    __MYSPI_H
#define    __MYSPI_Hvoid MySPI_Init(void);
void MySPI_Start(void);
void MySPI_Stop(void);
uint8_t MySPI_SwapByte(uint8_t ByteSend);#endif

二、W25Q64

應用層不變

#include "stm32f10x.h"                  // Device header
#include "MySPI.h"
#include "W25Q64_Ins.h"
void W25Q64_Init(void)
{MySPI_Init();
}void W25Q64_ReadID(uint8_t *MID ,uint16_t *DID)
{MySPI_Start();MySPI_SwapByte(W25Q64_JEDEC_ID);				//讀ID號的指令,拋玉引磚,返回值沒有意義沒用*MID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);		//0XFF沒有意義,拋磚引玉,就是為了把對面有意義的數據置換過來*DID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);		// 讀取設備ID高8位*DID <<=8;*DID |= MySPI_SwapByte(W25Q64_DUMMY_BYTE);		//獲得16位的DIDMySPI_Stop();
}void W25Q64_WriteEnable(void)
{MySPI_Start();MySPI_SwapByte(W25Q64_WRITE_ENABLE);MySPI_Stop();
}void W25Q64_WaiteBusy(void)
{uint32_t TimeOut;MySPI_Start();MySPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1);TimeOut=10000;while ((MySPI_SwapByte(W25Q64_DUMMY_BYTE)& 0x01)==0x01)		//用掩碼取出低位{TimeOut--;if(TimeOut==0){break;}}MySPI_Stop();
}void W25Q64_PageProgram(uint32_t Address,uint8_t *DataArray,uint16_t Count)
{uint16_t  i;W25Q64_WriteEnable();MySPI_Start();MySPI_SwapByte(W25Q64_PAGE_PROGRAM);MySPI_SwapByte(Address>>16);			//0x123456 變為 0x12MySPI_SwapByte(Address>>8);				//0x123456 變為 0x1234  高位自動舍去就是0x34	MySPI_SwapByte(Address);				//0x123456 高位自動舍去就是0x56	for(i=0;i<Count;i++){MySPI_SwapByte(DataArray[i]);}MySPI_Stop();W25Q64_WaiteBusy();	//事后等待
}void W25Q64_SectorErase(uint32_t Address)
{W25Q64_WriteEnable();	//寫使能MySPI_Start();MySPI_SwapByte(W25Q64_SECTOR_ERASE_4KB);MySPI_SwapByte(Address>>16);			//0x123456 變為 0x12MySPI_SwapByte(Address>>8);				//0x123456 變為 0x1234  高位自動舍去就是0x34	MySPI_SwapByte(Address);MySPI_Stop();W25Q64_WaiteBusy();		//事后等待
}
void W25Q64_ReadData(uint32_t Address,uint8_t *DataArray,uint32_t Count)
{uint32_t  i;MySPI_Start();MySPI_SwapByte(W25Q64_READ_DATA);MySPI_SwapByte(Address>>16);			//0x123456 變為 0x12MySPI_SwapByte(Address>>8);				//0x123456 變為 0x1234  高位自動舍去就是0x34	MySPI_SwapByte(Address);	for(i=0;i<Count;i++){DataArray[i]=MySPI_SwapByte(W25Q64_DUMMY_BYTE);}MySPI_Stop();}
#ifndef    __W25Q64_H
#define    __W25Q64_Hvoid W25Q64_Init(void);
void W25Q64_ReadID(uint8_t *MID ,uint16_t *DID);
void W25Q64_ReadData(uint32_t Address,uint8_t *DataArray,uint32_t Count);
void W25Q64_SectorErase(uint32_t Address);
void W25Q64_PageProgram(uint32_t Address,uint8_t *DataArray,uint16_t Count);#endif
#ifndef __W25Q64_INS_H
#define __W25Q64_INS_H#define W25Q64_WRITE_ENABLE							0x06
#define W25Q64_WRITE_DISABLE						0x04
#define W25Q64_READ_STATUS_REGISTER_1				0x05
#define W25Q64_READ_STATUS_REGISTER_2				0x35
#define W25Q64_WRITE_STATUS_REGISTER				0x01
#define W25Q64_PAGE_PROGRAM							0x02
#define W25Q64_QUAD_PAGE_PROGRAM					0x32
#define W25Q64_BLOCK_ERASE_64KB						0xD8
#define W25Q64_BLOCK_ERASE_32KB						0x52
#define W25Q64_SECTOR_ERASE_4KB						0x20
#define W25Q64_CHIP_ERASE							0xC7
#define W25Q64_ERASE_SUSPEND						0x75
#define W25Q64_ERASE_RESUME							0x7A
#define W25Q64_POWER_DOWN							0xB9
#define W25Q64_HIGH_PERFORMANCE_MODE				0xA3
#define W25Q64_CONTINUOUS_READ_MODE_RESET			0xFF
#define W25Q64_RELEASE_POWER_DOWN_HPM_DEVICE_ID		0xAB
#define W25Q64_MANUFACTURER_DEVICE_ID				0x90
#define W25Q64_READ_UNIQUE_ID						0x4B
#define W25Q64_JEDEC_ID								0x9F
#define W25Q64_READ_DATA							0x03
#define W25Q64_FAST_READ							0x0B
#define W25Q64_FAST_READ_DUAL_OUTPUT				0x3B
#define W25Q64_FAST_READ_DUAL_IO					0xBB
#define W25Q64_FAST_READ_QUAD_OUTPUT				0x6B
#define W25Q64_FAST_READ_QUAD_IO					0xEB
#define W25Q64_OCTAL_WORD_READ_QUAD_IO				0xE3#define W25Q64_DUMMY_BYTE							0xFF#endif

問題

總結

本節課主要是了解硬件SPI讀寫W25Q64,在硬件層面對整個SPI進行寫,主要是各種時序,標志位和事件來進行,硬件會自動開啟時序,需要進行配置。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/79492.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/79492.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/79492.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Model Context Protocol (MCP) 開放協議對醫療多模態數據整合的分析路徑【附代碼】

Model Context Protocol (MCP) 作為一種革命性的開放協議,正在重塑醫療領域多模態數據整合的方式。本文將深入分析MCP協議在醫療多模態數據整合中的具體路徑、技術實現、應用場景及未來發展方向,揭示這一協議如何成為連接AI與醫療數據的關鍵橋梁。 MCP協議概述及其在醫療多模…

刀片服務器的散熱構造方式

刀片服務器的散熱構造是其高密度、高性能設計的核心挑戰之一。其散熱系統需在有限空間內高效處理多個刀片模塊產生的集中熱量,同時兼顧能耗、噪音和可靠性。以下從模塊化架構、核心散熱技術、典型方案對比、廠商差異及未來趨勢等方面展開分析: 一、模塊化散熱架構 刀片服務器…

java 排序算法-快速排序

快速排序&#xff08;Quick Sort&#xff09;是一種高效的排序算法&#xff0c;它使用分治法&#xff08;Divide and Conquer&#xff09;策略來把一個序列分為較小和較大的兩個子序列&#xff0c;然后遞歸地排序兩個子序列。 快速排序算法的基本思想&#xff1a; 選擇基準值&…

Linux工具學習之【vim】

&#x1f4d6;vim 基本用法 要想學會 vim 先要學會進入與退出它 &#x1f4c3;進入 vim 首先要保證自己的 Linux 中已經安裝好了 vim &#xff08;云服務器大多數都是出廠就安裝好了&#xff09;&#xff0c;如果沒有安裝&#xff0c;需要在 root 用戶下通過指令 yum instal…

win11系統截圖的幾種方式

在 Windows 11 中&#xff0c;系統內置的截圖功能已全面升級&#xff0c;不僅支持多種截圖模式&#xff0c;還整合了錄屏、OCR 文字識別和 AI 增強編輯等功能。以下是從基礎操作到高階技巧的完整指南&#xff1a; 一、快捷鍵截圖&#xff08;效率首選&#xff09; 1. Win Sh…

寫論文時降AIGC和降重的一些注意事項

‘ 寫一些研究成果&#xff0c;英文不是很好&#xff0c;用有道翻譯過來句子很簡單&#xff0c;句型很單一。那么你會考慮用ai嗎&#xff1f; 如果語句太正式&#xff0c;高級&#xff0c;會被誤判成aigc &#xff0c;慎重選擇ai潤色。 有的話就算沒有用ai生成&#xff0c;但…

Java學習手冊:Java并發編程最佳實踐

在Java并發編程中&#xff0c;遵循最佳實踐可以顯著提高程序的性能、可靠性和可維護性。本文將總結Java并發編程中的關鍵最佳實踐&#xff0c;幫助開發者避免常見陷阱并編寫高效的并發程序。 1. 選擇合適的并發工具 Java提供了豐富的并發工具&#xff0c;選擇合適的工具可以簡…

天梯賽DFS合集

1.DFS特殊輸入&#xff1a;PTA | 程序設計類實驗輔助教學平臺 這題其他還是蠻容易&#xff0c;直接用遞歸即可&#xff0c;問題在于怎么輸入&#xff0c;其實可以在遞歸到底層時輸入即可&#xff0c;也就是邊遞歸邊輸入&#xff0c;另外提一嘴跟這個題沒什么關系的點&#xff…

使用Pydantic優雅處理幾何數據結構 - 前端輸入驗證實踐

使用Pydantic優雅處理幾何數據結構 - 前端輸入驗證實踐 一、應用場景解析 在視頻分析類項目中&#xff0c;前端常需要傳遞幾何坐標數據。例如智能安防系統中&#xff0c;需要接收&#xff1a; 視頻流地址&#xff08;rtsp_video&#xff09;檢測區域坐標點&#xff08;point…

智譜AI大模型免費開放:開啟AI創作新時代

文章摘要&#xff1a;近日&#xff0c;國內領先的人工智能公司智譜AI宣布旗下多款大模型服務免費開放&#xff0c;這一舉措標志著大模型技術正式邁入普惠階段。本文將詳細介紹智譜AI此次開放的GLM-4 等大模型&#xff0c;涵蓋其主要功能、技術特點、使用步驟以及應用場景&#…

JMeter中設置HTTPS請求

在JMeter中設置HTTPS請求&#xff0c;你可以按照以下步驟進行操作&#xff1a; 步驟一&#xff1a;添加線程組 打開JMeter后&#xff0c;右鍵點擊“測試計劃”&#xff0c;選擇“添加” -> “線程&#xff08;用戶&#xff09;” -> “線程組”。線程組用于定義虛擬用戶…

線程池七個參數的含義

Java中的線程池里七個參數的以及其各自的含義 面試題&#xff1a;說一下線程池七個參數的含義&#xff1f; 所謂的線程池的 7 大參數是指&#xff0c;在使用 ThreadPoolExecutor 創建線程池時所設置的 7 個參數&#xff0c;如以下源碼所示&#xff1a; public ThreadPoolExe…

【最后203篇系列】028 FastAPI的后臺任務處理

說明 今天偶然在別的文章里看到這個功能&#xff0c;突然覺得正好。 CeleryWorker已經搭好了&#xff0c;但是我一直想在用戶請求時進行額外的處理會比較影響處理時間&#xff0c;用這個正好可以搭配上。 我設想的一個場景&#xff1a; 1 用戶發起請求2 接口中進行關鍵信息…

uboot下讀取ubifs分區的方法

在uboot 的defconfig中增加以下內容&#xff1a; CONFIG_MTDIDS_DEFAULT"nand0nand0" CONFIG_MTDPARTS_DEFAULT"mtdpartsnand0:1M(boot1),1M(boot2),1M(hwinfo),6M(kernel1),6M(kernel2),56M(rootfs1),56M(rootfs2),-(ubi2)" CONFIG_CMD_UBIy 其中&#x…

圖+文+語音一體化:多模態合成數據集構建的實戰與方法論

目錄 圖文語音一體化&#xff1a;多模態合成數據集構建的實戰與方法論 一、多模態合成數據的核心價值 二、系統架構概覽 三、核心模塊與實現建議 ? 1. 文→圖&#xff1a;圖像合成&#xff08;Text-to-Image&#xff09; ? 2. 圖→文&#xff1a;自動描述&#xff08;I…

linux驅動之poll

驅動中 poll 實現 在用戶空間實現事件操作的一個主要實現是調用 select/poll/epoll 函數。那么在驅動中怎么來實現 poll 的底層呢&#xff1f; 其實在內核的 struct file_operations 結構體中有一個 poll 成員&#xff0c;其就是底層實現的接口函數。 驅動中 poll 函數實現原…

第八篇:系統分析師第三遍——3、4章

目錄 一、目標二、計劃三、完成情況四、意外之喜(最少2點)1.計劃內的明確認知和思想的提升標志2.計劃外的具體事情提升內容和標志 五、總結 一、目標 通過參加考試&#xff0c;訓練學習能力&#xff0c;而非單純以拿證為目的。 1.在復習過程中&#xff0c;訓練快速閱讀能力、掌…

C++17 新特性簡解

C17 新特性簡解 一、核心語言特性 1. 結構化綁定&#xff08;Structured Bindings&#xff09; 用途&#xff1a;解構復合類型&#xff08;如元組、結構體&#xff09;為獨立變量 示例&#xff1a; #include <iostream> #include <tuple>int main() {// 解構 st…

PHP使用pandoc把markdown文件轉為word

文章目錄 首先安裝pandocPHP處理 服務器操作系統是Linux&#xff0c;centos 首先安裝pandoc yum install -y pandoc安裝完成后輸入如下代碼&#xff0c;檢查安裝是否成功 pandoc --versionPHP處理 我把markdown內容存到了數據庫里&#xff0c;所以要從數據庫讀取內容。對內容…

【Python學習筆記】Pandas實現Excel質檢記錄表初審、復核及質檢統計

背景&#xff1a; 我有這樣一個需要審核的飛書題目表&#xff0c;按日期分成多個sheet&#xff0c;有初審——復核——質檢三個環節&#xff0c;這三個環節是不同的同學在作業&#xff0c;并且領到同一個題目的人選是隨機的&#xff0c;也就是說&#xff0c;完成一道題的三個人…