目录
一、串口屏
1.1、接收数据
1.2、发送数据
1.3、显示曲线
1.4、改变亮度
二、EG8010
2.1、接收发送数据
2.2、读电压电流温度频率
2.3、改变输出电压频率
三、RS485
3.1、485初始化
3.2、接收数据响应函数
四、WiFi
4.1、模拟串口流程图
4.2、模拟串口部分代码
五、EEPROM(断电数据保护)
六、力控(vb代码)
6.1、用到的函数
6.2、登录窗口
6.3、顶部窗口
6.4、监视窗口
七、视频演示
7.1、串口屏单片机
7.2、力控
八、离网硬件
8.1、逆变升压
8.2、驱动部分
九、呼吸灯
主控IAP15W4K61S4,外设有串口屏,8010,485,WiFi,都是通过串口实现通讯。EEPROM实现断电数据保护。
光伏电子工程的设计与实施2022赛题2单片机代码资源-CSDN文库
一、串口屏
单片机使用串口2接收和发送数据
实现功能有:改变屏幕的亮度,采集输出电压电流(adc)频率(eg8010)显示在串口屏,改变输出频率电压(接收串口屏数据,通过8010改变)。
1.1、接收数据
接收串口屏数据,因为数据字节数是无规律的,所以接收数据使用到了队列方式接收。
- 队列
用了比较简单的方式,循环队列。
大体思路就是:结构体变量(数组,头尾变量作为数组的下标)
typedef struct _QUEUE
{
u16 _head; //数据栈头指针
u16 _tail; //数据栈尾指针
u8 _data[QUEUE_MAX_SIZE]; //#define QUEUE_MAX_SIZE 80 ,队列长度
}QUEUE;
- 数据放入
void queue_push(u8 _data)//数据一个一个放入FIFO,串口屏
{
u16 pos;
pos = (que._tail+1)%QUEUE_MAX_SIZE; //
if(pos!=que._head)//非满状态
{
que._data[que._tail]=_data;
que._tail = pos;
}
}
- 数据取出
static void queue_pop(u8* _data)
{
if(que._tail!=que._head)//非空状态
{
*_data = que._data[que._head];
que._head = (que._head+1)%QUEUE_MAX_SIZE; //栈尾指向下一个指令头部? ? ? ?
}
}
1.2、发送数据
- 单片机发送给串口屏数据很简单,代码如下发送一个字节数据
void Uart2_SendData(unsigned char dat) //串口2发送数据
{
S2BUF = dat;//写数据到UART2数据寄存器
while(!(S2CON&S2TI));//等待串口发送完成
S2CON&=~S2TI;//发送中断标志位置0
}
- 发送2字节数据,把int拆分开高8位低8位分别发送
void SendDatas(u16 str)
{
Uart2_SendData((u8)((str>>8)&0xff));
Uart2_SendData((u8)(str&0xff));
}
- 串口屏显示的是文本,使用有时候要把整形数转为字符串,然后发送
sprintf(Text_buf,"%d",dat); //把整型dat转为字符串Text_buf
1.3、显示曲线
1、采集2个ad(电压 电流)
2、比较2ad,判断是否从曲线零点开始
3、循环采集屏的宽度+5个ad(滤波需要多出5个值)
4、滤波(取5个连续ad的平均值),比较出峰值谷值
5、发送数据给串口屏显示
1.4、改变亮度
接收串口屏的数据指令,发送改变亮度指令
二、EG8010
使用的串口3,数据接收到数组里面,因为数据很固定,都是发送2字节返回4字节数据
注:eg需要初始化
2.1、接收发送数据
- 接收
void UART3_Routine(void) interrupt 17 //串口3中断
{
if(S3CON&S3RI) //等待串口接收完成
{
S3CON&=~S3RI; //手动清零接收中断标志位
eg8010_buf[eg_count++]=S3BUF;//压入到指令缓冲区
}
}
- 发送
void Uart3_SendData(u8 dat)//串口3发送1个字节数据
{
S3BUF=dat;//写数据到UART3数据寄存器
while(!(S3CON&S3TI));//等待发送中断标志位置1
S3CON&=~S3TI;//将发送中断标志位置0
}
2.2、读电压电流温度频率
1、发送读取指令
2、等待数据接收完成
3、读取接收到的数据,4字节,分别是电压电流温度频率
2.3、改变输出电压频率
1、接收到串口屏的改变电压(频率)命令
2、发送给eg8010改变电压命令改变输出电压,控制数据命令改变输出频率
三、RS485
3.1、485初始化
使能脚定义,串口4初始化
sbit EN_485=P0^4; //485使能脚
void RS485_init()//485初始化
{
Uart4_init();
EN_485 = 0;//接收状态
}
3.2、接收数据响应函数
- 完整函数
接收到数据,根据功能码去处理,如果5ms还没有处理就摒弃掉这次数据
void check_modbus(void)
{
unsigned int crcData,tempData,temp;
timeProc();//定时处理,数据保留5ms
if(receCount>4) //如果接收到数据
{
if(receBuf[0]==LocalAddr) //核对地址
{
if(receBuf[1]<10)
{
if(receCount>=8) //数据大于8个进入,地址1 功能码1 4 校验码2
{
crcData = CRC16_Check(receBuf,6); //核对校验码
temp = (receBuf[7]<<8)+receBuf[6];
if(temp==crcData)
{
switch(receBuf[1]) //读取功能码
{
case 1: readCoils(); break; //读取线圈输出状态(一个或多个)
case 2: readInPutCoils(); break; //读取线圈输入(只读寄存器)状态
case 3: readRegisters(); break; //读取多个寄存器值
case 5: forceSingleCoil(); break; //强制单个线圈
case 6: presetSingleRegister(); break; //设置单个寄存器
default:break;
}
}
}
}
else if(receBuf[1]==16)
{
tempData = (receBuf[4]<<8) + receBuf[5]; //设置寄存器个数
tempData = tempData * 2; //数据个数= 寄存器*2
tempData += 9; //从询问数据包格式可知,receCount应该等于9+byteCount
if(receCount>=tempData)
{
crcData = CRC16_Check(receBuf,tempData-2);
if(crcData==((receBuf[tempData-1]<<8)+ receBuf[tempData-2]))
if(receBuf[1]==16)
presetMultipleRegisters();
receCount=0;
}
}
}
}
}
- 定时处理
从接收到最后一个数据开始计时,5ms后数据清理
void timeProc(void)
{
if(bt1ms) //每1ms进入
{
bt1ms = 0;
if(receTimeOut>0) //接收到数据赋值5,每接收一个数据receTimeOut赋值5
{
receTimeOut--; //等待5ms
if(receTimeOut==0 && receCount>0) //判断通讯接收是否超时
{
EN_485 = 0; //将485置为接收状态
receCount = 0;// //将接收地址偏移寄存器清零
}
}
}
}
- 数据接收中断
receTimeOut决定数据处理等待最大时间,5就是5ms清零,与定时处理函数相关
void Uart4_485(void) interrupt 18 //串口4中断
{
if(S4CON&S4RI)
{
S4CON&=~S4RI; //手动清零接收中断标志位
receTimeOut=5; //处理等待时间
receBuf[receCount++] = S4BUF; //保存接收字节,并递增计数器
}
}
- 设置单个寄存器函数
读出寄存器地址,和设置的内容,调用设置寄存器函数,最后数据发回plc
/****************fuction:06设置单个寄存器ok**********************************************************/
//主机发送:从机地址 + 功能码 + 寄存器地址(2个字节,先寄存器高位,在寄存器低位)+数据写入值(2个字节,先高位再低位)+ CRC16校验(低位再高位)
//从机返回:从机地址 + 功能码 + 寄存器地址(2个字节,先寄存器高位,在寄存器低位)+数据写入值(2个字节,先高位再低位)+ CRC16校验(低位再高位)
/****************************************************************************************************/
void presetSingleRegister(void) //fuction:06设置单个寄存器
{
unsigned int addr;
unsigned int tempAddr;
unsigned int tempData;
unsigned char i;
addr = (receBuf[2]<<8)+receBuf[3]; //读取初始地址
tempAddr = addr+40000;//读取地址
tempData = (receBuf[4]<<8) + receBuf[5];//设置寄存器写入值
setRegisterVal(tempAddr,tempData); //设置寄存器内容
for(i=0;i<receCount;i++) //回发,把数据发送回plc
sendBuf[i] = receBuf[i];
RS485_Send(sendBuf,8);
}
- 设置寄存器函数
根据地址,设置不同的寄存器
void setRegisterVal(unsigned int addr,unsigned int tempData) //设置寄存器内容函数
{
switch(addr)
{
case RWDAT3: EG_OUTDY(tempData); break; //电压 180-220
case RWDAT4: EG_OUTPL(tempData); break; //频率 0:50 1:60
default:break;
}
}
四、WiFi
使用的是模拟串口的方式,定时器1
4.1、模拟串口流程图
大致流程就是:中断中4个判断(3个有关接收,1个发送)
r被拉低,说明开始接收,就去接收8位,接收完就放入数组。
t拉低,起始位开始发送,发送8位+1停止位
4.2、模拟串口部分代码
void TIM0() interrupt 3
{
if(REND) //接收是否完成
{
REND=0; //清零接收完成
wifi_buf[r]=RBUF; //数据放入接收数组
r++; //存储位+1
uart_flag=1; //置位数据存放完成flag
}
if(RING) //是否开始接收
{
if(--RCNT == 0)
{
RCNT = 3; //重置接收波特计数器
if (--RBIT == 0)
{
RBUF = RDAT; 将数据保存到 RBUF
RING = 0; 停止接收
REND = 1; //设置接收已完成标志
}
else
{
RDAT >>= 1;
if (RXB) RDAT |= 0x80; ///将数据一位一位放入缓冲区
}
}
}
if ((!RXB)&&(RING==0)) //RXB(P3.6)==0,接收起始位
{
RING = 1; 设置开始接收标志
RCNT = 4; ///初始接收波特计数器
RBIT = 9; //初始接收位数 (8 数据位 + 1 停止位)
}
if (--TCNT == 0) //波特计数
{
TCNT = 3; //重置发送波特计数器
if(TING) //判断是否发送,开始发送标志位
{
if (TBIT == 0)
{
TXB = 0; //发送起始位
TDAT = TBUF; //将数据从 TBUF 加载到 TDAT
TBIT = 9; //初始发送位数 (8 数据位 + 1 停止位)
}
else //数据未发送完
{
TDAT >>= 1; //shift data to CY
if (--TBIT == 0) //是否发送完
{
TXB = 1; //P3.7 == 1
TING = 0; //停止发送
TEND = 1; //设置发送完成标志
}
else //未发送完
{
TXB = CY; //write CY to TX port
}
}
}
}
}
void UART_Send(u8 dat)
{
if(TEND) //发送完成
{
TEND=0;
TBUF=dat;
TING=1; //置位开始发送
}
delay_ms(5);
}
五、EEPROM(断电数据保护)
当每次智能离网微逆变系统信号源开机时,将“开机”的“次数”加1;
单片机上电初始化程序完成后读取EEPROM里的开机次数到变量a+1,并且显示在串口屏。
EEPROM程序实现参考STC——EEPROM(断电数据保护)_dz小伟的博客-CSDN博客
六、力控(vb代码)
6.1、用到的函数
#TextControl.Text //文本框内容,获取,更改
Display("顶部窗口"); //打开窗口,切换窗口
MsgBox("密码错误"+ setten +"次"); //弹出提示框
Exit(2); //退出程序应用01234
CloseWindow(); //关闭窗口,关闭弹出式窗口用
IntToStr($Day,10); //整数转字符串,返回值字符串
StrRight(string,2); //截取字符串右边个字符
Rand(10000); //随机数
#ComboBox1.GetWindowsText(); //获取编辑框内容,下拉框
#Text1.Show(1); //隐藏控件,图形目标
#TreeMenu.GetSelItemData(); //获取树形菜单数值
strlen(); //返回字符串长度
StrRepeate("#",5); // 重复形成的字符串。密码隐藏用
//数据报表使用
#Report.AddRow(0,2,1);//插入一行
//#Report.SetCellDouble(0,2,1,123.1,1);//输出变量
#Report.SetCellString(0,2,1,$Time,1);//输出字符串
6.2、登录窗口
- 密码错误次数提示框
点击“登录”按钮后,若账号密码错误则弹窗提示“密码错误X次”
实现方法:定义一个整型变量,错误后加一,然后变量转字符串,用函数MsgBox显示
- 密码隐藏(***)和显示
1.设有“隐藏”按钮,点击后可将密码隐藏(显示的密码更换为‘#’)再次点击后可将隐藏额密码恢复显示。
按钮改变变量a(是否显示密码),窗口脚本判断是否显示密码:是并且flag为0把文本框内容赋值给密码变量mm,flag为1(密码隐藏切换回显示后执行一次),把mm赋值给文本框显示,flag清零。否隐藏密码并且flag为0(密码显示切换隐藏后执行一次),把flag置1,求出变量mm长度,把文本框赋值为长度len个#,flag置1。flag为1(密码隐藏循环执行的语句),判断mm和文本框内容长度是否一致,一致什么都不执行,否则变量len2=文本框-mm,把文本框的右len2个字符加到mm上,重新给文本框赋值len+len2个#。
2.设有“隐藏”按钮,点击后可将密码隐藏(显示的密码更换为‘*’)再次点击后可将隐藏额密码恢复显示。
做2文本框(属性设置为一个密码显示一个密码隐藏),2文本框切换显示(按钮改变变量完成切换)(切记2文本框内容必须一样且同步,在窗口脚本中同步)
- 验证码
设有4位验证码显示、可刷新验证码。
使用rand产生随机数。点击刷新,产生随机数并赋值给文本(不是4位数则重新随机)
- 自动判断账号和密码错误
账号输入正确时,弹窗提示“账号正确”,若账号输入超过4个字符且错误时弹窗提示“账号错误”并清空输入的账号;当密码输入正确时,弹窗提示“密码正确”,若密码输入超过6个字符且错误时弹窗提示“密码错误”并清空输入的密码;
定义flag和flag2(账号密码标志位,正确则置1),窗口脚本,进入后分别判断flag和flag2是否为0,为0后继续判断账号正确(flag置1),账号错误(账号不等于并且长度大于4)。
清空文本框:给文本框赋值""
- 账号锁定
一个变量,1表示锁定账户,解锁清空0
- 延迟2s
点击“登录”按钮后,若管理员账号密码正确,则等待2秒后,弹窗提示“管理员账号登录”并打开“顶部窗口”和“操作界面”;
密码正确后一个变量a置1,窗口脚本另一个变量b++,直到b加到2s后,弹窗和打开指定窗口。
6.3、顶部窗口
- 切换窗口(树形菜单)
读取树形菜单数值到变量a,根据a切换窗口,(切换窗口之前加一个判断是否和上次变量shu1一样),注:要在进入窗口的时候先给shu1赋初始值(不赋值会直接进入到树形菜单的初始值界面)
- 切换窗口(切换控件)
一个变量a++,一个a--,然后切换根据a数值切换。
- 显示时间(格式:XX日XX月XX年—上午/下午XX时XX分—星期X)
读取出变量,按格式赋值给文本框。
6.4、监视窗口
- 下拉框切换组合表
判断下拉内容然后隐藏显示表
-
单选
判断3中间变量状态是否和开关状态相等
不相等在判断中间变量是0还是1
0把开关状态置1,其他2个置0
1把开关状态置0
IF cs11 == cs111 THEN
ELSE
IF cs11 == 0 THEN
cs111 = 1;
cs112 = 0;
cs113 = 0;
cs11 = cs111;
cs12 = cs112;
cs13 = cs113;
ELSE
cs111 = 0;
cs11 = cs111;
cs12 = cs112;
cs13 = cs113;
flagcs==0;
ENDIF
ENDIF
IF cs12 == cs112 THEN
ELSE
IF cs12 == 0 THEN
cs111 = 0;
cs112 = 1;
cs113 = 0;
cs11 = cs111;
cs12 = cs112;
cs13 = cs113;
ELSE
cs112 = 0;
cs11 = cs111;
cs12 = cs112;
cs13 = cs113;
flagcs==0;
ENDIF
ENDIF
IF cs13 == cs113 THEN
ELSE
IF cs13 == 0 THEN
cs111 = 0;
cs112 = 0;
cs113 = 1;
cs11 = cs111;
cs12 = cs112;
cs13 = cs113;
ELSE
cs113 = 0;
cs11 = cs111;
cs12 = cs112;
cs13 = cs113;
flagcs==0;
ENDIF
ENDIF
七、视频演示
7.1、串口屏单片机
2022光伏电子单片机部分
7.2、力控
全国技能大赛(光伏电子工程)力控用到vb脚本部分
八、离网硬件
分为2块电路板
8.1、逆变升压
升压,逆变,前后级检测保护
- 前级升压到400v
- 后级逆变,LR滤波输出交流220v
8.2、驱动部分
前级升压驱动,后级逆变驱动,故障检测通信控制文章来源:https://www.toymoban.com/news/detail-409874.html
- 前级SG3525升压驱动(pwm12),过压过流欠压保护
- 后级EG8010逆变驱动(spwm1234),IR2110隔离放大5-15v
- 单片机通信控制电路
九、呼吸灯
利用定时器1中断,模拟PWM,使led呈现呼吸灯效果文章来源地址https://www.toymoban.com/news/detail-409874.html
//定时器1中断,2us定时器
void timer1(void) interrupt 3
{
s++;
if(s>=500) //1ms
{
s=0;
if(fx==0){ //a相当于占空比
a++;
if(a>=500){
fx=1;
}
}else{
a--;
if(a<=0){
fx=0;
}
}
}
if(s<=a){ //通过改变1ms内,低电平的时间(a相当于占空比)
Led=0;
}else{
Led=1;
}
}
//1ms定时器
void time1_sssww() interrupt 3
{
ls++;
//150ms改变一次占空比
if(lflag==10){
lflag=0;
if(fx==0){
la++;
if(la>=15){
fx=1;
}
}else{
la--;
if(la<=0){
fx=0;
}
}
}
//15ms为一个PWM周期
if(ls>=15){
ls=0;
lflag++;
}
if(ls<la){
LED1=0;
}else{
LED1=1;
}
}
到了这里,关于光伏电子工程的设计与实施2022——单片机力控部分实现思路的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!