光伏电子工程的设计与实施2022——单片机力控部分实现思路

目录

一、串口屏

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把文本框内容赋值给密码变量mmflag为1(密码隐藏切换回显示后执行一次),把mm赋值给文本框显示,flag清零隐藏密码并且flag为0(密码显示切换隐藏后执行一次),把flag置1,求出变量mm长度,把文本框赋值为长度len个#,flag置1flag为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、驱动部分

前级升压驱动,后级逆变驱动,故障检测通信控制

  • 前级SG3525升压驱动(pwm12),过压过流欠压保护
  • 后级EG8010逆变驱动(spwm1234),IR2110隔离放大5-15v
  • 单片机通信控制电路

九、呼吸灯

利用定时器1中断,模拟PWM,使led呈现呼吸灯效果

//定时器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;
	}
}

  • 14
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
前些时间自己动手弄了一个24V2000W的逆变器,现已完工,来晒晒,付原理图,欢迎大家指点,提出宝贵意见,也欢迎拍砖。废话不多说,先上图 这是整机测试的照片,拍照的时候输出还处于短路状态。 输出的正弦波,看着还行,EG8010的SPWM精度不够高,波形也就这样了。另外死区时间有点长(1uS),过零点那里不太好看,为了保证管子安全,我也不去调整了。 这个是满载测试,两个热得快,2100W左右,水完全沸腾了。最大带载过3000W,10秒左右,迫于直流电源压力太大(一大电源两小电源并联)没有继续测试。调节功率限制电位器,将最大功率限制在2500W左右,即大于2500W,机器工作不到两秒就关闭输出。短路保护也是短路两秒左右就关闭输出,由于EG8010程序原因,如果此时不断电,过几秒后会重新输出。此机启动能力不错,两根1000W的太阳灯并联,启动时间一秒左右。此机设计功率在2200W左右,标题写2000W是因为直流电源最大输出电流是100A,故只能测到2200W左右,2000W长时间测试过(大于12小时),实际估计长时间2500W没啥问题。 这是满载时前级场管的D级波形。 满载时前级场管的D级波形展开。 这是逆变器空载功耗测试,从两个万用表读数可以看出,空载功耗为24.6*0.27=6.642W,空载比较小,节能,适合光伏等新能源用。 前级环形变压器特写。用65*35*25的铁氧体磁环两个叠起来,初级3T+3T,用1mm漆包线16根并绕,次级用那种多股很细的漆包线缠在一起的线绕的42T,辅助电源3T。 用了4对ixfh80n10,80A,100V,12.5毫欧的内阻。整流管是4只MUR1560,两个450V470uF的大电解。24V输入用了4个35V1000uF的日本化工电容。 后级特写,后级功率管用的是4只FQA28N50。输出电感是用52mm的铁硅铝用1.5mm的漆包线绕120T,电感量1mH,电容是两只4.7uF的安规。后面调试的时候已经将后级高频臂换成两只FQL40N50,低频臂是两只FQA50N50 短路测试。机器短路保护灵敏,经过多次短路(短路开机、空载短路、满载短路、带载短路),前后一共应该上百次了,机器仍然安全工作,逆变器输出端子上接的两条引线都炸的伤痕累累,镊子也是。 以下是电路部分: 前级DC-DC功率板电路,常规推挽。 前级DC-DC驱动原理图。有欠压、过压、过流保护,过流保护用检测管压降实现。电路也是常规SG3525+LM393。 后级DC-AC原理图,采用的是常规电路,没有什么新颖的地方,唯一就是加入了高压检测电路。即直流高压大于240V时辅助电源才接通,后级开始工作。调试的时候还增加了辅助电源下降时关掉SPWM驱动的电路,防止当辅助电源降低而高压直流还较高时因为功率管驱动不足引起的炸管事故,增加这个功能后就可以安全的短路关机了。 SPWM驱动板电路,EG8010+IR2110,用检测管压降作为短路保护。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dz小伟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值