【试用】从零设计电能表

[复制链接]
2236|2
 楼主| jinyi7016 发表于 2016-4-5 21:57 | 显示全部楼层 |阅读模式
本帖最后由 jinyi7016 于 2016-4-6 09:52 编辑


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

先上最后的组成图。
20160405_210927.jpg

下载F207相关文件。

无标题.png

下载GD-Link等等驱动。


无标题.png


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

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


无标题.png

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

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

无标题.png

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

无标题.png

添加头文件目录:

无标题.png

编译完成,没有错误:

无标题.png

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

无标题.png

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

无标题.png

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

无标题.png

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

GPIO配置:
无标题.png

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

无标题.png

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

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

  2. #define LED1(a) if (a)  \
  3.                     GPIO_SetBits(GPIOD,GPIO_PIN_11);\
  4.                     else    \
  5.                     GPIO_ResetBits(GPIOD,GPIO_PIN_11)

  6. #define LED2(a) if (a)  \
  7.                     GPIO_SetBits(GPIOD,GPIO_PIN_12);\
  8.                     else    \
  9.                     GPIO_ResetBits(GPIOD,GPIO_PIN_12)

  10. #define LED3(a) if (a)  \
  11.                     GPIO_SetBits(GPIOD,GPIO_PIN_13);\
  12.                     else    \
  13.                     GPIO_ResetBits(GPIOD,GPIO_PIN_13)

  14.                                        
  15. void GPIO_Configuration(void)
  16. {   
  17.     GPIO_InitPara GPIO_InitStructure;
  18.     RCC_APB2PeriphClock_Enable(RCC_APB2PERIPH_GPIOD,ENABLE);

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

  23. }

  24. int main(void)
  25. {
  26.         GPIO_Configuration();
  27.         SysTick_Configuration();
  28.         while (1){
  29.         LED1(1);
  30.         LED2(1);
  31.         LED3(1);
  32.       
  33.         Delay_1ms(1000);

  34.         LED1(0);
  35.         LED2(0);
  36.         LED3(0);
  37.         
  38.         Delay_1ms(1000);

  39.         LED1(1);
  40.         
  41.         Delay_1ms(1000);

  42.         LED2(1);
  43.         LED1(0);
  44.         Delay_1ms(1000);

  45.         LED3(1);
  46.         LED2(0);
  47.         Delay_1ms(1000);

  48.         
  49.         LED3(0);
  50.         Delay_1ms(1000);
  51.     }
  52. }

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

无标题.png


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

串口的使用配置:

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

无标题.png

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

  1.   GPIO_InitStructure.GPIO_Pin     = GPIO_PIN_6 ;
  2.     GPIO_InitStructure.GPIO_Mode    = GPIO_MODE_AF_PP;
  3.     GPIO_InitStructure.GPIO_Speed   = GPIO_SPEED_50MHZ;
  4.     GPIO_Init( GPIOB , &GPIO_InitStructure);
  5.     GPIO_InitStructure.GPIO_Pin     = GPIO_PIN_7;
  6.     GPIO_InitStructure.GPIO_Mode    = GPIO_MODE_IN_FLOATING;;
  7.     GPIO_Init( GPIOB , &GPIO_InitStructure);

  8.     GPIO_PinRemapConfig(GPIO_REMAP_USART1, ENABLE);

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

  1.    USART_InitStructure.USART_BRR                 = 115200;
  2.     USART_InitStructure.USART_WL                  = USART_WL_8B;
  3.     USART_InitStructure.USART_STBits              = USART_STBITS_1;
  4.     USART_InitStructure.USART_Parity              = USART_PARITY_RESET;
  5.     USART_InitStructure.USART_HardwareFlowControl = USART_HARDWAREFLOWCONTROL_NONE;
  6.     USART_InitStructure.USART_RxorTx              = USART_RXORTX_RX | USART_RXORTX_TX;
  7.     USART_Init(USART1, &USART_InitStructure);

  8.     /* USART enable */
  9.     USART_Enable(USART1, ENABLE);            
  10.         USART_INT_Set(USART1, USART_INT_RBNE, ENABLE);
  11.         
  12.     /* Enable the USARTx Interrupt */
  13.     NVIC_InitStructure.NVIC_IRQ = USART1_IRQn;
  14.     NVIC_InitStructure.NVIC_IRQPreemptPriority = 0;
  15.     NVIC_InitStructure.NVIC_IRQSubPriority = 0;
  16.     NVIC_InitStructure.NVIC_IRQEnable = ENABLE;
  17.     NVIC_Init(&NVIC_InitStructure);

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

无标题.png

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

  1. void USART1_IRQHandler(void)
  2. {
  3.         unsigned int temp;
  4.         if(USART_GetIntBitState(USART1, USART_INT_RBNE) != RESET)
  5.         {
  6.                 temp=USART_DataReceive(USART1);
  7.                 USART_DataSend(USART1, temp);
  8.         }
  9.          if(USART_GetIntBitState(USART1, USART_INT_TBE) != RESET)
  10.     {   
  11.         
  12.       
  13.         
  14.         USART_INT_Set(USART1, USART_INT_TBE, DISABLE);
  15.    
  16.     }
  17. }

定时器:

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

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

  1.     TIMER_DeInit(TIMER2);
  2.     TIM_TimeBaseStructure.TIMER_Prescaler         = 119;
  3.     TIM_TimeBaseStructure.TIMER_CounterMode       = TIMER_COUNTER_UP;
  4.     TIM_TimeBaseStructure.TIMER_Period            = 999;
  5.     TIM_TimeBaseStructure.TIMER_ClockDivision     = TIMER_CDIV_DIV1;
  6.     TIM_TimeBaseStructure.TIMER_RepetitionCounter = 1;
  7.     TIMER_BaseInit(TIMER2,&TIM_TimeBaseStructure);
  8.         
  9.         TIMER_INTConfig(TIMER2,TIMER_INT_UPDATE,ENABLE);
  10.         TIMER_CARLPreloadConfig(TIMER1,ENABLE);
  11.     /* TIMER enable counter*/
  12.     TIMER_Enable( TIMER2, ENABLE );

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

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

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

ATT7022EGD32F207的接线图为:

无标题.png



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


无标题.png


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

无标题.png

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

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

  12.     GPIO_InitStructure.GPIO_Pin = GPIO_PIN_4;                              
  13.     GPIO_InitStructure.GPIO_Speed = GPIO_SPEED_50MHZ;
  14.     GPIO_InitStructure.GPIO_Mode = GPIO_MODE_OUT_PP;
  15.     GPIO_Init(GPIOA, &GPIO_InitStructure);
  16.    
  17.    
  18. }

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

SPI的配置如下:
  1.    SPI_InitPara  SPI_InitStructure;
  2.     SPI_InitStructure.SPI_TransType = SPI_TRANSTYPE_FULLDUPLEX;
  3.     SPI_InitStructure.SPI_Mode = SPI_MODE_MASTER;
  4.     SPI_InitStructure.SPI_FrameFormat = SPI_FRAMEFORMAT_8BIT;
  5.     SPI_InitStructure.SPI_SCKPL = SPI_SCKPL_LOW;
  6.     SPI_InitStructure.SPI_SCKPH = SPI_SCKPH_1EDGE;
  7.     SPI_InitStructure.SPI_SWNSSEN = SPI_SWNSS_SOFT;
  8.     SPI_InitStructure.SPI_PSC = SPI_PSC_64;
  9.     SPI_InitStructure.SPI_FirstBit = SPI_FIRSTBIT_MSB;
  10.     SPI_InitStructure.SPI_CRCPOL = 7;
  11.   
  12.     SPI_Init(SPI1, &SPI_InitStructure);
  13.     SPI_Enable(SPI1, ENABLE);  

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


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

无标题.png


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


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

无标题.png

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

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

  1. TIMER_INTConfig(TIMER2,TIMER_INT_UPDATE,DISABLE);
  2. 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
写入各相的电压、电流校表系数。
程序如下:

  1.         RST_ATT_L
  2.         Delay_by_for(0XFFFF);
  3.         RST_ATT_H
  4.         Delay_by_for(0X1fFFFF);
  5.         SPI3_ReadWritebit(0X81,0Xb97E);
  6.         SPI3_ReadWritebit(0X82,0X0055);
  7.         SPI3_ReadWritebit(0X83,0Xf884);
  8.         SPI3_ReadWritebit(0XB1,0X3406);
  9.         SPI3_ReadWritebit(0X9E,HFconst);
  10.        
  11.         Delay_by_for(0X1FFFF);
  12.         Delay_by_for(0X1FFFF);
  13.         Delay_by_for(0X1FFFF);
  14.         Delay_by_for(0X1FFFF);
  15.        
  16.        
  17.         SPI3_ReadWritebit(0x84,data_temp[0]);
  18.         SPI3_ReadWritebit(0x87,data_temp[1]);
  19.         SPI3_ReadWritebit(0x8a,data_temp[2]);
  20.                                
  21.                        
  22.         SPI3_ReadWritebit(0x85,data_temp[3]);
  23.         SPI3_ReadWritebit(0x88,data_temp[4]);
  24.         SPI3_ReadWritebit(0x8b,data_temp[5]);
  25.                                
  26.         SPI3_ReadWritebit(0x86,data_temp[6]);
  27.         SPI3_ReadWritebit(0x89,data_temp[7]);
  28.         SPI3_ReadWritebit(0x8c,data_temp[8]);
  29.                
  30.         SPI3_ReadWritebit(0x9a,data_temp[9]);
  31.         SPI3_ReadWritebit(0x9b,data_temp[10]);
  32.         SPI3_ReadWritebit(0x9c,data_temp[11]);
  33.         SPI3_ReadWritebit(0x97,data_temp[12]);
  34.         SPI3_ReadWritebit(0x98,data_temp[13]);
  35.         SPI3_ReadWritebit(0x99,data_temp[14]);
  36.        

而对于校表,校表流程如下:
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的配置与校表寄存器以及其含意如下:
无标题.png
无标题.png
无标题.png
无标题.png

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

无标题.png

无标题.png

通过寄存器读取的数据并是不实际上的具有物理意义的量,是要通过转换的。
就以电压、电流有效值为例:
有效值寄存器采用补码形式给出,最高位是符号位,有效值总是大于或者等于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)
功率因数的计算方法为:
无标题.png

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

捕获.PNG

coslight 发表于 2016-4-6 21:03 | 显示全部楼层
挺牛的应用
您需要登录后才可以回帖 登录 | 注册

本版积分规则

149

主题

1419

帖子

12

粉丝
快速回复 在线客服 返回列表 返回顶部