STC进阶开发(四)SPI协议、矩阵键盘、EEPROM

前言

        这一期我们简单介绍一下SPI协议,然后我们学习一下矩阵键盘,了解EEPROM是干什么用的,话不多说,开整!

SPI协议

SPI(Serial Peripheral Interface)是一种同步串行通信协议,用于在嵌入式系统中连接微控制器(MCU)和外围设备(如传感器、存储器、显示器等)。SPI协议需要4根线进行数据传输,分别是:

  • SCLK:时钟信号线,由主设备控制时序,用于同步数据传输。
  • MOSI:主设备输出从设备输入线,主设备通过该线向从设备发送数据。
  • MISO:主设备输入从设备输出线,从设备通过该线向主设备发送数据。
  • SS:从设备片选线,用于选择与主设备通信的从设备。(其他叫法CS)

SPI协议支持全双工通信,意味着主设备和从设备可以同时发送和接收数据。SPI协议传输数据时采用的是先进先出的方式。

标准的SPI总共有4根线,包括:SCLK(时钟线)、MOSI(主机输出从机输入线)、MISO(主机输入从机输出线)和SS(片选线)。但是在实际的应用中,可能会根据需要添加其他的辅助信号线,如数据就绪信号等。因此,SPI的具体实现方式可能会有所不同。

SPI协议中的DC线是指数据/命令线(Data/Command line),有时也称作RS线(Register Select line)。它是用来控制从主设备到从设备传输的数据是命令还是普通数据的信号线。在许多液晶显示屏、OLED屏幕、触摸屏等设备中,SPI总线上的DC线通常用于指示传输的数据是图像数据还是命令数据,以便设备能够正确地解析和处理数据

SPI通讯的时序是由主设备(Master)发起的,在数据传输的过程中,需要进行时序的协调,具体流程如下:

  • 主设备(Master)通过片选信号(Slave Select)选择通信的从设备(Slave)。
  • 主设备(Master)向从设备(Slave)发送时钟信号(SCLK),并将数据从输出口(MOSI)发送到从设备(Slave)的输入口(MISO)。
  • 从设备(Slave)在每个时钟脉冲的下降沿采样输入口(MISO)的数据,并将数据从输出口(MOSI)发送回主设备(Master)的输入口(MISO)。
  • 当传输完成后,主设备(Master)取消片选信号(Slave Select),从设备(Slave)被释放。

具体的通讯流程时序可以根据实际应用情况进行调整,例如可以调整时钟信号的极性和相位、选择合适的时钟频率等。

矩阵键盘

简介

        矩阵键盘是一种常见的数字输入设备,由多行多列的按键组成。每个按键都有一个唯一的行列坐标,通过行列坐标可以确定按键的编号,从而实现对数字或字母的输入。

原理图

矩阵键盘的基本结构包括按键、行引脚和列引脚。按键一般是机械按键或触摸按键,行引脚和列引脚分别与矩阵键盘的行和列相连,用于检测按键的输入状态。

代码

因为矩阵按键和之前学的独立按键很相似,所以代码不做过多解析,基本注释都在代码上标明,我们直接通过位运算来设定中间值从而捕获到每个按键的状态。

获取按键状态
#include "GPIO.h"
#include "NVIC.h"
#include "Delay.h"
#include "UART.h"
#include "Switch.h"

#define COL1 P03
#define COL2 P06
#define COL3 P07
#define COL4 P17

#define ROW1 P34
#define ROW2 P35
#define ROW3 P40
#define ROW4 P41

//判断按键的状态
#define IS_KEY_DOWN(row , col)  ((states & (1 << (row * 4 + col))) == 0)
#define IS_KEY_UP(row,col) ((states  & (1 << (row * 4 + col))) > 0)

//设置按键的状态
#define SET_KEY_DOWN(row, col)   (states &= ~(1 << (row * 4 + col)))
#define SET_KEY_UP(row, col)  (states  |=  (1 << (row * 4 + col)))

//按键的状态
#define DOWN 0
#define UP 1

void GPIO_config(void) {
	GPIO_InitTypeDef	GPIO_InitStructure;		//结构定义
	GPIO_InitStructure.Pin  = GPIO_Pin_3 | GPIO_Pin_6 | GPIO_Pin_7;		//指定要初始化的IO,
	GPIO_InitStructure.Mode = GPIO_PullUp;	//指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
	GPIO_Inilize(GPIO_P0, &GPIO_InitStructure);//初始化
	
	GPIO_InitStructure.Pin  =  GPIO_Pin_7;		//指定要初始化的IO,
	GPIO_InitStructure.Mode = GPIO_PullUp;	//指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
	GPIO_Inilize(GPIO_P1, &GPIO_InitStructure);//初始化
	
	GPIO_InitStructure.Pin  =  GPIO_Pin_4  | GPIO_Pin_5;		//指定要初始化的IO,
	GPIO_InitStructure.Mode = GPIO_PullUp;	//指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
	GPIO_Inilize(GPIO_P3, &GPIO_InitStructure);//初始化
	
	GPIO_InitStructure.Pin  =  GPIO_Pin_0  | GPIO_Pin_1;		//指定要初始化的IO,
	GPIO_InitStructure.Mode = GPIO_PullUp;	//指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
	GPIO_Inilize(GPIO_P4, &GPIO_InitStructure);//初始化
}

void UART_config(void) {
	// >>> 记得添加 NVIC.c, UART.c, UART_Isr.c <<<
    COMx_InitDefine		COMx_InitStructure;					//结构定义
    COMx_InitStructure.UART_Mode      = UART_8bit_BRTx;	//模式, UART_ShiftRight,UART_8bit_BRTx,UART_9bit,UART_9bit_BRTx
    COMx_InitStructure.UART_BRT_Use   = BRT_Timer1;			//选择波特率发生器, BRT_Timer1, BRT_Timer2 (注意: 串口2固定使用BRT_Timer2)
    COMx_InitStructure.UART_BaudRate  = 115200ul;			//波特率, 一般 110 ~ 115200
    COMx_InitStructure.UART_RxEnable  = ENABLE;				//接收允许,   ENABLE或DISABLE
    COMx_InitStructure.BaudRateDouble = DISABLE;			//波特率加倍, ENABLE或DISABLE
    UART_Configuration(UART1, &COMx_InitStructure);		//初始化串口1 UART1,UART2,UART3,UART4

  	NVIC_UART1_Init(ENABLE,Priority_1);		//中断使能, ENABLE/DISABLE; 优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3
    UART1_SW(UART1_SW_P30_P31);		// 引脚选择, UART1_SW_P30_P31,UART1_SW_P36_P37,UART1_SW_P16_P17,UART1_SW_P43_P44
}

u8 get_col(u8 i){
	if(i==0) return COL1;
	if(i==1) return COL2;
	if(i==2) return COL3;
	if(i==3) return COL4;
	return COL1;
}

// 根据遍历的索引下标来拉低对应行的电平状态
void pull_row(u8 i){
	ROW1 = i == 0 ? 0 : 1;
	ROW2 = i == 1 ? 0 : 1;
	ROW3 = i == 2 ? 0 : 1;
	ROW4 = i == 3 ? 0 : 1;
}


u16 states = 0xFFFF;  //1111 1111 1111 1111


void main(){
	int i , j;
	EA = 1 ;
	
	GPIO_config();
	
	UART_config();
	
	printf("start...\n");
	
	while(1){

		// 判定4行的状态
		
		//外层循环控制的是:行
		for(i  = 0 ; i < 4 ; i++){
			
			// 每遍历一次,就拉低这一行对应的电平状态,拉高其他行的电平状态
			pull_row(i);
			
			//里层循环控制的是: 列
			for(j = 0 ; j < 4  ; j++){
				
				/*
					 第1行:
							0-0 :  字节的 第 0 位   
							0-1 :  字节的 第 1 位
							0-2 :  字节的 第 2 位
							0-3 :  字节的 第 3 位
				
					第2行:
							1-0 :  字节的 第 4 位
							1-1 :  字节的 第 5 位
							1-2 :  字节的 第 6 位
							1-3 :  字节的 第 7 位
				
					第3行:
							2-0 :  字节的 第 8 位
							2-1 :  字节的 第 9 位
							2-2 :  字节的 第 10 位
							2-3 :  字节的 第 11 位
				
					第4行:
							3-0 :  字节的 第 12 位
							3-1 :  字节的 第 13 位
							3-2 :  字节的 第 14 位
							3-3 :  字节的 第 15 位
							
					所以i行j列的键,对应的 位是: i * 4 + j
					我们要去操作对应的键和位。
				*/
				
				
				
				if(get_col(j) == UP && IS_KEY_DOWN(i, j)){
					printf("%d-%d::弹起\n" , i+1 , j+1);
					SET_KEY_UP(i, j);
				}else if(get_col(j) == DOWN && IS_KEY_UP(i,j) ){
					printf("%d-%d::按下\n" , i+1 , j+1);
					SET_KEY_DOWN(i,j);
				}
			}
		}
		
	
		delay_ms(10);
	}
}
获取按键状态(通过extern封装)
main.c
#include "GPIO.h"
#include "NVIC.h"
#include "Delay.h"
#include "UART.h"
#include "Switch.h"
#include "MatrixKey.h"


void UART_config(void) {
	// >>> 记得添加 NVIC.c, UART.c, UART_Isr.c <<<
    COMx_InitDefine		COMx_InitStructure;					//结构定义
    COMx_InitStructure.UART_Mode      = UART_8bit_BRTx;	//模式, UART_ShiftRight,UART_8bit_BRTx,UART_9bit,UART_9bit_BRTx
    COMx_InitStructure.UART_BRT_Use   = BRT_Timer1;			//选择波特率发生器, BRT_Timer1, BRT_Timer2 (注意: 串口2固定使用BRT_Timer2)
    COMx_InitStructure.UART_BaudRate  = 115200ul;			//波特率, 一般 110 ~ 115200
    COMx_InitStructure.UART_RxEnable  = ENABLE;				//接收允许,   ENABLE或DISABLE
    COMx_InitStructure.BaudRateDouble = DISABLE;			//波特率加倍, ENABLE或DISABLE
    UART_Configuration(UART1, &COMx_InitStructure);		//初始化串口1 UART1,UART2,UART3,UART4

  	NVIC_UART1_Init(ENABLE,Priority_1);		//中断使能, ENABLE/DISABLE; 优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3
    UART1_SW(UART1_SW_P30_P31);		// 引脚选择, UART1_SW_P30_P31,UART1_SW_P36_P37,UART1_SW_P16_P17,UART1_SW_P43_P44
}


void MK_keydown(u8 row , u8 col){
	printf("%d-%d  按键按下了..\n" , (int)row , (int)col);
}

void MK_keyup(u8 row , u8 col){
		printf("%d-%d  按键弹起了..\n" , (int)row , (int)col);
}


void main(){

	

	EA = 1 ;
	
	//初始化矩阵键盘
	MK_init();
	
	UART_config();
	
	printf("start...\n");
	


	while(1){

		//扫描键盘
		MK_scan();
		
		
		delay_ms(10);
	}
}
MatrixKey.h
#ifndef	__MATRIXKEY_H
#define	__MATRIXKEY_H

#include "GPIO.h"

// 声明: 宏、结构体
#define COL1 P03
#define COL2 P06
#define COL3 P07
#define COL4 P17

#define ROW1 P34
#define ROW2 P35
#define ROW3 P40
#define ROW4 P41

//判断按键的状态
#define IS_KEY_DOWN(row , col)  ((states & (1 << (row * 4 + col))) == 0)
#define IS_KEY_UP(row,col) ((states  & (1 << (row * 4 + col))) > 0)


//设置按键的状态
#define SET_KEY_DOWN(row, col)   (states &= ~(1 << (row * 4 + col)))
#define SET_KEY_UP(row, col)  (states  |=  (1 << (row * 4 + col)))

//按键的状态
#define DOWN 0
#define UP 1


// 函数具体功能
void MK_init();

//扫描按键的状态的函数
void MK_scan();


//既然封装了按键的扫描功能,但是以后按键按下了,或者弹起了,用户有自己的想法
//它们需求千变万化,所以特地声明了两个extern 标记的函数,谁要是使用我们的这一套代码
//就需要在自己的代码里面实现|定义这两个函数,这样就可以捕捉到是按下了按键还是弹起了按键。
//就可以针对性的去处理了。
extern void MK_keydown(u8 row , u8 col);

extern void MK_keyup(u8 row , u8 col);



#endif
MatrixKey.c
#include "MatrixKey.h"

u16 states = 0xFFFF;  //1111 1111 1111 1111

void MK_init(){
  GPIO_InitTypeDef	GPIO_InitStructure;		//结构定义
	GPIO_InitStructure.Pin  = GPIO_Pin_3 | GPIO_Pin_6 | GPIO_Pin_7;		//指定要初始化的IO,
	GPIO_InitStructure.Mode = GPIO_PullUp;	//指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
	GPIO_Inilize(GPIO_P0, &GPIO_InitStructure);//初始化

	GPIO_InitStructure.Pin  =  GPIO_Pin_7;		//指定要初始化的IO,
	GPIO_InitStructure.Mode = GPIO_PullUp;	//指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
	GPIO_Inilize(GPIO_P1, &GPIO_InitStructure);//初始化
	
	GPIO_InitStructure.Pin  =  GPIO_Pin_4  | GPIO_Pin_5;		//指定要初始化的IO,
	GPIO_InitStructure.Mode = GPIO_PullUp;	//指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
	GPIO_Inilize(GPIO_P3, &GPIO_InitStructure);//初始化
	
		
	
	GPIO_InitStructure.Pin  =  GPIO_Pin_0  | GPIO_Pin_1;		//指定要初始化的IO,
	GPIO_InitStructure.Mode = GPIO_PullUp;	//指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
	GPIO_Inilize(GPIO_P4, &GPIO_InitStructure);//初始化
}

u8 get_col(u8 i){
	if(i==0) return COL1;
	if(i==1) return COL2;
	if(i==2) return COL3;
	if(i==3) return COL4;
	return COL1;
}

// 根据遍历的索引下标来拉低对应行的电平状态
void pull_row(u8 i){
	ROW1 = i == 0 ? 0 : 1;
	ROW2 = i == 1 ? 0 : 1;
	ROW3 = i == 2 ? 0 : 1;
	ROW4 = i == 3 ? 0 : 1;
}

void MK_scan(){
	u8 i , j;
	// 判定4行的状态
		
		//外层循环控制的是:行
		for(i  = 0 ; i < 4 ; i++){
			
			// 每遍历一次,就拉低这一行对应的电平状态,拉高其他行的电平状态
			pull_row(i);
			
			//里层循环控制的是: 列
			for(j = 0 ; j < 4  ; j++){
				if(get_col(j) == UP && IS_KEY_DOWN(i, j)){
					SET_KEY_UP(i, j);
					
					 MK_keyup(i, j);
				}else if(get_col(j) == DOWN && IS_KEY_UP(i,j) ){
					SET_KEY_DOWN(i,j);
					
					MK_keydown(i ,j);
				}
			}
		}
}
获取按键状态(通过函数指针封装)
main.c
#include "Delay.h"
#include "NVIC.h"
#include "GPIO.h"
#include "UART.h"
#include "Switch.h"
#include "MKkey.h"

void UART_config(void) {
	// >>> 记得添加 NVIC.c, UART.c, UART_Isr.c <<<
    COMx_InitDefine		COMx_InitStructure;					//结构定义
    COMx_InitStructure.UART_Mode      = UART_8bit_BRTx;	//模式, UART_ShiftRight,UART_8bit_BRTx,UART_9bit,UART_9bit_BRTx
    COMx_InitStructure.UART_BRT_Use   = BRT_Timer1;			//选择波特率发生器, BRT_Timer1, BRT_Timer2 (注意: 串口2固定使用BRT_Timer2)
    COMx_InitStructure.UART_BaudRate  = 115200ul;			//波特率, 一般 110 ~ 115200
    COMx_InitStructure.UART_RxEnable  = ENABLE;				//接收允许,   ENABLE或DISABLE
    COMx_InitStructure.BaudRateDouble = DISABLE;			//波特率加倍, ENABLE或DISABLE
    UART_Configuration(UART1, &COMx_InitStructure);		//初始化串口1 UART1,UART2,UART3,UART4

  	NVIC_UART1_Init(ENABLE,Priority_1);		//中断使能, ENABLE/DISABLE; 优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3
    UART1_SW(UART1_SW_P30_P31);		// 引脚选择, UART1_SW_P30_P31,UART1_SW_P36_P37,UART1_SW_P16_P17,UART1_SW_P43_P44
}

void keydown(u8 row,u8 col){
	printf("%d-%d:按下\n",(int)row+1,(int)col+1);
}

void keyup(u8 row,u8 col){
	printf("%d-%d:弹起\n",(int)row+1,(int)col+1);
}


void main(){
	
	EA = 1;
	KEY_init();
	UART_config();

	while(1){
		
		MK_GET_key(keydown,keyup);
		delay_ms(10);

	}
}
MKkey.h
#ifndef	__MKkey_H
#define	__MKkey_H

#include "GPIO.h"

#define COL1 P03
#define COL2 P06
#define COL3 P07
#define COL4 P17

#define ROW1 P34
#define ROW2 P35
#define ROW3 P40
#define ROW4 P41

	
#define IS_KEY_DOWN(row,col) ((states & (1<<(row * 4 + col))) == 0)
#define IS_KEY_UP(row,col) ((states & (1<<(row * 4 + col))) > 0)
#define SET_KEY_DOWN(row,col) (states &= ~((1<<(row * 4 + col))))
#define SET_KEY_UP(row,col) (states |= (1<<(row * 4 + col)))
#define DOWN 0
#define UP 1


void KEY_init();

void MK_GET_key(void(*keydown)(u8 row,u8 col),void(*keyup)(u8 row,u8 col));

#endif
MKkey.c
#include "MKkey.h"

u16 states = 0xffff;

void KEY_init(){
	GPIO_InitTypeDef	GPIO_InitStructure;		//结构定义
	GPIO_InitStructure.Pin  = GPIO_Pin_3 | GPIO_Pin_6 | GPIO_Pin_7;		//指定要初始化的IO,
	GPIO_InitStructure.Mode = GPIO_PullUp;	//指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
	GPIO_Inilize(GPIO_P0, &GPIO_InitStructure);//初始化
	
	GPIO_InitStructure.Pin  = GPIO_Pin_7;		//指定要初始化的IO,
	GPIO_InitStructure.Mode = GPIO_PullUp;	//指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
	GPIO_Inilize(GPIO_P1, &GPIO_InitStructure);//初始化
	
	GPIO_InitStructure.Pin  = GPIO_Pin_4 | GPIO_Pin_5;		//指定要初始化的IO,
	GPIO_InitStructure.Mode = GPIO_PullUp;	//指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
	GPIO_Inilize(GPIO_P3, &GPIO_InitStructure);//初始化
	
	GPIO_InitStructure.Pin  = GPIO_Pin_0 | GPIO_Pin_1;		//指定要初始化的IO,
	GPIO_InitStructure.Mode = GPIO_PullUp;	//指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
	GPIO_Inilize(GPIO_P4, &GPIO_InitStructure);//初始化
}

u8 get_key(u8 i){
	if(i==0) return COL1;
	if(i==1) return COL2;
	if(i==2) return COL3;
	if(i==3) return COL4;
	return COL1;
		
}

void key_down(u8 i){
	ROW1 = i == 0 ? 0 : 1;
	ROW2 = i == 1 ? 0 : 1;
	ROW3 = i == 2 ? 0 : 1;
	ROW4 = i == 3 ? 0 : 1;
}

void MK_GET_key(void(*keydown)(u8 row,u8 col),void(*keyup)(u8 row,u8 col)){
		int i,j;
	for(i=0;i<4;i++){
			key_down(i);
			for(j=0;j<4;j++){
				if(get_key(j) == UP && IS_KEY_DOWN(i,j)){
					
						SET_KEY_UP(i,j);
					if(keyup!=NULL){
					keyup(i,j);
					}
				
				}else if(get_key(j) == DOWN && IS_KEY_UP(i,j)){
					
					SET_KEY_DOWN(i,j);
					if(keydown!=NULL){
					keydown(i,j);
					}
					
				}
			}
		
		}
}

EEPROM

简介

        EEPROM是一种可擦写可编程只读存储器(Electrically Erasable Programmable Read-Only Memory)的缩写。它是一种非易失性存储器,可以在不需要外部电源的情况下保持存储数据。与ROM不同,EEPROM可以通过电子擦除和编程来修改存储的数据,因此它是一种可重写的存储器。

        EEPROM通常用于存储需要频繁修改的数据,例如系统配置信息、用户设置、校准数据等。由于EEPROM可以在系统运行时进行读写操作,因此它在许多应用中都具有很高的实用价值。

设置EEPROM

TC8H8K64U的EEPROM可以在烧录的时候指定大小, 如下图

代码

#include "Delay.h"
#include "NVIC.h"
#include "UART.h"
#include "Switch.h"
#include "EEPROM.h"
#include "string.h"

void UART_config(void) {
	// >>> 记得添加 NVIC.c, UART.c, UART_Isr.c <<<
    COMx_InitDefine		COMx_InitStructure;					//结构定义
    COMx_InitStructure.UART_Mode      = UART_8bit_BRTx;	//模式, UART_ShiftRight,UART_8bit_BRTx,UART_9bit,UART_9bit_BRTx
    COMx_InitStructure.UART_BRT_Use   = BRT_Timer1;			//选择波特率发生器, BRT_Timer1, BRT_Timer2 (注意: 串口2固定使用BRT_Timer2)
    COMx_InitStructure.UART_BaudRate  = 115200ul;			//波特率, 一般 110 ~ 115200
    COMx_InitStructure.UART_RxEnable  = ENABLE;				//接收允许,   ENABLE或DISABLE
    COMx_InitStructure.BaudRateDouble = DISABLE;			//波特率加倍, ENABLE或DISABLE
    UART_Configuration(UART1, &COMx_InitStructure);		//初始化串口1 UART1,UART2,UART3,UART4

  	NVIC_UART1_Init(ENABLE,Priority_1);		//中断使能, ENABLE/DISABLE; 优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3
    UART1_SW(UART1_SW_P30_P31);		// 引脚选择, UART1_SW_P30_P31,UART1_SW_P36_P37,UART1_SW_P16_P17,UART1_SW_P43_P44
}

u16 EE_address = 0x0000;
xdata	char  str2[100];

void main(){
	
	char *  str = "helloworld";

	
	u16 len  =  strlen(str);
	
	EA = 1 ;
	UART_config();
	
	//=============================操作EEPROM==========================
	
	//1. 擦除EPPROM :: 擦除的起始地址
	EEPROM_SectorErase(EE_address);
	
	//2. 写数据参数一: 写入的起始地址 ,参数二: 写什么数据,参数三,写多少个长度
	EEPROM_write_n(EE_address,str,len);
	
	//3. 读数据参数一: 读取的起始地址 ,参数二: 读取到哪里,参数三,读多少个长度
	EEPROM_read_n(EE_address,str2,len);
	
	
	//因为使用的字符数组来接收数据,它的长度很长,我们需要去设置字符的截止符号
	str2[len] = '\0';
	
	printf("str2=%s\n" , str2);
	

	while(1){
	
	}
}

总结

        今天内容比较容易,但是小伙伴们也一定要多加练习,在验证eeprom时,可以通过以上代码将数据写入进去,然后进行断电重新上电,直接进行读的操作,就会发现我们之前写上的数据仍然可以读取出来,说明数据被我们写里面存储啦。下期再见!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

OJ刷题 第十七篇()

34005 - 汽水瓶&#xff08;有意思&#xff09; 时间限制 : 1 秒 内存限制 : 128 MB 有这样一道智力题&#xff1a;“某商店规定&#xff1a;三个空汽水瓶可以换一瓶汽水。小张手上有十个空汽水瓶&#xff0c;她最多可以换多少瓶汽水喝&#xff1f;”答案是5瓶&#xff0c;方…

Python 测试框架 Pytest 入门

简介 pytest 是一个功能强大而易于使用的 Python 测试框架。它提供了简单的语法和灵活的功能&#xff0c;用于编写和组织测试代码。 1、简单易用&#xff1a;pytest 的语法简洁明了&#xff0c;使得编写测试用例更加直观和易于理解。它使用 assert 语句来验证预期结果&#x…

SpringBoot内嵌的Tomcat启动过程以及请求

1.springboot内嵌的tomcat的pom坐标 启动后可以看到tomcat版本为9.0.46 2.springboot 内嵌tomcat启动流程 点击进入SpringApplication.run()方法里面 看这次tomcat启动相关的核心代码refreshContext(context);刷新上下文方法 public ConfigurableApplicationContext run(Stri…

银行卡号识别

导入库 from typing import Any, Union, Sequencefrom cv2.mat_wrapper import Mat from imutils import contours import numpy as np import argparse import imutils import cv2 import myutils设置参数 # 设置参数 from numpy import dtype, ndarray, genericap argpars…

“巴渝工匠杯”2022年重庆市职业院校技能大赛(高职组)云计算样题

“巴渝工匠杯”2022年重庆市职业院校技能大赛&#xff08;高职组&#xff09;云计算样题 需要软件包环境可私信博主 【赛程名称】云计算赛项第一场次-私有云 某企业拟使用OpenStack搭建一个企业云平台&#xff0c;以实现资源池化弹性管理、企业应用集中管理、统一安全认证和授…

Visual Studio 2022进行文件差异比较

前言 Visual Studio 2022在版本17.7.4中发布在解决方案资源管理器中比较文件的功能&#xff0c;通过使用此功能&#xff0c;可以轻松地查看两个文件之间的差异&#xff0c;包括添加、删除和修改的代码行。可以逐行查看差异&#xff0c;并根据需要手动调整和编辑文件内容以进行…

易图讯便携式三维电子沙盘实战应用系统

便携式三维电子沙盘采用军工加固三防高性能笔记本&#xff0c;具有IP65级防尘防水防摔性能&#xff0c;以大数据、云计算、虚拟现实、物联网、AI等先进技术为支撑&#xff0c;支持高清卫星影像、DEM高程数据、矢量数据、三维模型、倾斜摄像、BIM、点云、城市白模、等高线、标高…

数据结构学习 jz56数组中数字出现的次数

关键词&#xff1a;位运算 异或性质 虽然有两道题&#xff0c;但是其实应该分成三个级别的题目。 题目一&#xff1a; 一个整型数组 sockets 里除 一个 数字之外&#xff0c;其他数字都出现了两次。 思路&#xff1a;异或的性质 复杂度计算&#xff1a; 时间复杂度O(n) 空…

在Go语言中处理HTTP请求中的Cookie

在Web开发中&#xff0c;Cookie是一种常用的技术&#xff0c;用于在客户端存储数据&#xff0c;并在随后的请求中发送回服务器。Go语言的标准库提供了强大的支持来处理HTTP请求中的Cookie。 首先&#xff0c;让我们了解如何在Go语言中设置Cookie。以下是一个简单的示例&#x…

three.js场景设计器-小地图的视角参考功能

three.js实现场景方向的左上角小地图 思路 1&#xff1a;创建单独场景 2.添加辅助线 3.添加坐标轴的XYZ文字-使用sprite实现 4.旋转主视图时同步相机位置到小地图。 <template> <div ref"miniMapContainer" class"mini-map"></div>…

Apache Commons Email在邮件发送中的应用

第1章&#xff1a;简介 大家好&#xff0c;我是小黑&#xff0c;今天咱们聊聊Apache Commons Email这个库&#xff0c;它在发送邮件方面可谓是小而美的利器。Apache Commons Email基于JavaMail API&#xff0c;但它提供了更简洁、更易用的接口&#xff0c;让咱们在处理电子邮件…

如果PostgreSQL有两层nginx代理,会发生什么事?

转载说明&#xff1a;如果您喜欢这篇文章并打算转载它&#xff0c;请私信作者取得授权。感谢您喜爱本文&#xff0c;请文明转载&#xff0c;谢谢。 1. 前言 PostgreSQL默认只能本机连接&#xff0c;若要在别的客户端远程连接pgsql&#xff0c;则需要修改配置文件pg_hba.conf&a…

CommonJS 和 ES6 Module:一场模块规范的对决(下)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

QML —— SwipeView、PageIndicator组合示例(附完整源码)

示例效果 介绍 SwipeView提供了一个基于滑动的导航模型,由一组页面组成。一次只能看到一个页面。用户可以通过横向滑动在页面之间导航。请注意,SwipeView本身是完全不可见的。建议将其与PageIndicator结合使用,以向用户提供有多个页面的视觉线索。 PageIndicator用于指示包含…

UG装配-沿线运动

如果希望图中圆柱销沿着槽运动&#xff0c;直接约束面是困难的&#xff0c;我们可以画出圆弧的中心线和圆柱销的中心点&#xff0c;约束点在线上&#xff0c;进行移动 需要注意的是&#xff0c;我们在零件中画点和线的时候&#xff0c;在装配体默认加载模型引用集的时候是无法显…

生活中的物理3——神奇陷阱(随机倒下的抽屉柜门)

1实验 材料&#xff1a;大自然&#xff08;风&#xff09;、抽屉门松掉的抽屉 实验 1、找一个大风的日子&#xff0c;打开窗户&#xff08;不要找下雨天&#xff0c;不然你会被你亲爱的嫲嫲KO&#xff09; 2、让风在抽屉面前刮过 3、你发现了什么&#xff1f;&#xff1f;&…

南某人:从工厂到品牌的华丽转身!

南某人&#xff0c;这个名字在中国的市场上已经响当当&#xff0c;但你知道吗&#xff1f;这个品牌其实并没有自己的工厂和门店。那么&#xff0c;他们是如何做到年收入高达40亿的呢&#xff1f; 起初&#xff0c;南某人和许多中国品牌一样&#xff0c;从生产保暖内衣起家。然…

傅里叶级数、傅里叶变换、小波变换、离散余弦变换的理解

目录 1. 傅里叶级数2.傅里叶变换 1. 傅里叶级数 功能&#xff1a;能把任意周期性函数展开成一系列正弦、余弦函数的和。 公式&#xff1a; f ( x ) a 0 2 ∑ n 1 ∞ ( a n cos ⁡ ( 2 π n x T ) b n sin ⁡ ( 2 π n x T ) ) 傅里叶系数 a n 2 T ∫ x 0 x 0 T f ( x )…

机器学习(三) -- 特征工程(1)

系列文章目录 机器学习&#xff08;一&#xff09; -- 概述 机器学习&#xff08;二&#xff09; -- 数据预处理&#xff08;1-3&#xff09; 机器学习&#xff08;三&#xff09; -- 特征工程&#xff08;1-2&#xff09; 未完待续…… 目录 系列文章目录 前言 一、特征…

nginx 一、安装与conf浅析

文章目录 一、安装nginxdocker方式安装linux方式安装Ubuntu 或 Debian 系统&#xff1a;CentOS 或 RHEL 系统&#xff1a; macOS 系统&#xff08;使用 Homebrew&#xff09;&#xff1a;Windows 系统&#xff1a; 二、nginx.conf浅析 Nginx&#xff08;发音为“engine-x”&…