单片机开发者的福音: 让AI看电路图帮你编写程序
这里使用的人工智能是 OpenAI公司开发的 ChatGPT.为了让AI用起来更加的顺手我们可以选择单片机工程师这个训练好的模型,当然用原本的ChatGPT-4o也是挺好用的.
第0步 需要准备东西
1. 科学上网
2. 电路图 (可以是pdf文件\也可是高清的png或者jpg图片)
第1步 寻找一个适合自己的模型
可以点开链接直达这个模型单片机工程师 with keil uvision 5,或者登录后按照下图的方式找到这个模型.
第2步 沟通方式
公式 : 介绍+目的+要求
例子:
因为chatGPT 联系上下文的能力很强, 除了第一次沟通的时候发送文件和介绍之外,在此对话的时候就可以不用发送了.就是当做一个仆人一样来对话就好, 它给我们程序,我们拿去编译 下载就好,不满足我们的要求就接着和它沟通,只要你把问题和你想要的效果描述清楚,它基本上都能完成的很好,大大提高你的工作效率.
看看这次AI的回答:
我们可以看到 , 它给出了
#include "lcd1602.h"
#include "ds1302.h"
这两个库我们是没有的,所以呢 我们具体的情况还要给ai说明白,我们没有ds1302这个芯片, 也没有lcd1602.h这个库,需要自己写
进一步说明情况
请看ai的这次回答: 我个人认为应该是没什么问题,它把lcd1602模块给单独拿来出来方便我们复用,下面是ai的回答:
好的,我们可以不使用DS1302芯片,直接在程序中预设时间信息为“23:55”,并使用定时器来模拟时间的递增。以下是修改后的完整代码,包括LCD1602的驱动程序和时间的显示功能。
LCD1602驱动程序 (
lcd1602.h
和lcd1602.c
)lcd1602.h
#ifndef _LCD1602_H_ #define _LCD1602_H_ #include <reg51.h> sbit RS = P2^0; // RS引脚 sbit RW = P2^1; // RW引脚 sbit E = P2^2; // E引脚 sbit D4 = P2^4; // 数据引脚 D4 sbit D5 = P2^5; // 数据引脚 D5 sbit D6 = P2^6; // 数据引脚 D6 sbit D7 = P2^7; // 数据引脚 D7 void LCD_Init(void); void LCD_Cmd(unsigned char cmd); void LCD_Data(unsigned char dat); void LCD_ShowString(unsigned char x, unsigned char y, char *str); void LCD_ShowNum(unsigned char x, unsigned char y, unsigned int num, unsigned char len); void LCD_ShowChar(unsigned char x, unsigned char y, char ch); #endif
lcd1602.c
#include "lcd1602.h" // 延时函数 void Delay(unsigned int ms) { unsigned int i, j; for (i = 0; i < ms; i++) for (j = 0; j < 114; j++); } // 发送命令到LCD void LCD_Cmd(unsigned char cmd) { RS = 0; RW = 0; E = 1; D4 = cmd & 0x10; D5 = cmd & 0x20; D6 = cmd & 0x40; D7 = cmd & 0x80; E = 0; E = 1; D4 = cmd & 0x01; D5 = cmd & 0x02; D6 = cmd & 0x04; D7 = cmd & 0x08; E = 0; Delay(2); } // 发送数据到LCD void LCD_Data(unsigned char dat) { RS = 1; RW = 0; E = 1; D4 = dat & 0x10; D5 = dat & 0x20; D6 = dat & 0x40; D7 = dat & 0x80; E = 0; E = 1; D4 = dat & 0x01; D5 = dat & 0x02; D6 = dat & 0x04; D7 = dat & 0x08; E = 0; Delay(2); } // 初始化LCD1602 void LCD_Init(void) { LCD_Cmd(0x02); // 初始化指令 LCD_Cmd(0x28); // 4位数据接口, 2行显示, 5x7点阵 LCD_Cmd(0x0C); // 显示开,光标关 LCD_Cmd(0x06); // 数据输入后,光标右移 LCD_Cmd(0x01); // 清屏 } // 显示字符串 void LCD_ShowString(unsigned char x, unsigned char y, char *str) { unsigned char addr; if (y == 0) addr = 0x80 + x; else addr = 0xC0 + x; LCD_Cmd(addr); while (*str) LCD_Data(*str++); } // 显示数字 void LCD_ShowNum(unsigned char x, unsigned char y, unsigned int num, unsigned char len) { unsigned char t, temp; unsigned char show_flag = 0; for (t = 0; t < len; t++) { temp = (num / 10000) % 10; if (show_flag == 0 && t < len - 1) { if (temp == 0) LCD_Data(' '); else { show_flag = 1; LCD_Data(temp + '0'); } } else LCD_Data(temp + '0'); num *= 10; } } // 显示单个字符 void LCD_ShowChar(unsigned char x, unsigned char y, char ch) { unsigned char addr; if (y == 0) addr = 0x80 + x; else addr = 0xC0 + x; LCD_Cmd(addr); LCD_Data(ch); }
主程序 (
main.c
)#include <reg51.h> #include "lcd1602.h" // 定义全局变量来存储时间 unsigned char hour = 23, minute = 55, second = 0; // 定时器中断函数,每1秒调用一次 void Timer0_ISR(void) interrupt 1 { TH0 = 0xFC; // 重装载定时器初值 TL0 = 0x18; second++; if (second == 60) { second = 0; minute++; if (minute == 60) { minute = 0; hour++; if (hour == 24) hour = 0; } } } // 定时器0初始化 void Timer0_Init() { TMOD = 0x01; // 设置定时器0为模式1(16位定时器) TH0 = 0xFC; // 设置定时器初值,使得1秒触发一次中断 TL0 = 0x18; ET0 = 1; // 使能定时器0中断 EA = 1; // 使能总中断 TR0 = 1; // 启动定时器0 } void main() { LCD_Init(); // 初始化LCD1602 Timer0_Init(); // 初始化定时器 while(1) { // 在LCD1602上显示时间 LCD_ShowString(0, 0, "Time:"); LCD_ShowNum(5, 0, hour, 2); // 显示小时 LCD_ShowChar(7, 0, ':'); LCD_ShowNum(8, 0, minute, 2); // 显示分钟 LCD_ShowChar(10, 0, ':'); LCD_ShowNum(11, 0, second, 2); // 显示秒 Delay(1000); // 刷新间隔 } }
说明:
- LCD1602驱动:
lcd1602.h
和lcd1602.c
文件实现了LCD1602的初始化和显示功能,包括显示字符串、数字和单个字符。- 定时器模拟时钟:定时器0的中断每秒触发一次,用于更新秒、分钟和小时变量,并将其显示在LCD1602上。
- 程序结构:主程序通过循环不断更新LCD1602上的时间显示。
编译和运行:
- 将上述代码分别保存到
lcd1602.h
、lcd1602.c
和main.c
中。- 使用Keil uVision 5进行编译并将程序下载到开发板上。
- 观察LCD1602上显示的时间变化。
这个程序将在开始时显示"23:55",然后每秒自动递增,模拟时钟功能。
总之它给程序,你就拿到你的板子上进行调试即可,还不满意你就让它改.
但是由于你不是plus用户, 用多了可能就回出现(提醒你该充钱或者切换账号)
没钱有没钱的办法,可以使用 copilot 请看这篇文章【51单片机】让AI识别电路图,帮你进行编码(以51单片机为例)
这是我让AI实现的万年历功能,阅读代码其实我们不难看出ai的创造性思维是不如人类的, 就比如它不知道如何把独立按键发挥到极致,让时间日期数值可以增加可以减小,程序中它只有增加没有减小的逻辑,对用户来说体验感就大打折扣.我们能做的就是继续和它沟通完善程序,或者自己修改程序.
要实现一个万年历功能,显示时间和日期,并且可以通过独立按键修改时间和日期,需要按照以下步骤进行设计和实现。这个程序将使用8051单片机、LCD1602显示屏、以及独立按键来实现时间和日期的显示和调整。
功能实现概要:
- 显示内容:在LCD1602上显示当前的日期和时间。
- 时间日期初始化:初始时间为2024年8月15日16:19:15。
- 时间更新:使用定时器中断每秒更新一次时间。
- 独立按键调整时间日期:使用三个独立按键分别调整小时、分钟和日期。
硬件连接假设:
- LCD1602:数据线接P2口,控制线RS、RW、E分别接P2.0、P2.1、P2.2。
- 按键:三个独立按键分别接P3.0(调整小时)、P3.1(调整分钟)、P3.2(调整日期)。
万年历功能代码
lcd1602.h
#ifndef _LCD1602_H_ #define _LCD1602_H_ #include <reg51.h> sbit RS = P2^0; // RS引脚 sbit RW = P2^1; // RW引脚 sbit E = P2^2; // E引脚 sbit D4 = P2^4; // 数据引脚 D4 sbit D5 = P2^5; // 数据引脚 D5 sbit D6 = P2^6; // 数据引脚 D6 sbit D7 = P2^7; // 数据引脚 D7 void LCD_Init(void); void LCD_Cmd(unsigned char cmd); void LCD_Data(unsigned char dat); void LCD_ShowString(unsigned char x, unsigned char y, char *str); void LCD_ShowNum(unsigned char x, unsigned char y, unsigned int num, unsigned char len); void LCD_ShowChar(unsigned char x, unsigned char y, char ch); #endif
lcd1602.c
#include "lcd1602.h" // 延时函数 void Delay(unsigned int ms) { unsigned int i, j; for (i = 0; i < ms; i++) for (j = 0; j < 114; j++); } // 发送命令到LCD void LCD_Cmd(unsigned char cmd) { RS = 0; RW = 0; E = 1; D4 = cmd & 0x10; D5 = cmd & 0x20; D6 = cmd & 0x40; D7 = cmd & 0x80; E = 0; E = 1; D4 = cmd & 0x01; D5 = cmd & 0x02; D6 = cmd & 0x04; D7 = cmd & 0x08; E = 0; Delay(2); } // 发送数据到LCD void LCD_Data(unsigned char dat) { RS = 1; RW = 0; E = 1; D4 = dat & 0x10; D5 = dat & 0x20; D6 = dat & 0x40; D7 = dat & 0x80; E = 0; E = 1; D4 = dat & 0x01; D5 = dat & 0x02; D6 = dat & 0x04; D7 = dat & 0x08; E = 0; Delay(2); } // 初始化LCD1602 void LCD_Init(void) { LCD_Cmd(0x02); // 初始化指令 LCD_Cmd(0x28); // 4位数据接口, 2行显示, 5x7点阵 LCD_Cmd(0x0C); // 显示开,光标关 LCD_Cmd(0x06); // 数据输入后,光标右移 LCD_Cmd(0x01); // 清屏 } // 显示字符串 void LCD_ShowString(unsigned char x, unsigned char y, char *str) { unsigned char addr; if (y == 0) addr = 0x80 + x; else addr = 0xC0 + x; LCD_Cmd(addr); while (*str) LCD_Data(*str++); } // 显示数字 void LCD_ShowNum(unsigned char x, unsigned char y, unsigned int num, unsigned char len) { unsigned char t, temp; unsigned char show_flag = 0; for (t = 0; t < len; t++) { temp = (num / 10000) % 10; if (show_flag == 0 && t < len - 1) { if (temp == 0) LCD_Data(' '); else { show_flag = 1; LCD_Data(temp + '0'); } } else LCD_Data(temp + '0'); num *= 10; } } // 显示单个字符 void LCD_ShowChar(unsigned char x, unsigned char y, char ch) { unsigned char addr; if (y == 0) addr = 0x80 + x; else addr = 0xC0 + x; LCD_Cmd(addr); LCD_Data(ch); }
主程序 (
main.c
)#include <reg51.h> #include "lcd1602.h" // 定义引脚 sbit Key1 = P3^0; // 调整小时 sbit Key2 = P3^1; // 调整分钟 sbit Key3 = P3^2; // 调整日期 // 时间和日期变量 unsigned int year = 2024; unsigned char month = 8, day = 15, hour = 16, minute = 19, second = 15; // 检测按键按下状态的函数 bit Key_Pressed(sbit key) { if (!key) { // 如果按键被按下 Delay(20); // 消抖延时 if (!key) { // 确认按下 while (!key); // 等待按键释放 return 1; } } return 0; } // 更新时间函数 void Update_Time() { second++; if (second == 60) { second = 0; minute++; if (minute == 60) { minute = 0; hour++; if (hour == 24) { hour = 0; day++; // 判断每月天数 if ((month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12) && day == 32) { day = 1; month++; } else if ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) { day = 1; month++; } else if (month == 2 && (year % 4 == 0 && year % 100 != 0 || year % 400 == 0)) { // 闰年 if (day == 30) { day = 1; month++; } } else if (month == 2 && day == 29) { day = 1; month++; } if (month == 13) { month = 1; year++; } } } } } // 定时器中断函数,每1秒调用一次 void Timer0_ISR(void) interrupt 1 { TH0 = 0xFC; // 重装载定时器初值 TL0 = 0x18; Update_Time(); } // 定时器0初始化 void Timer0_Init() { TMOD = 0x01; // 设置定时器0为模式1(16位定时器) TH0 = 0xFC; // 设置定时器初值,使得1秒触发一次中断 TL0 = 0x18; ET0 = 1; // 使能定时器0中断 EA = 1; // 使能总中断 TR0 = 1; // 启动定时器0 } void main() { LCD_Init(); // 初始化LCD1602 Timer0_Init(); // 初始化定时器 while(1) { // 检测按键,调整时间和日期 if (Key_Pressed(Key1)) { hour++; if (hour == 24) hour = 0; } if (Key_Pressed(Key2)) { minute++; if (minute == 60) minute = 0; } if (Key_Pressed(Key3)) { day++; // 需要根据月份判断天数 if ((month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12) && day == 32) { day = 1; month++; } else if ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) { day = 1; month++; } else if (month == 2 && (year % 4 == 0 && year % 100 != 0 || year % 400 == 0)) { // 闰年 if (day == 30) { day = 1; month++; } } else if (month == 2 && day == 29) { day = 1; month++; } if (month == 13) { month = 1; year++; } } // 在LCD1602上显示日期 LCD_ShowString(0, 0, "Date:"); LCD_ShowNum(5, 0, year, 4); // 显示年份 LCD_ShowChar(9, 0, '-'); LCD_ShowNum(10, 0, month, 2); // 显示月份 LCD_ShowChar(12, 0, '-'); LCD_ShowNum(13, 0, day, 2); // 显示日期 // 在LCD1602上显示时间 LCD_ShowString(0, 1, "Time:"); LCD_ShowNum(5, 1, hour, 2); // 显示小时 LCD_ShowChar(7, 1, ':'); LCD_ShowNum(8, 1, minute, 2); // 显示分钟 LCD_ShowChar(10, 1, ':'); LCD_ShowNum(11, 1, second, 2); // 显示秒 Delay(100); // 刷新间隔 } }
说明:
- LCD1602驱动:
lcd1602.h
和lcd1602.c
文件实现了LCD1602的初始化和显示功能,包括显示字符串、数字和单个字符。- 时间和日期更新:定时器0中断每秒触发一次,更新秒、分钟、小时、天、月和年。
- 按键调整:三个独立按键分别用于调整小时、分钟和日期。
- 闰年判断:程序中对2月份日期进行了闰年的判断和处理。
- 程序结构:主程序通过循环不断检测按键输入,并在LCD1602上实时更新日期和时间显示。
编译和运行:
- 将上述代码分别保存到
lcd1602.h
、lcd1602.c
和main.c
中。- 使用Keil uVision 5进行编译,并将程序下载到开发板上。
- 通过按键调整时间和日期,并观察LCD1602上显示的结果。
这个程序实现了一个简单的万年历功能,可以显示时间和日期,并且通过按键进行调整。你可以根据需求进一步修改或扩展功能。
当然,AI对程序某个部分的驱动其实是值得我们学习的, 比如按键消抖和lcd1602的驱动等等。如果你是初学的你可能很难理解按键消抖,也很难理解LCD1602的时序图,然后对着时序图 读状态 写指令 读数据 写数据。 这东西对曾经只想完成老师作业的我来说简直就是一种折磨。