打印

【试用】从零设计电能表

[复制链接]
1785|2
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 jinyi7016 于 2016-4-6 09:52 编辑


这里吐槽一下,这么大的144的芯片就引出了这么几个引脚,太浪费了,就算要跟ardunio看齐,除了引脚,好像也没有什么其他可以与ardunio相关联了吧。
那几个LED布线拉了好长,还是沿板子最边上走的线,就近找个位置放一下也可以吧。

先上最后的组成图。


下载F207相关文件。



下载GD-Link等等驱动。





下载Keil 5.13以上的版本,可以支持GD32F2xx的芯片。
前面已经下载了GD32F2xxpack包,打开keil5,选择这个图标打开pack installer


选择FileàImport…
再选择上Keil.GD32F2xx_DFP.1.0.0.pack进行安装。
安装完成后就可以找到GD32F207的芯片型号了。




新建工程。
ProjectàNew  uVision Project
输入工程名,选择芯片型号为GD32F207ZE


进行Target设置,在Debug窗口选择仿真的型号如下图:



GD32F2xx的源码在GD32F20x_Firmware_Library.rar里,要提前下载的。



添加头文件目录:



编译完成,没有错误:



为了提高编译速度,可以把源码编译成lib



从启动文件中可以看到,已经在s文件中调用了系统的初始化函数,所以在我们的main函数中是不用再次调用的。



插上开发板,识别了驱动,在keil里,看到了仿真器的信息。



GPIO试水:
光盘里没有找到原理图,只是有个硬件手册;从手册上看到LEDGPIO


GPIO配置:


RCC_APB2PeriphClock_Enable函数,使能时钟。
使用GPIO_InitPara结构体,GPIO模式、速度、引脚号设置。
GPIO_Init函数初始化引脚。



GPIO_SetBits函数,把GPIO置高电平。
GPIO_ResetBits函数,把GPIO置低电平。

程序如下:
#include "main.h"

#define LED1(a) if (a)  \
                    GPIO_SetBits(GPIOD,GPIO_PIN_11);\
                    else    \
                    GPIO_ResetBits(GPIOD,GPIO_PIN_11)

#define LED2(a) if (a)  \
                    GPIO_SetBits(GPIOD,GPIO_PIN_12);\
                    else    \
                    GPIO_ResetBits(GPIOD,GPIO_PIN_12)

#define LED3(a) if (a)  \
                    GPIO_SetBits(GPIOD,GPIO_PIN_13);\
                    else    \
                    GPIO_ResetBits(GPIOD,GPIO_PIN_13)

                                       
void GPIO_Configuration(void)
{   
    GPIO_InitPara GPIO_InitStructure;
    RCC_APB2PeriphClock_Enable(RCC_APB2PERIPH_GPIOD,ENABLE);

    GPIO_InitStructure.GPIO_Pin = GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13;
    GPIO_InitStructure.GPIO_Mode = GPIO_MODE_OUT_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_SPEED_50MHZ;
    GPIO_Init(GPIOD,&GPIO_InitStructure);

}

int main(void)
{
        GPIO_Configuration();
        SysTick_Configuration();
        while (1){
        LED1(1);
        LED2(1);
        LED3(1);
      
        Delay_1ms(1000);

        LED1(0);
        LED2(0);
        LED3(0);
        
        Delay_1ms(1000);

        LED1(1);
        
        Delay_1ms(1000);

        LED2(1);
        LED1(0);
        Delay_1ms(1000);

        LED3(1);
        LED2(0);
        Delay_1ms(1000);

        
        LED3(0);
        Delay_1ms(1000);
    }
}

程序烧写后,三个灯开始闪烁,但是有一个问题。在keil中配置成烧写后复位但是并没有效果。还是要按一次复位按键。




可以看出与STM32的使用方法是一样的,这样移植起来就不会有什么困难了。
通过以上的试验,可以确定GD32的开发与STM32是基本相通的,那么接下来就是做一下原有项目的移植了。

串口的使用配置:

UART1通过 CH340 芯片,转 USB 接口连接 PC
原理图部分如下:



串口使用的两个引脚是PB6PB7,那么要对这两个引脚进行初始化:

  GPIO_InitStructure.GPIO_Pin     = GPIO_PIN_6 ;
    GPIO_InitStructure.GPIO_Mode    = GPIO_MODE_AF_PP;
    GPIO_InitStructure.GPIO_Speed   = GPIO_SPEED_50MHZ;
    GPIO_Init( GPIOB , &GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin     = GPIO_PIN_7;
    GPIO_InitStructure.GPIO_Mode    = GPIO_MODE_IN_FLOATING;;
    GPIO_Init( GPIOB , &GPIO_InitStructure);

    GPIO_PinRemapConfig(GPIO_REMAP_USART1, ENABLE);

之后就是对串口1的配置:
波特率选择的是115200,数据是8位,1个停止位,无校验。

   USART_InitStructure.USART_BRR                 = 115200;
    USART_InitStructure.USART_WL                  = USART_WL_8B;
    USART_InitStructure.USART_STBits              = USART_STBITS_1;
    USART_InitStructure.USART_Parity              = USART_PARITY_RESET;
    USART_InitStructure.USART_HardwareFlowControl = USART_HARDWAREFLOWCONTROL_NONE;
    USART_InitStructure.USART_RxorTx              = USART_RXORTX_RX | USART_RXORTX_TX;
    USART_Init(USART1, &USART_InitStructure);

    /* USART enable */
    USART_Enable(USART1, ENABLE);            
        USART_INT_Set(USART1, USART_INT_RBNE, ENABLE);
        
    /* Enable the USARTx Interrupt */
    NVIC_InitStructure.NVIC_IRQ = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQPreemptPriority = 0;
    NVIC_InitStructure.NVIC_IRQSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQEnable = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

这里使用了串口1 的接收中断,从启动文件中可以找到,中断函数的名字是USART1_IRQHandler,在gd32F20x_it.c中新建这个函数。



先实现了最简单的loopback功能的中断服务函数:

void USART1_IRQHandler(void)
{
        unsigned int temp;
        if(USART_GetIntBitState(USART1, USART_INT_RBNE) != RESET)
        {
                temp=USART_DataReceive(USART1);
                USART_DataSend(USART1, temp);
        }
         if(USART_GetIntBitState(USART1, USART_INT_TBE) != RESET)
    {   
        
      
        
        USART_INT_Set(USART1, USART_INT_TBE, DISABLE);
   
    }
}

定时器:

定时器的使用也是项目中所必须的。
对于GD32F207的定时器的定时功能与中断功能,没有在例程中找到,自己参考STM32写的。

首先是对定时器的配置:
使定时器2缺省状态。通过结构体TIM_TimeBaseStructure对定时器2初始化化。

    TIMER_DeInit(TIMER2);
    TIM_TimeBaseStructure.TIMER_Prescaler         = 119;
    TIM_TimeBaseStructure.TIMER_CounterMode       = TIMER_COUNTER_UP;
    TIM_TimeBaseStructure.TIMER_Period            = 999;
    TIM_TimeBaseStructure.TIMER_ClockDivision     = TIMER_CDIV_DIV1;
    TIM_TimeBaseStructure.TIMER_RepetitionCounter = 1;
    TIMER_BaseInit(TIMER2,&TIM_TimeBaseStructure);
        
        TIMER_INTConfig(TIMER2,TIMER_INT_UPDATE,ENABLE);
        TIMER_CARLPreloadConfig(TIMER1,ENABLE);
    /* TIMER enable counter*/
    TIMER_Enable( TIMER2, ENABLE );

中断配置:
这是参考STM32写的,与STM32的库函数有一些区别。
  NVIC_InitStructure.NVIC_IRQ = TIMER2_IRQn;
    NVIC_InitStructure.NVIC_IRQPreemptPriority = 1;
    NVIC_InitStructure.NVIC_IRQSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQEnable = ENABLE;
    NVIC_Init(&NVIC_InitStructure);        

注意要添加中断服务函数。函数名与启动文件中的要一样。

SPI:
SPI 是与电能计量芯片通讯的,电压、电流、电能都是通过SPI口从电能计量芯片是读取出来的,这一部分也是很重要的。
电能计量芯片选用ATT7022E,它适用于三相三线和三相四线应用。ATT7022E集成了多路二阶sigma-deltaADC、参考电压电路以及所有功率、能量、有效值、功率因数及频率测量的数字信号处理等电路,能够测量各相以及合相的有功功率、无功功率、视在功率、有功能量及无功能量,同时还能测量各相电流、电压有效值、功率因数、相角、频率等参数,充分满足三相复费率多功能电能表的需求。详细数据定义请参阅参数寄存器部分。

ATT7022EGD32F207的接线图为:





那么从数据手册中,找到SPI1对应的引脚,使用PA4作为片选CS引脚。





这四个引脚在板子上对应的位置为:



接下来就是在程序上对SPI1进行配置了。
首先是GPIO的配置:
void SPI1_GPIO_Configuration(void)
{
  
    GPIO_InitPara  GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = GPIO_PIN_5 |GPIO_PIN_7;
    GPIO_InitStructure.GPIO_Speed = GPIO_SPEED_50MHZ;
    GPIO_InitStructure.GPIO_Mode = GPIO_MODE_AF_PP;                     
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = GPIO_PIN_6 ;
    GPIO_InitStructure.GPIO_Mode = GPIO_MODE_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = GPIO_PIN_4;                              
    GPIO_InitStructure.GPIO_Speed = GPIO_SPEED_50MHZ;
    GPIO_InitStructure.GPIO_Mode = GPIO_MODE_OUT_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
   
   
}

STM32不同的是,在STM32中,SCLKMOSIMISO都配置成了GPIO_Mode_AF_PP,但是GD32MISO却是GPIO_MODE_IN_FLOATING

SPI的配置如下:
   SPI_InitPara  SPI_InitStructure;
    SPI_InitStructure.SPI_TransType = SPI_TRANSTYPE_FULLDUPLEX;
    SPI_InitStructure.SPI_Mode = SPI_MODE_MASTER;
    SPI_InitStructure.SPI_FrameFormat = SPI_FRAMEFORMAT_8BIT;
    SPI_InitStructure.SPI_SCKPL = SPI_SCKPL_LOW;
    SPI_InitStructure.SPI_SCKPH = SPI_SCKPH_1EDGE;
    SPI_InitStructure.SPI_SWNSSEN = SPI_SWNSS_SOFT;
    SPI_InitStructure.SPI_PSC = SPI_PSC_64;
    SPI_InitStructure.SPI_FirstBit = SPI_FIRSTBIT_MSB;
    SPI_InitStructure.SPI_CRCPOL = 7;
  
    SPI_Init(SPI1, &SPI_InitStructure);
    SPI_Enable(SPI1, ENABLE);  

有好多的宏定义也STM32的,与GD32F103的都不同了,要移植原有的程序,还是要再费点功夫的。
使用查询对SPI进行字节读写的程序:
        while(SPI_I2S_GetBitState(SPI3, SPI_FLAG_TBE) == RESET);
        SPI_I2S_SendData(SPI3,data);
        
        while (SPI_I2S_GetBitState(SPI3, SPI_FLAG_RBNE) == RESET);
        return SPI_I2S_ReceiveData(SPI3);


接下来就是对ATT7022E的操作了,要读取ATT7022E中的数据,也要向ATT7022E中写入数据时行配置,使ATT7022E能正常的工作。那么最基本的就是对ATT7022E的寄存器进行读写了。
以下是ATT7022ESPI读取数据的时序图,先发送8位的寄存器地址,再接收24位的寄存器数据。由于ATT7022E的数据寄存器是24位的,所以读取时是按32位数据进行处理的,最高8位为0



对应的程序为:
        union{unsigned char bit[4];unsigned int all;}temp;
        ATT7022E_CS(0);  
        SPI3_ReadWritebit(addr);
        temp.bit[3]=0;
        temp.bit[2]=SPI3_ReadWritebit(0);//H
        temp.bit[1]=SPI3_ReadWritebit(0);
        temp.bit[0]=SPI3_ReadWritebit(0);//L
        
        
        ATT7022E_CS(1);  
        return temp.all;


以下是ATT7022E的写寄存器的时序图,先发送8位的寄存器地址,再发送16位的寄存器数据。这里是ATT手册的错误,它的配置寄存器都是16位的,这里只要发送16位的寄存器数据就可以了。



对应的程序为:
        union{unsigned char bit[2];unsigned short int all;}temp; 
        temp.all=data;
        ATT7022E_CS(0);   
        SPI3_ReadWritebit(addr);
        SPI3_ReadWritebit(0x00);
        SPI3_ReadWritebit(temp.bit[1]);
        SPI3_ReadWritebit(temp.bit[0]);
        ATT7022E_CS(1);   

ATT7022E配置时,最好是关掉中断,等配置完成了再打开中断,防止配置过程中出现问题,导致ATT7022E工作不正常。

TIMER_INTConfig(TIMER2,TIMER_INT_UPDATE,DISABLE);
USART_INT_Set(USART1, USART_INT_RBNE, DISABLE);


楼下继续......













沙发
jinyi7016|  楼主 | 2016-4-5 21:58 | 只看该作者
本帖最后由 jinyi7016 于 2016-4-5 22:08 编辑

要配置的寄存器有:相线、信号放大倍数、脉冲使能、脉冲常数以及校表数据。
首先对芯片进行复位。
配置开三相电压、电流,femu=921.6kHz;数据慢速更新,打开refchop,打开adcchop
电流放大1倍,电压不放大。
CF1\CF2\CF3\CF4计量功能,关谐波功能。
ADC采样数据来于未经高通的原始数据,ADC采样14.4KPQS方式计量。
BOP,关TPS,关第七路高通。
写入高频脉冲常数HFconst
写入各相的电压、电流校表系数。
程序如下:

        RST_ATT_L
        Delay_by_for(0XFFFF);
        RST_ATT_H
        Delay_by_for(0X1fFFFF);
        SPI3_ReadWritebit(0X81,0Xb97E);
        SPI3_ReadWritebit(0X82,0X0055);
        SPI3_ReadWritebit(0X83,0Xf884);
        SPI3_ReadWritebit(0XB1,0X3406);
        SPI3_ReadWritebit(0X9E,HFconst);
       
        Delay_by_for(0X1FFFF);
        Delay_by_for(0X1FFFF);
        Delay_by_for(0X1FFFF);
        Delay_by_for(0X1FFFF);
       
       
        SPI3_ReadWritebit(0x84,data_temp[0]);
        SPI3_ReadWritebit(0x87,data_temp[1]);
        SPI3_ReadWritebit(0x8a,data_temp[2]);
                               
                       
        SPI3_ReadWritebit(0x85,data_temp[3]);
        SPI3_ReadWritebit(0x88,data_temp[4]);
        SPI3_ReadWritebit(0x8b,data_temp[5]);
                               
        SPI3_ReadWritebit(0x86,data_temp[6]);
        SPI3_ReadWritebit(0x89,data_temp[7]);
        SPI3_ReadWritebit(0x8c,data_temp[8]);
               
        SPI3_ReadWritebit(0x9a,data_temp[9]);
        SPI3_ReadWritebit(0x9b,data_temp[10]);
        SPI3_ReadWritebit(0x9c,data_temp[11]);
        SPI3_ReadWritebit(0x97,data_temp[12]);
        SPI3_ReadWritebit(0x98,data_temp[13]);
        SPI3_ReadWritebit(0x99,data_temp[14]);
       

而对于校表,校表流程如下:
1、模式配置寄存器(0x01)写入: 0xB97E 开启 Vref Chopper 功能提升 Vref 性能; 开启功率有效值慢速模式,减小跳动;配置 EMU 时钟 921.6kHz,降低功耗;开启 6 ADC,关闭 In 通道。
2EMU 单元配置寄存器(0x03)写入:0xF804。开启能量计量,使用功率作为潜动起动依据,关闭基波功能,视在功率能量选择 PQS 方式。
3、模拟模块使能寄存器(0x31)写入: 0x3427,开启高通滤波器;开启 BOR 电源监测电路。
4、写入 HFconst 参数(同一款表写入同样的 HFconst ):根据输入信号电压计算
HFConst=INT[25920000000*G*G*Vu*Vi/(EC*Un*Ib)]
5、功率增益校正
6、相位校正
7、电压、电流有效值校正
ATT7022E的配置与校表寄存器以及其含意如下:





配置完了,校表完了,就是通过寄存器读取电压、电流、功率等数据了。
ATT7022E的数据寄存器比较多,这里例举几个:





通过寄存器读取的数据并是不实际上的具有物理意义的量,是要通过转换的。
就以电压、电流有效值为例:
有效值寄存器采用补码形式给出,最高位是符号位,有效值总是大于或者等于0,所以有效值的符号位始终为0
分相Vrms 24位数据,补码形式
实际分相电压有效值为: Urms = Vrms/2^13
实际分相电流有效值为: Irms = (Vrms/2^13)/N
比例系数N定义: 额定电流Ib输入到芯片端取样电压为50mV时,对应的电流有效值寄存器值为Vrms Vrms/2^13约等于60,此时N=60/Ib Ib=1.5A N=60/1.5=40 Ib=6A N=60/6=10。同理,当输入到芯片端取样电压为25mV时, Vrms/2^13约等于30 Ib=1.5A N=30/1.5=20 Ib=6AN=30/6=5 可根据当前Ib电流的实际值,计算N值。
合相Vrms 24位数据,补码形式
实际合相电压有效值为: Urms=Vrms/2^12
实际合相电流有效值为: Irms = (Vrms/2^12)/N (N为比例系数, 计算方法同上)

单位为:伏特(V)或者安培(A)
功率因数的计算方法为:


其他电参量可以根据手册中的说明进行相应的计算,这里不再一一说明。
关于显示:
由于板子的引脚问题,无法驱动并口LCD,手头上也只有这个LCD了,那只好用使用串口将数据发送到PC上进行显示了。
由于是在家里,只有220V的市电,也没有电流,无法进行电流与电能的校准,市电也不是很稳定,也就粗略的进行了电压的校准,精度有一些差了。
通过串口读到的数据如下:



使用特权

评论回复
板凳
coslight| | 2016-4-6 21:03 | 只看该作者
挺牛的应用

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

131

主题

1336

帖子

12

粉丝