51单片机-DS1302(RTC时钟显示,代码内改变,内设的24年9月5日,上午11:12:00)

2024-09-05 12:36

本文主要是介绍51单片机-DS1302(RTC时钟显示,代码内改变,内设的24年9月5日,上午11:12:00),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、DS1302时序及命令字

两个操作:写操作和读操作

写操作:

       (由我们单片机一个控制引脚控制DS1302的IO口写入)首先就是通过时序图把我们的命令字写入,命令字是控制我们对应要写入的年月日,时分秒等配置的关键寄存器,只有设置好命令字相关参数才能写入相关的年月日等时间信息,一个写时序共2个字节,第一个字节是我们的命令字,第二个字节是我们要写入的数据(此数据为16进制写入,最后读的时候我们需要转换成十进制读出),数据和命令字一样通过RCLK的上升沿将数据和命令字(数据就是我们的日期如24年这个数据,写入的时候先转16进制,命令字就是控制时分秒等的寄存器)写入。如下代码:

//单字节写
void DS1302_WriteByte(unsigned char Command,Data)
{unsigned char i;DS1302_CE = 1; //使能开始for(i=0; i<8; i++){DS1302_IO = Command & (0X01 <<i); //取出第0位DS1302_SCLK = 1;DS1302_SCLK = 0;}for(i=0; i<8; i++){DS1302_IO = Data & (0X01 <<i); //取出第0位DS1302_SCLK = 1;DS1302_SCLK = 0;}DS1302_CE = 0; //使能结束
}

读操作:

        同理,单片机读DS1302中的数据就比较简单了,只要我们第一个字节读出对应的命令字(81H:秒的地址)读的地址,第二个字节我们就会利用时序将IO口上的数据一位一位的读出,并保存到指定变量中,即可将数据存入,接下来我们就利用LCD1602的显示函数将其显示出来,(注意:读的时候也是读的16进制,因此,我么显示的时候也需要16进制转换为10进制显示)。代码如下:

//单字节读
unsigned char DS1302_ReadByte(unsigned char Command)
{unsigned char i,Data=0X00;Command |= 0X01; //将写的命令字转换为或的命令字(方便单片机读取DS1302的时间)DS1302_CE = 1;for(i=0; i<8; i++){DS1302_IO = Command & (0x01<<i);DS1302_SCLK = 0;DS1302_SCLK = 1;}for(i=0; i<8; i++){DS1302_SCLK = 1;DS1302_SCLK = 0;if(DS1302_IO){Data|=(0X01<<i); //或等于是有1才1,不会影响Data本身其他位,通过0X01移i位来与Data原来的值相或,Data的第i位会被置0或置1,而不影响其他位(Data本身是一个字节)}}DS1302_CE = 0; //使能结束DS1302_IO = 0;return Data;
}

个人理解:写操作:首先,我们CE和RCLK置为低电平进行时序的初始化,然后,我们就需要配置命令字,第一步就要取出命令字的第0位,将该位放置到IO口上,第二步,我们开始控制SCLK拉高再降低,把数据写入,读就是就比较简单了,自己可以理解看着时序图想一下(Whappy鼓励你)

二、不可调节时钟程序(软件程序初始设置完成)

可以用GPT解释代码(比较简单)(一定要模块化)

main.c

#include <REGX52.H>
#include "LCD1602.h"
#include "DS1302.h"
#include "Delay.h"unsigned char Second,Minute;void main(void)
{LCD_Init();DS1302_Init();DS1302_SetTime();LCD_ShowString(1,1,"  -  -");LCD_ShowString(2,1,"  :  :");while(1){	DS1302_ReadTime();LCD_ShowNum(1,1,DS1302_Time[0],2);LCD_ShowNum(1,4,DS1302_Time[1],2);LCD_ShowNum(1,7,DS1302_Time[2],2);LCD_ShowNum(2,1,DS1302_Time[3],2);LCD_ShowNum(2,4,DS1302_Time[4],2);LCD_ShowNum(2,7,DS1302_Time[5],2);		}
}

DS1302.c

#include <REGX52.H>
#include "DS1302.h"unsigned char DS1302_Time[] = {24,9,5,11,12,00,6};sbit DS1302_SCLK = P3^6;
sbit DS1302_CE = P3^5;
sbit DS1302_IO = P3^4;//写入时间的命令字寄存器配置
#define DS1302_SECOND   0X80   //秒的寄存器配置写入
#define DS1302_MINUTE   0X82
#define DS1302_HOUR     0X84
#define DS1302_DATE     0X86   //日
#define DS1302_MONTH    0X88
#define DS1302_DAY      0X8A  //星期
#define DS1302_YEAR     0X8C
#define DS1302_WP       0X8E//初始化
void DS1302_Init(void)
{DS1302_CE = 0;DS1302_SCLK = 0;
}/*** @brief  DS1302写一个字节* @param  Command 命令字/地址* @param  Data 要写入的数据* @retval 无*/
//单字节写
void DS1302_WriteByte(unsigned char Command,Data)
{unsigned char i;DS1302_CE = 1; //使能开始for(i=0; i<8; i++){DS1302_IO = Command & (0X01 <<i); //取出第0位DS1302_SCLK = 1;DS1302_SCLK = 0;}for(i=0; i<8; i++){DS1302_IO = Data & (0X01 <<i); //取出第0位DS1302_SCLK = 1;DS1302_SCLK = 0;}DS1302_CE = 0; //使能结束
}//void DS1302_WriteByte(unsigned char Command,Data)
//{
//	unsigned char i;
//	DS1302_CE=1;
//	for(i=0;i<8;i++)
//	{
//		DS1302_IO=Command&(0x01<<i);
//		DS1302_SCLK=1;
//		DS1302_SCLK=0;
//	}
//	for(i=0;i<8;i++)
//	{
//		DS1302_IO=Data&(0x01<<i);
//		DS1302_SCLK=1;
//		DS1302_SCLK=0;
//	}
//	DS1302_CE=0;
//}//单字节读
unsigned char DS1302_ReadByte(unsigned char Command)
{unsigned char i,Data=0X00;Command |= 0X01; //将写的命令字转换为或的命令字(方便单片机读取DS1302的时间)DS1302_CE = 1;for(i=0; i<8; i++){DS1302_IO = Command & (0x01<<i);DS1302_SCLK = 0;DS1302_SCLK = 1;}for(i=0; i<8; i++){DS1302_SCLK = 1;DS1302_SCLK = 0;if(DS1302_IO){Data|=(0X01<<i); //或等于是有1才1,不会影响Data本身其他位,通过0X01移i位来与Data原来的值相或,Data的第i位会被置0或置1,而不影响其他位(Data本身是一个字节)}}DS1302_CE = 0; //使能结束DS1302_IO = 0;return Data;
}//unsigned char DS1302_ReadByte(unsigned char Command)
//{
//	unsigned char i,Data=0x00;
//	Command|=0x01;	//将指令转换为读指令
//	DS1302_CE=1;
//	for(i=0;i<8;i++)
//	{
//		DS1302_IO=Command&(0x01<<i);
//		DS1302_SCLK=0;
//		DS1302_SCLK=1;
//	}
//	for(i=0;i<8;i++)
//	{
//		DS1302_SCLK=1;
//		DS1302_SCLK=0;
//		if(DS1302_IO){Data|=(0x01<<i);}
//	}
//	DS1302_CE=0;
//	DS1302_IO=0;	//读取后将IO设置为0,否则读出的数据会出错
//	return Data;
//}void DS1302_SetTime()
{DS1302_WriteByte(DS1302_WP,0X00);//打开写保护,不然烧录不正确DS1302_WriteByte(DS1302_YEAR,DS1302_Time[0]/10*16+DS1302_Time[0]%10);DS1302_WriteByte(DS1302_MONTH,DS1302_Time[1]/10*16+DS1302_Time[1]%10);DS1302_WriteByte(DS1302_DATE,DS1302_Time[2]/10*16+DS1302_Time[2]%10);DS1302_WriteByte(DS1302_HOUR,DS1302_Time[3]/10*16+DS1302_Time[3]%10);DS1302_WriteByte(DS1302_MINUTE,DS1302_Time[4]/10*16+DS1302_Time[4]%10);DS1302_WriteByte(DS1302_SECOND,DS1302_Time[5]/10*16+DS1302_Time[5]%10);DS1302_WriteByte(DS1302_DAY,DS1302_Time[6]/10*16+DS1302_Time[6]%10);DS1302_WriteByte(DS1302_WP,0X80);//关闭写保护,不然烧录不正确
}void DS1302_ReadTime()
{unsigned char Temp;Temp = DS1302_ReadByte(DS1302_YEAR);DS1302_Time[0] = Temp/16*10+Temp%16;Temp = DS1302_ReadByte(DS1302_MONTH);DS1302_Time[1] = Temp/16*10+Temp%16;Temp = DS1302_ReadByte(DS1302_DATE);DS1302_Time[2] = Temp/16*10+Temp%16;Temp = DS1302_ReadByte(DS1302_HOUR);DS1302_Time[3] = Temp/16*10+Temp%16;Temp = DS1302_ReadByte(DS1302_MINUTE);DS1302_Time[4] = Temp/16*10+Temp%16;Temp = DS1302_ReadByte(DS1302_SECOND);DS1302_Time[5] = Temp/16*10+Temp%16;Temp = DS1302_ReadByte(DS1302_DAY);DS1302_Time[6] = Temp/16*10+Temp%16;
}///*
//参考代码//#include <REGX52.H>引脚定义
//sbit DS1302_SCLK=P3^6;
//sbit DS1302_IO=P3^4;
//sbit DS1302_CE=P3^5;寄存器写入地址/指令定义
//#define DS1302_SECOND		0x80
//#define DS1302_MINUTE		0x82
//#define DS1302_HOUR			0x84
//#define DS1302_DATE			0x86
//#define DS1302_MONTH		0x88
//#define DS1302_DAY			0x8A
//#define DS1302_YEAR			0x8C
//#define DS1302_WP			0x8E时间数组,索引0~6分别为年、月、日、时、分、秒、星期
//unsigned char DS1302_Time[]={19,11,16,12,59,55,6};///**
//  * @brief  DS1302初始化
//  * @param  无
//  * @retval 无
//  */
//void DS1302_Init(void)
//{
//	DS1302_CE=0;
//	DS1302_SCLK=0;
//}///**
//  * @brief  DS1302写一个字节
//  * @param  Command 命令字/地址
//  * @param  Data 要写入的数据
//  * @retval 无
//  */
//void DS1302_WriteByte(unsigned char Command,Data)
//{
//	unsigned char i;
//	DS1302_CE=1;
//	for(i=0;i<8;i++)
//	{
//		DS1302_IO=Command&(0x01<<i);
//		DS1302_SCLK=1;
//		DS1302_SCLK=0;
//	}
//	for(i=0;i<8;i++)
//	{
//		DS1302_IO=Data&(0x01<<i);
//		DS1302_SCLK=1;
//		DS1302_SCLK=0;
//	}
//	DS1302_CE=0;
//}///**
//  * @brief  DS1302读一个字节
//  * @param  Command 命令字/地址
//  * @retval 读出的数据
//  */
//unsigned char DS1302_ReadByte(unsigned char Command)
//{
//	unsigned char i,Data=0x00;
//	Command|=0x01;	//将指令转换为读指令
//	DS1302_CE=1;
//	for(i=0;i<8;i++)
//	{
//		DS1302_IO=Command&(0x01<<i);
//		DS1302_SCLK=0;
//		DS1302_SCLK=1;
//	}
//	for(i=0;i<8;i++)
//	{
//		DS1302_SCLK=1;
//		DS1302_SCLK=0;
//		if(DS1302_IO){Data|=(0x01<<i);}
//	}
//	DS1302_CE=0;
//	DS1302_IO=0;	//读取后将IO设置为0,否则读出的数据会出错
//	return Data;
//}///**
//  * @brief  DS1302设置时间,调用之后,DS1302_Time数组的数字会被设置到DS1302中
//  * @param  无
//  * @retval 无
//  */
//void DS1302_SetTime(void)
//{
//	DS1302_WriteByte(DS1302_WP,0x00);
//	DS1302_WriteByte(DS1302_YEAR,DS1302_Time[0]/10*16+DS1302_Time[0]%10);//十进制转BCD码后写入
//	DS1302_WriteByte(DS1302_MONTH,DS1302_Time[1]/10*16+DS1302_Time[1]%10);
//	DS1302_WriteByte(DS1302_DATE,DS1302_Time[2]/10*16+DS1302_Time[2]%10);
//	DS1302_WriteByte(DS1302_HOUR,DS1302_Time[3]/10*16+DS1302_Time[3]%10);
//	DS1302_WriteByte(DS1302_MINUTE,DS1302_Time[4]/10*16+DS1302_Time[4]%10);
//	DS1302_WriteByte(DS1302_SECOND,DS1302_Time[5]/10*16+DS1302_Time[5]%10);
//	DS1302_WriteByte(DS1302_DAY,DS1302_Time[6]/10*16+DS1302_Time[6]%10);
//	DS1302_WriteByte(DS1302_WP,0x80);
//}///**
//  * @brief  DS1302读取时间,调用之后,DS1302中的数据会被读取到DS1302_Time数组中
//  * @param  无
//  * @retval 无
//  */
//void DS1302_ReadTime(void)
//{
//	unsigned char Temp;
//	Temp=DS1302_ReadByte(DS1302_YEAR);
//	DS1302_Time[0]=Temp/16*10+Temp%16;//BCD码转十进制后读取
//	Temp=DS1302_ReadByte(DS1302_MONTH);
//	DS1302_Time[1]=Temp/16*10+Temp%16;
//	Temp=DS1302_ReadByte(DS1302_DATE);
//	DS1302_Time[2]=Temp/16*10+Temp%16;
//	Temp=DS1302_ReadByte(DS1302_HOUR);
//	DS1302_Time[3]=Temp/16*10+Temp%16;
//	Temp=DS1302_ReadByte(DS1302_MINUTE);
//	DS1302_Time[4]=Temp/16*10+Temp%16;
//	Temp=DS1302_ReadByte(DS1302_SECOND);
//	DS1302_Time[5]=Temp/16*10+Temp%16;
//	Temp=DS1302_ReadByte(DS1302_DAY);
//	DS1302_Time[6]=Temp/16*10+Temp%16;
//}//*/

DS1302.h

#ifndef __DS1302_H__
#define __DS1302_H__extern unsigned char DS1302_Time[];void DS1302_WriteByte(unsigned char Command,Data);  //单片机写操作
unsigned char DS1302_ReadByte(unsigned char Command); //单片机读操作
void DS1302_Init(void);
void DS1302_ReadTime(); //单片机读出时间
void DS1302_SetTime();  //设置时间#endif

LCD1602.c

#include <REGX52.H>//引脚配置:
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0//函数定义:
/*** @brief  LCD1602延时函数,12MHz调用可延时1ms* @param  无* @retval 无*/
void LCD_Delay()
{unsigned char i, j;i = 2;j = 239;do{while (--j);} while (--i);
}/*** @brief  LCD1602写命令* @param  Command 要写入的命令* @retval 无*/
void LCD_WriteCommand(unsigned char Command)
{LCD_RS=0;LCD_RW=0;LCD_DataPort=Command;LCD_EN=1;LCD_Delay();LCD_EN=0;LCD_Delay();
}/*** @brief  LCD1602写数据* @param  Data 要写入的数据* @retval 无*/
void LCD_WriteData(unsigned char Data)
{LCD_RS=1;LCD_RW=0;LCD_DataPort=Data;LCD_EN=1;LCD_Delay();LCD_EN=0;LCD_Delay();
}/*** @brief  LCD1602设置光标位置* @param  Line 行位置,范围:1~2* @param  Column 列位置,范围:1~16* @retval 无*/
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{if(Line==1){LCD_WriteCommand(0x80|(Column-1));}else if(Line==2){LCD_WriteCommand(0x80|(Column-1+0x40));}
}/*** @brief  LCD1602初始化函数* @param  无* @retval 无*/
void LCD_Init()
{LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动LCD_WriteCommand(0x01);//光标复位,清屏
}/*** @brief  在LCD1602指定位置上显示一个字符* @param  Line 行位置,范围:1~2* @param  Column 列位置,范围:1~16* @param  Char 要显示的字符* @retval 无*/
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
{LCD_SetCursor(Line,Column);LCD_WriteData(Char);
}/*** @brief  在LCD1602指定位置开始显示所给字符串* @param  Line 起始行位置,范围:1~2* @param  Column 起始列位置,范围:1~16* @param  String 要显示的字符串* @retval 无*/
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{unsigned char i;LCD_SetCursor(Line,Column);for(i=0;String[i]!='\0';i++){LCD_WriteData(String[i]);}
}/*** @brief  返回值=X的Y次方*/
int LCD_Pow(int X,int Y)
{unsigned char i;int Result=1;for(i=0;i<Y;i++){Result*=X;}return Result;
}/*** @brief  在LCD1602指定位置开始显示所给数字* @param  Line 起始行位置,范围:1~2* @param  Column 起始列位置,范围:1~16* @param  Number 要显示的数字,范围:0~65535* @param  Length 要显示数字的长度,范围:1~5* @retval 无*/
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{unsigned char i;LCD_SetCursor(Line,Column);for(i=Length;i>0;i--){LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');}
}/*** @brief  在LCD1602指定位置开始以有符号十进制显示所给数字* @param  Line 起始行位置,范围:1~2* @param  Column 起始列位置,范围:1~16* @param  Number 要显示的数字,范围:-32768~32767* @param  Length 要显示数字的长度,范围:1~5* @retval 无*/
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
{unsigned char i;unsigned int Number1;LCD_SetCursor(Line,Column);if(Number>=0){LCD_WriteData('+');Number1=Number;}else{LCD_WriteData('-');Number1=-Number;}for(i=Length;i>0;i--){LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');}
}/*** @brief  在LCD1602指定位置开始以十六进制显示所给数字* @param  Line 起始行位置,范围:1~2* @param  Column 起始列位置,范围:1~16* @param  Number 要显示的数字,范围:0~0xFFFF* @param  Length 要显示数字的长度,范围:1~4* @retval 无*/
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{unsigned char i,SingleNumber;LCD_SetCursor(Line,Column);for(i=Length;i>0;i--){SingleNumber=Number/LCD_Pow(16,i-1)%16;if(SingleNumber<10){LCD_WriteData(SingleNumber+'0');}else{LCD_WriteData(SingleNumber-10+'A');}}
}/*** @brief  在LCD1602指定位置开始以二进制显示所给数字* @param  Line 起始行位置,范围:1~2* @param  Column 起始列位置,范围:1~16* @param  Number 要显示的数字,范围:0~1111 1111 1111 1111* @param  Length 要显示数字的长度,范围:1~16* @retval 无*/
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{unsigned char i;LCD_SetCursor(Line,Column);for(i=Length;i>0;i--){LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');}
}

LCD1602.h

#ifndef __LCD1602_H__
#define __LCD1602_H__//用户调用函数:
void LCD_Init();
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char);
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String);
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length);
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);#endif

Whappy:下一篇文章(可调时钟)

这篇关于51单片机-DS1302(RTC时钟显示,代码内改变,内设的24年9月5日,上午11:12:00)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1138979

相关文章

第10章 中断和动态时钟显示

第10章 中断和动态时钟显示 从本章开始,按照书籍的划分,第10章开始就进入保护模式(Protected Mode)部分了,感觉从这里开始难度突然就增加了。 书中介绍了为什么有中断(Interrupt)的设计,中断的几种方式:外部硬件中断、内部中断和软中断。通过中断做了一个会走的时钟和屏幕上输入字符的程序。 我自己理解中断的一些作用: 为了更好的利用处理器的性能。协同快速和慢速设备一起工作

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来

安卓链接正常显示,ios#符被转义%23导致链接访问404

原因分析: url中含有特殊字符 中文未编码 都有可能导致URL转换失败,所以需要对url编码处理  如下: guard let allowUrl = webUrl.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {return} 后面发现当url中有#号时,会被误伤转义为%23,导致链接无法访问

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

poj 1258 Agri-Net(最小生成树模板代码)

感觉用这题来当模板更适合。 题意就是给你邻接矩阵求最小生成树啦。~ prim代码:效率很高。172k...0ms。 #include<stdio.h>#include<algorithm>using namespace std;const int MaxN = 101;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int n

day-51 合并零之间的节点

思路 直接遍历链表即可,遇到val=0跳过,val非零则加在一起,最后返回即可 解题过程 返回链表可以有头结点,方便插入,返回head.next Code /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}*

计算机毕业设计 大学志愿填报系统 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点赞 👍 收藏 ⭐评论 📝 🍅 文末获取源码联系 👇🏻 精彩专栏推荐订阅 👇🏻 不然下次找不到哟~Java毕业设计项目~热门选题推荐《1000套》 目录 1.技术选型 2.开发工具 3.功能

代码随想录冲冲冲 Day39 动态规划Part7

198. 打家劫舍 dp数组的意义是在第i位的时候偷的最大钱数是多少 如果nums的size为0 总价值当然就是0 如果nums的size为1 总价值是nums[0] 遍历顺序就是从小到大遍历 之后是递推公式 对于dp[i]的最大价值来说有两种可能 1.偷第i个 那么最大价值就是dp[i-2]+nums[i] 2.不偷第i个 那么价值就是dp[i-1] 之后取这两个的最大值就是d

pip-tools:打造可重复、可控的 Python 开发环境,解决依赖关系,让代码更稳定

在 Python 开发中,管理依赖关系是一项繁琐且容易出错的任务。手动更新依赖版本、处理冲突、确保一致性等等,都可能让开发者感到头疼。而 pip-tools 为开发者提供了一套稳定可靠的解决方案。 什么是 pip-tools? pip-tools 是一组命令行工具,旨在简化 Python 依赖关系的管理,确保项目环境的稳定性和可重复性。它主要包含两个核心工具:pip-compile 和 pip

基于51单片机的自动转向修复系统的设计与实现

文章目录 前言资料获取设计介绍功能介绍设计清单具体实现截图参考文献设计获取 前言 💗博主介绍:✌全网粉丝10W+,CSDN特邀作者、博客专家、CSDN新星计划导师,一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等单片机设计 主要对象是咱们电子相关专业的大学生,希望您们都共创辉煌!✌💗 👇🏻 精彩专栏 推荐订阅👇🏻 单片机