Ncube Brian T Final Year Project
Ncube Brian T Final Year Project
FACULTY OF ENGINEERING
DEPARTMENT OF ELECTRICAL ENGINEERING
LEVEL 4 PROJECT
Submitted in partial fulfilment of
BSc (HONOURS) DEGREE IN ELECTRICAL ENGINEERING
By
Brian Takudzwa Ncube (R137000W)
[email protected]
Supervised by
Dr. T Marisa
DECLARATION
In presenting this project report, I agree that permission for the copying of this project report
for scholarly purposes may be granted by the head of the department or by his or her
representatives. Copying or publication of this report for financial gain shall not be allowed
without my written permission.
Date…………………………………………..
Signature……………………………….....
i
ABSTRACT
In this design of the HMI. The human machine interface is defined and ISO standards relating
to HMI design are discussed. The STM32 Cortex-M3 microcontroller is used as the ‘brains’ of
the system. The designer reviews the microcontroller’s peripherals specific to the design. The
design process is laid out and C code is given at the end to show the implementation of the
design.
ii
ACKNOWLEDGEMENTS
I would like to recognise and show gratitude to my supervisor Dr T Marisa for his guidance,
support and patience throughout my project.
Great appreciation goes to my parents Mr and Mrs Ncube and my brothers Larrack, Tendai and
Lamont Jr. for their support, love and encouragement during the completion of this project.
Above all, I thank the Almighty GOD for the grace that He has given me. It is my belief that
this project has helped me grow professionally and in the pursuance of my studies.
iii
Contents
DECLARATION ........................................................................................................................ i
ABSTRACT ...............................................................................................................................ii
Contents ..................................................................................................................................... 1
Chapter 1 .................................................................................................................................... 4
Chapter 2 .................................................................................................................................... 6
Chapter 3 .................................................................................................................................. 13
1
3.0 Graphical User Interface (GUI)................................................................................. 13
Chapter 4 .................................................................................................................................. 19
REFERENCES ........................................................................................................................ 21
APPENDIX .............................................................................................................................. 23
V – Inverter ADC................................................................................................................. 52
2
List of figures
Figure 1: Design concept ........................................................................................................... 5
Figure 2: Cortex-M3 peripherals ............................................................................................... 7
Figure 3: STM32 CAN block diagram ...................................................................................... 7
Figure 4: Controller Area Network structure ............................................................................. 8
Figure 5: Structure of the data frame ......................................................................................... 8
Figure 6: Successive approximation ADC ............................................................................... 10
Figure 7: Analogue to Digital Converter ................................................................................. 10
Figure 8: DMA data transfer .................................................................................................... 11
Figure 9: Three PWM signals for different CCRx values ....................................................... 12
Figure 10: Flow chart for GUI ................................................................................................. 13
Figure 11: Home Screen .......................................................................................................... 14
Figure 12: Inverter selection screen (left) and Screen showing Inverter_1 parameters (right)15
Figure 13: Graph displaying generation statistics .................................................................... 15
Figure 14: Grid ......................................................................................................................... 16
Figure 15: Power Usage ........................................................................................................... 16
Figure 16: Battery .................................................................................................................... 17
Figure 17: Inverter-HMI CAN connection .............................................................................. 18
3
Chapter 1
1.1 Introduction
Technology has advanced quickly in the past decades. Factories are becoming more automated,
households have more appliances that run on electricity and more personal electronic gadgets
are available to the consumers. Due to this increase the demand for electricity has also
drastically increased. Zimbabwe has also been part of this technological revolution and the
country faces a problem of not being able to meet the public energy demand (Longman, 2015).
A large portion of the country’s energy demand is being met by fossil fuel based energy
generation as well as imports from neighbouring countries. Burning of fossil fuels to produce
energy has adverse effects to the environment, is expensive to maintain and the fossil fuel
resources are dwindling (uSwitch, 2016). Importing energy is also costly for the country and
contributing to the high cost of energy for the consumers. The country is in need of more energy
from renewable sources. Renewable energy sources are infinite and do not emit substances that
are detrimental to the environment. The most abundant renewable energy source is the sun and
solar photovoltaic (PV) cells can be used to convert the energy of the sun into cheap clean
electricity.
Solar PV cells produce direct current (DC), but many household appliances require alternating
current (AC) to operate, therefore an inverter is required to convert DC into usable AC. An
even more advanced form of inverter is the grid tied inverter (GTI). This enables excess energy
produced from the solar panels to be fed into the national grid.
1.2 Aim
To design a Human Machine Interface (HMI) to service multiple grid tie inverters
1.3 Objectives
Create an inverter-HMI communication link
Set up an inverter-sensor network
Display current operating conditions for individual inverter
Show inverter output over time graphically
Display grid parameters
4
Show battery voltage
1.4 Justification
Solar panels and hence inverters are generally mounted on rooftops of households or in an open
field in the case of a solar farm. It is then necessary to enable the user to monitor the
performance of individual solar panels. The HMI helps the user to do this from a centralised
location and not having to inspect each individual inverter.
1.5 Concept
The HMI and inverters will be implemented in a scheme as shown below:
BATTERY
CHARGER
INVERTER #1 SENSORS
CAN BUS
HMI
INVERTER #n SENSORS
BATTERY
CHARGER
Sensors will be used to measure grid parameters which will allow the output of the GTI to
match these parameters so that it can supply power to the grid. Other sensors will be used to
measure the parameters of the P.V. array such as its output voltage and the temperature of the
panels. Control Area Network (CAN) will be used for communication of the sensors with the
Human Machine Interface (HMI). Operating conditions and grid conditions among other
statistics will be displayed on the HMI.
5
Chapter 2
LITERATURE REVIEW
2.0 Human Machine Interface (HMI)
An HMI is a software application that presents information to a user or operator on the state of
processes in a device and also accepts parameters to execute the operator’s control instructions
(International Engineering Consortium, 2000). Like any other product, HMIs have certain
standards they have to meet. One such standard is the ISO9421.
6
Figure 2: Cortex-M3 peripherals
7
Automatic switching off of defective nodes
Excellent noise immunity
A CAN message is sent without a specific address instead messages use unique identifiers. The
identifier is also used to define the priority of the message in the event multiple nodes require
to transmit in the bus. Before a node can transmit a message, the messages pass through the
CAN controller which ‘constructs’ the transmission message. When the transmitter receives
bus allocation all the other nodes turn to receivers. Each node then reads the identifier to
determine whether it need the message or not by filtering. If it is required, it is sent to the
receiver FIFO where it is read otherwise it is discarded (Boys, 2009).
8
- DLC field indicates data length of the requested message
Error Frame – this is not a real frame. It is a result of error signalling and recovery.
Overload Frame – a special version of an error frame. The error and overload frames
are of fixed form (Natale, 2008)
2.1.1.4 Error management
The CAN protocol can automatically detect and signal error and perform self-diagnostics. This
makes it an extremely robust protocol. Error management consists of error detection, error
signalling and fault confinement (Dandahwa, 2016).
Five different error checking detection mechanisms are implemented, three at message level
and two at bit level. Messages that fail any of the mechanisms will not be accepted and will
result in transmission of an error frame. The message level mechanisms are Cyclic Redundancy
Check (CRC), Frame check and ACK errors. For CRC, check bits are sent at the end of
transmission, these are retested at the receiver, a disagreement of these bits signifies a CRC
check. Frame check looks at the structure of the bit fields in the message and compares them
with an expected fixed format. Received frames are acknowledged by recipients by positive
acknowledgement, if the transmitter does not receive acknowledgement transmission errors
may have been detected by the recipients. Bit level mechanisms are monitoring and bit stuffing.
Monitoring is based on the ability of the transmitter to monitor each transmitted bit. Bit stuffing
ensures that a stream of recessive bits is not mistaken for an error frame or the seven-bit inter-
frame space that signifies the end of a message by inserting a complementary bit whenever five
consecutive equal bits are sent (BOSCH, 1991).
Errors a signalled by the transmission of the error flag. This prevent reception of erroneous
messages. The CAN protocol has a mechanism that differentiates short disturbances and
permanent failures so as to achieve fault confinement. A node operating with defects can be
categorised in any of three states, these are error active, error passive and buss off (Natale,
2008).
Error active – node takes part in bus transmission and signals active error flags
Error passive – error signalling limited to passive error flags but can still take part in
bus transmission
Bus off – node is likely corrupted and cannot have any influence on the bus
9
2.3 Analogue-to-Digital Converter (ADC)
STM32 microcontroller are fitted with a 12-bit successive approximation ADC. Successive
approximation ADC operates as follows. To begin conversion, hardware control captures a
sample of the input voltage (Vain). The captured input (Vsamp) is captured and the controller
generates a sequence of digital approximations D(vest) and checks each by converting the
approximation to an analogue signal which is then compared to the sampled input. When the
best approximation is found, the value is loaded into the ADC data register (ADCDR) (Brown,
2012).
Figure 6: Successive approximation ADC
The ADC can be configured to operate either in single or continuous conversion. For single
conversion, once an internal or external trigger occurs it converts the channel once and stores
10
the value in the ADCDR. For continuous conversion, a new conversion is started as soon as
the conversion is completed. Scan conversion means the ADC converts a group of channels at
once and stores the values in the ADCDR, scan conversion can be single or continuous
(STMicroelectronics, 2015).
11
Figure 9: Three PWM signals for different CCRx values
12
Chapter 3
INVERTER # GRAPH
SOLAR
HOME
GRID BATTERY
SCREEN
POWER
USAGE
13
3.0.1 GUI Layout
3.0.1.1 Home screen
The Home Screen is setup as shown in Figure 11, the user can select either of the four on screen
buttons to view the specifics of operation of the system. The buttons are: ‘Solar’, ‘Power
Usage’, ‘Grid’ and ‘Battery’.
3.0.1.2 Solar
Selecting the button ‘Solar’ opens a window for selection of individual inverters. On this screen
the total power being produced by the inverters in the system is shown. The user can select an
inverter to view its individual operating conditions. A further option ‘History’ is available to
enable the user to view the output of the inverter over a period of twelve hours (Figure 12).
14
Figure 12: Inverter selection screen (left) and Screen showing Inverter_1 parameters (right)
15
3.0.1.3 Grid
‘Grid’ shows power drawn from the grid and power being injected to the grid.
16
3.0.1.5 Battery
The ‘Battery’ button is applicable to systems incorporating a battery bank. The actual value of
the voltage as well as the percentage of the full charge is shown.
17
the control of the inverter’s operation for example alteration of the PWM duty cycle to control
the output of the boost converter. These also are used to calculation input and output powers
of the inverter for display on the HMI.
To achieve near real time data on the operating conditions of the inverter. The ADC is run in
continuous scan conversion mode. The converted values are stored in an array which is
processed by the inverter to implement control measures and the same array is sent to the HMI
via CAN for processing and display.
18
Chapter 4
4.1 Conclusion
The HMI for a grid tie inverter with minimum requirements has been realised. The design can
be implemented as a low-cost HMI solution for monitoring not just grid tie inverters but also
off-grid inverters in domestic and industrial settings.
The student has been able to gain practical knowledge in design and troubleshooting of the
product. Embedded system concepts have been reinforced after working with STM32
microcontroller. The student is more proficient in programming STM32 microcontrollers in C
and also being able to debug code. The inverter part of the project enabled the student to have
a deeper appreciation of power electronics technologies and design techniques. It has been
realised that calculation and simulation results do not directly translate into a working physical
product but repetitive testing may result in design parameters different from those acquired
during simulation.
4.2 Recommendations
Engagement with authorities to come up with structures to promote investment in grid
tie technology domestically and industrially
Upgrade of the current utility grid by the authorities to be able to handle erratic power
supplied by solar system
Setting up of payment methods for people and organisations supplying power to the
utility grid
19
4.3.3 Inverter Control
To enable the user not to just view inverter parameters but also enable them to switch the
inverters ON and OFF.
20
REFERENCES
[1] BOSCH, 1991. CAN Specification, Stuttgart: Robert Bosch GmbH.
[2] Boys, R., 2009. CAN Primer: Creating your own Network, San Jose: ARM, Ltd.
[3] Brown, G., 2012. Discovering the STM32 Microcontroller, s.l.: s.n.
[4] Charaborty, S., 2013. Design of a Transformer-less Grid-Tie Photovoltaic Inverter Using
Dual-stage Buck and Boost Converters, Bangladesh: Independent University.
[5] Cullen, R. A., 2015. www.blueskyenergyinc.com. [Online]
[Accessed 14 October 2016].
[6] Dandahwa, N., 2016. The Controller Area Network, Harare: University of Zimbabwe .
[7] Harrison, P., 2016. PWM basics on the STM32 general purpose timers. [Online]
Available at: http://www.micromouseonline.com/2016/02/06/pwm-basics-on-the-stm32-
general-purpose-timers/
[Accessed 8 Feruary 2017].
[8] International Engineering Consortium, 2000. The Human-Machine Interface. [Online]
Available at: http://www.iec.org
[Accessed 6 February 2017].
[9] ISO, 2010. Ergonomics of human–system-Part 210: Human-centred design for interactive
systems, Geneva: International Organisation for Standardization.
[10]Longman, N., 2015. Zimbabwe's energy crisis. [Online]
Available at: http://www.africanbusinessreview.co.za/technology/2102/Zimbabwe%27s-
energy-crisis
[Accessed 12 May 2017].
[11] Natale, M. D., 2008. Understanding and using the Controller Area, s.l.: s.n.
Shahryiar, S., 2015. STM32 Analogue-to-Digital Converter (ADC). [Online]
Available at: http://embedded-lab.com/blog/stm32-adc-2/
[Accessed 25 January 2017].
[12] STMicroelectronics, 2015. Reference Manual (RM0008) : STM32F101xx,
STM32F102xx, STM32F103xx, STM32F105xx and STM32F107xx advanced ARM®-based
32-bit MCUs, s.l.: ST.
[13] STMicroelectronics, 2016. STM32 32-bit ARM Cortex MCUs. [Online]
Available at: http://www.st.com/en/microcontrollers/stm32-32-bit-arm-cortex-mcus
[Accessed 12 December 2016].
21
[14] uSwitch, 2016. Renewable energy facts. [Online]
Available at: https://www.uswitch.com/solar-panels/guides/renewable-energy-facts/#step3
[Accessed 9 October 2016].
22
APPENDIX
I – HMI home screen and subsequent screens
The code below shows methods to create the home screen and it objects and also methods to
initialise subsequent screens. When a new screen is selected objects on the current screen are
cleared before the new screen can be drawn, for example ClearHome () clears the buttons on
the home screen.
#include "BUTTON.h"
#include "EDIT.h"
//Defines
BUTTON_Handle hButton, hB1, hB2, hB3, hB4, hB5, hB6, hB7,hB8, hB9, hB0, hB10;
EDIT_Handle hE1, hE2, hE3, hE4, hE5, hE6, hE7, hE8, hE9;
/*******************************************************************************
* Function Name : Home Screen
* Description : Screen shown when HMI is turned on
*******************************************************************************/
void Home (void) {
23
hB3 = BUTTON_CreateEx(205, 135, 90, 50, 0, WM_CF_SHOW, 0, GUI_ID_BUTTON3);
/* Set text and font */
BUTTON_SetText(hB3, "Battery");
BUTTON_SetFont(hB3, &GUI_Font8x15B_ASCII);
GUI_Exec();
while(1){
DelayMil(50);
SenseHome();
}
}
/*******************************************************************************
* Function Name : Solar
*
* Description : CLICK button "Solar" to display
*******************************************************************************/
void Solar (void){
24
hB6 = BUTTON_CreateEx(82, 146, 135, 20, 0, WM_CF_SHOW, 0,
GUI_ID_BUTTON6);
/* Set text and font */
BUTTON_SetText(hB6, "Inverter_3");
BUTTON_SetFont(hB6, &GUI_Font8x15B_ASCII);
GUI_Exec();
while(1){
DelayMil(50);
SenseSolar();
}
}
/*******************************************************************************
* Function Name : Grid
*
* Description : CLICK button "Grid" to display
*******************************************************************************/
void Grid (void){
25
hB8 = BUTTON_CreateEx(33, 195, 80, 20, 0, WM_CF_SHOW, 0,
GUI_ID_BUTTON8);
/* Set text and font */
BUTTON_SetText(hB8, "History");
BUTTON_SetFont(hB8, &GUI_Font8x15B_ASCII);
GUI_Exec();
while(1){
DelayMil(50);
SenseGrid();
}
/*******************************************************************************
* Function Name : Battery
*
* Description : CLICK button "Battery" to display
*******************************************************************************/
void Battery (void){
/*BUTTON_Handle hB0;
EDIT_Handle hE5, hE6;*/
26
hB0 = BUTTON_CreateEx(216, 208, 80, 20, 0, WM_CF_SHOW, 0,
GUI_ID_BUTTON0);
/* Set text and font */
BUTTON_SetText(hB0, "Back");
BUTTON_SetFont(hB0, &GUI_Font8x15B_ASCII);
GUI_Exec();
while(1){
DelayMil(50);
SenseBattery();
}
/*******************************************************************************
* Function Name : Battery
*
* Description : CLICK button "Battery" to display
*******************************************************************************/
void PowerUsage (void){
// BUTTON_Handle hB10;
// EDIT_Handle hE7, hE8, hE9;
27
hB10 = BUTTON_CreateEx(221, 212, 80, 20, 0, WM_CF_SHOW, 0,
GUI_ID_BUTTON0);
/* Set text and font */
BUTTON_SetText(hB10, "Back");
BUTTON_SetFont(hB10, &GUI_Font8x15B_ASCII);
GUI_Exec();
while(1){
DelayMil(50);
SensePowerUsage();
}
void Inverter_1(void){
CAN1_Val_Tx = 10;
TxMessage.Data[0] = CAN1_Val_Tx>>8;
TxMessage.Data[1] = CAN1_Val_Tx;
CanWriteData1(CAN1,&TxMessage);
GUI_Exec();
while(1){
if (CAN1_RxRdy==ENABLE){
CAN1_RxRdy=DISABLE;
CurrentIn=CAN1_Val_Rx;
CurrentIn = (CurrentIn/4096)*3.3;
GUI_SetFont(&GUI_Font8x16);
GUI_SetColor(GUI_BLACK);
GUI_SetTextMode(GUI_TM_TRANS);
GUI_DispStringHCenterAt("Inverter_1 Power(W): ",140,40);
GUI_DispFloatMin(CurrentIn,2);
GUI_DispStringHCenterAt("Voltage(V): " ,140,80);
28
GUI_DispFloatMin(24,2);
GUI_DispStringHCenterAt("Temperature(degrees): ",140,120);
GUI_DispFloatMin(30,2);
}
//DelayMil(50);
SenseInverter_1();
}
}
void Inverter_2(void){
hB7 = BUTTON_CreateEx(230, 197, 80, 20, 0, WM_CF_SHOW, 0, GUI_ID_BUTTON7);
/* Set text and font */
BUTTON_SetText(hB7, "Back");
BUTTON_SetFont(hB7, &GUI_Font8x15B_ASCII);
GUI_Exec();
while(1){
DelayMil(50);
SenseInverter_2();
}
}
void Inverter_3(void){
hB7 = BUTTON_CreateEx(230, 197, 80, 20, 0, WM_CF_SHOW, 0, GUI_ID_BUTTON7);
/* Set text and font */
BUTTON_SetText(hB7, "Back");
BUTTON_SetFont(hB7, &GUI_Font8x15B_ASCII);
GUI_Exec();
while(1){
DelayMil(50);
SenseInverter_3();
}
}
/*******************************************************************************
* Function Name : Methods to delete widgets
* Description : To avoid overlay of screens
*******************************************************************************/
void ClearHome (void) {
BUTTON_Delete(hB1);
29
BUTTON_Delete(hB2);
BUTTON_Delete(hB3);
BUTTON_Delete(hButton);
EDIT_Delete(hE1);
void ClearInverter_1(void){
BUTTON_Delete(hB7);
}
void ClearInverter_2(void){
BUTTON_Delete(hB7);
}
void ClearInverter_3(void){
BUTTON_Delete(hB7);
}
30
II – Touch panel calibration
TouchPanel.c from WaveShare used to calibrate the touch panel
/********************************************************************************
*
* File : TouchPanel.c
* Version : V1.0
* By :
*
* (c) Copyright 2005-2011, WaveShare
* http://www.waveshare.net
* All Rights Reserved
*
*********************************************************************************/
/* Includes ------------------------------------------------------------------*/
#include "TouchPanel.h"
#include "systick.h"
#include "LCD.h"
/*******************************************************************************
* Function Name : ADS7843_SPI_Init
*******************************************************************************/
static void ADS7843_SPI_Init(void)
{
SPI_InitTypeDef SPI_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI3, ENABLE);
/* DISABLE SPI3 */
SPI_Cmd(SPI3, DISABLE);
/* SPI3 Config -------------------------------------------------------------*/
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64;
31
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI3, &SPI_InitStructure);
/* Enable SPI3 */
SPI_Cmd(SPI3, ENABLE);
}
/*******************************************************************************
* Function Name : TP_Init
*******************************************************************************/
void TP_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD |
RCC_APB2Periph_AFIO, ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_SPI3, ENABLE);
/* TP_CS */
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC,&GPIO_InitStruct);
/* TP_IRQ */
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOD, &GPIO_InitStruct);
TP_CS(1);
ADS7843_SPI_Init();
}
32
/*******************************************************************************
* Function Name : DelayUS
*******************************************************************************/
static void DelayUS(vu32 cnt)
{
uint16_t i;
for(i = 0;i<cnt;i++)
{
uint8_t us = 12;
while (us--)
{
;
}
}
}
/*******************************************************************************
* Function Name : WR_CMD
* Input : - cmd:
*******************************************************************************/
static void WR_CMD (uint8_t cmd)
{
/* Wait for SPI3 Tx buffer empty */
while (SPI_I2S_GetFlagStatus(SPI3, SPI_I2S_FLAG_TXE) == RESET);
/* Send SPI3 data */
SPI_I2S_SendData(SPI3,cmd);
/* Wait for SPI3 data reception */
while (SPI_I2S_GetFlagStatus(SPI3, SPI_I2S_FLAG_RXNE) == RESET);
/* Read SPI3 received data */
SPI_I2S_ReceiveData(SPI3);
}
/*******************************************************************************
* Function Name : RD_AD
*******************************************************************************/
static int RD_AD(void)
{
unsigned short buf,temp;
/* Wait for SPI3 Tx buffer empty */
while (SPI_I2S_GetFlagStatus(SPI3, SPI_I2S_FLAG_TXE) == RESET);
33
/* Send SPI3 data */
SPI_I2S_SendData(SPI3,0x0000);
/* Wait for SPI3 data reception */
while (SPI_I2S_GetFlagStatus(SPI3, SPI_I2S_FLAG_RXNE) == RESET);
/* Read SPI3 received data */
temp=SPI_I2S_ReceiveData(SPI3);
buf=temp<<8;
DelayUS(1);
while (SPI_I2S_GetFlagStatus(SPI3, SPI_I2S_FLAG_TXE) == RESET);
/* Send SPI3 data */
SPI_I2S_SendData(SPI3,0x0000);
/* Wait for SPI3 data reception */
while (SPI_I2S_GetFlagStatus(SPI3, SPI_I2S_FLAG_RXNE) == RESET);
/* Read SPI3 received data */
temp=SPI_I2S_ReceiveData(SPI3);
buf |= temp;
buf>>=3;
buf&=0xfff;
return buf;
}
/*******************************************************************************
* Function Name : Read_X
* Description : Read ADS7843 ADC X
*******************************************************************************/
int Read_X(void)
{
int i;
TP_CS(0);
DelayUS(1);
WR_CMD(CHX);
DelayUS(1);
i=RD_AD();
TP_CS(1);
return i;
}
/*******************************************************************************
34
* Function Name : Read_Y
*******************************************************************************/
int Read_Y(void)
{
int i;
TP_CS(0);
DelayUS(1);
WR_CMD(CHY);
DelayUS(1);
i=RD_AD();
TP_CS(1);
return i;
}
/*******************************************************************************
* Function Name : TP_GetAdXY
* Description : Read ADS7843
*******************************************************************************/
void TP_GetAdXY(int *x,int *y)
{
int adx,ady;
adx=Read_X();
DelayUS(1);
ady=Read_Y();
*x=adx;
*y=ady;
}
/*******************************************************************************
* Function Name : TP_DrawPoint
* Input : - Xpos: Row Coordinate
* - Ypos: Line Coordinate
*******************************************************************************/
void TP_DrawPoint(uint16_t Xpos,uint16_t Ypos)
{
LCD_SetPoint(Xpos,Ypos,Blue); /* Center point */
LCD_SetPoint(Xpos+1,Ypos,Blue);
LCD_SetPoint(Xpos,Ypos+1,Blue);
LCD_SetPoint(Xpos+1,Ypos+1,Blue);
35
}
/*******************************************************************************
* Function Name : DrawCross
* Input : - Xpos: Row Coordinate
* - Ypos: Line Coordinate
*******************************************************************************/
void DrawCross(uint16_t Xpos,uint16_t Ypos)
{
LCD_DrawLine(Xpos-15,Ypos,Xpos-2,Ypos,Red);
LCD_DrawLine(Xpos+2,Ypos,Xpos+15,Ypos,Red);
LCD_DrawLine(Xpos,Ypos-15,Xpos,Ypos-2,Red);
LCD_DrawLine(Xpos,Ypos+2,Xpos,Ypos+15,Red);
LCD_DrawLine(Xpos-15,Ypos+15,Xpos-7,Ypos+15,RGB565CONVERT(184,158,131));
LCD_DrawLine(Xpos-15,Ypos+7,Xpos-15,Ypos+15,RGB565CONVERT(184,158,131));
LCD_DrawLine(Xpos-15,Ypos-15,Xpos-7,Ypos-15,RGB565CONVERT(184,158,131));
LCD_DrawLine(Xpos-15,Ypos-7,Xpos-15,Ypos-15,RGB565CONVERT(184,158,131));
LCD_DrawLine(Xpos+7,Ypos+15,Xpos+15,Ypos+15,RGB565CONVERT(184,158,131));
LCD_DrawLine(Xpos+15,Ypos+7,Xpos+15,Ypos+15,RGB565CONVERT(184,158,131));
LCD_DrawLine(Xpos+7,Ypos-15,Xpos+15,Ypos-15,RGB565CONVERT(184,158,131));
LCD_DrawLine(Xpos+15,Ypos-15,Xpos+15,Ypos-7,RGB565CONVERT(184,158,131));
/*******************************************************************************
* Function Name : Read_Ads7846
* Description : Get TouchPanel X Y
*******************************************************************************/
Coordinate *Read_Ads7846(void)
{
static Coordinate screen;
int m0,m1,m2,TP_X[1],TP_Y[1],temp[3];
uint8_t count=0;
int buffer[2][9]={{0},{0}};
do
36
{
TP_GetAdXY(TP_X,TP_Y);
buffer[0][count]=TP_X[0];
buffer[1][count]=TP_Y[0];
count++;
}
while(!TP_INT_IN&& count<9); /* TP_INT_IN */
if(count==9) /* Average X Y */
{
/* Average X */
temp[0]=(buffer[0][0]+buffer[0][1]+buffer[0][2])/3;
temp[1]=(buffer[0][3]+buffer[0][4]+buffer[0][5])/3;
temp[2]=(buffer[0][6]+buffer[0][7]+buffer[0][8])/3;
m0=temp[0]-temp[1];
m1=temp[1]-temp[2];
m2=temp[2]-temp[0];
m0=m0>0?m0:(-m0);
m1=m1>0?m1:(-m1);
m2=m2>0?m2:(-m2);
if(m0<m1)
{
if(m2<m0)
screen.x=(temp[0]+temp[2])/2;
else
screen.x=(temp[0]+temp[1])/2;
}
else if(m2<m1)
screen.x=(temp[0]+temp[2])/2;
else
screen.x=(temp[1]+temp[2])/2;
/* Average Y */
temp[0]=(buffer[1][0]+buffer[1][1]+buffer[1][2])/3;
temp[1]=(buffer[1][3]+buffer[1][4]+buffer[1][5])/3;
temp[2]=(buffer[1][6]+buffer[1][7]+buffer[1][8])/3;
37
m0=temp[0]-temp[1];
m1=temp[1]-temp[2];
m2=temp[2]-temp[0];
m0=m0>0?m0:(-m0);
m1=m1>0?m1:(-m1);
m2=m2>0?m2:(-m2);
if(m0>THRESHOLD&&m1>THRESHOLD&&m2>THRESHOLD) return 0;
if(m0<m1)
{
if(m2<m0)
screen.y=(temp[0]+temp[2])/2;
else
screen.y=(temp[0]+temp[1])/2;
}
else if(m2<m1)
screen.y=(temp[0]+temp[2])/2;
else
screen.y=(temp[1]+temp[2])/2;
return &screen;
}
return 0;
}
/*******************************************************************************
* Function Name : setCalibrationMatrix
* Description : Calculate K A B C D E F
*******************************************************************************/
FunctionalState setCalibrationMatrix( Coordinate * displayPtr,
Coordinate * screenPtr,
Matrix * matrixPtr)
{
FunctionalState retTHRESHOLD = ENABLE ;
matrixPtr->Divider = ((screenPtr[0].x - screenPtr[2].x) * (screenPtr[1].y -
screenPtr[2].y)) -
((screenPtr[1].x - screenPtr[2].x) * (screenPtr[0].y -
screenPtr[2].y)) ;
if( matrixPtr->Divider == 0 )
38
{
retTHRESHOLD = DISABLE;
}
else
{
matrixPtr->An = ((displayPtr[0].x - displayPtr[2].x) * (screenPtr[1].y -
screenPtr[2].y)) -
((displayPtr[1].x - displayPtr[2].x) * (screenPtr[0].y -
screenPtr[2].y)) ;
matrixPtr->Bn = ((screenPtr[0].x - screenPtr[2].x) * (displayPtr[1].x -
displayPtr[2].x)) -
((displayPtr[0].x - displayPtr[2].x) * (screenPtr[1].x -
screenPtr[2].x)) ;
matrixPtr->Cn = (screenPtr[2].x * displayPtr[1].x - screenPtr[1].x *
displayPtr[2].x) * screenPtr[0].y +
(screenPtr[0].x * displayPtr[2].x - screenPtr[2].x *
displayPtr[0].x) * screenPtr[1].y +
(screenPtr[1].x * displayPtr[0].x - screenPtr[0].x *
displayPtr[1].x) * screenPtr[2].y ;
matrixPtr->Dn = ((displayPtr[0].y - displayPtr[2].y) * (screenPtr[1].y -
screenPtr[2].y)) -
((displayPtr[1].y - displayPtr[2].y) * (screenPtr[0].y -
screenPtr[2].y)) ;
matrixPtr->En = ((screenPtr[0].x - screenPtr[2].x) * (displayPtr[1].y -
displayPtr[2].y)) -
((displayPtr[0].y - displayPtr[2].y) * (screenPtr[1].x -
screenPtr[2].x)) ;
matrixPtr->Fn = (screenPtr[2].x * displayPtr[1].y - screenPtr[1].x *
displayPtr[2].y) * screenPtr[0].y +
(screenPtr[0].x * displayPtr[2].y - screenPtr[2].x *
displayPtr[0].y) * screenPtr[1].y +
(screenPtr[1].x * displayPtr[0].y - screenPtr[0].x *
displayPtr[1].y) * screenPtr[2].y ;
}
return( retTHRESHOLD ) ;
}
/*******************************************************************************
39
* Function Name : getDisplayPoint
*******************************************************************************/
FunctionalState getDisplayPoint(Coordinate * displayPtr,
Coordinate * screenPtr,
Matrix * matrixPtr )
{
FunctionalState retTHRESHOLD =ENABLE ;
if( matrixPtr->Divider != 0 )
{
/* XD = AX+BY+C */
displayPtr->x = ( (matrixPtr->An * screenPtr->x) +
(matrixPtr->Bn * screenPtr->y) +
matrixPtr->Cn
) / matrixPtr->Divider ;
/* YD = DX+EY+F */
displayPtr->y = ( (matrixPtr->Dn * screenPtr->x) +
(matrixPtr->En * screenPtr->y) +
matrixPtr->Fn
) / matrixPtr->Divider ;
}
else
{
retTHRESHOLD = DISABLE;
}
return(retTHRESHOLD);
}
/*******************************************************************************
* Function Name : TouchPanel_Calibrate
*******************************************************************************/
void TouchPanel_Calibrate(void)
{
uint8_t i;
Coordinate * Ptr;
for(i=0;i<3;i++)
{
LCD_Clear(White);
GUI_Text(50,150,"Touch cross hair to calibrate",Cyan,White);
40
delay_ms(100);
DrawCross(DisplaySample[i].x,DisplaySample[i].y);
do
{
Ptr=Read_Ads7846();
}
while( Ptr == (void*)0 );
ScreenSample[i].x= Ptr->x; ScreenSample[i].y= Ptr->y;
}
setCalibrationMatrix( &DisplaySample[0],&ScreenSample[0],&matrix );
LCD_Clear(Black);
}
41
III– Touch Sense
The GUI uses polling to determine touch events and hence respond accordingly. The polling
methods for each screen are as follows:
void SenseHome(void);
void SenseHome (void) {
GUI_PID_STATE _State;
screenPtr=Read_Ads7846();
if (screenPtr != (void*)0) { //If there is a touch event
getDisplayPoint(displayPtr, screenPtr, matrixPtr );
//Button 'Solar'
if ((displayPtr->x >= 21) && (displayPtr->y >= 51) && (displayPtr->x <= 111) &&
(displayPtr->y <= 101)) {
ClearHome();
GUI_Clear();
GUI_SetBkColor(GUI_CYAN);
Solar();}
//Button 'Grid'
else if ((displayPtr->x >= 21) && (displayPtr->y >= 135) && (displayPtr->x <= 111) &&
(displayPtr->y <= 185)) {
ClearHome();
GUI_Clear();
GUI_SetBkColor(GUI_CYAN);
Grid(); }
//Button 'Battery'
else if ((displayPtr->x >= 205) && (displayPtr->y >= 135) && (displayPtr->x <= 295) &&
(displayPtr->y <= 185)) {
ClearHome();
GUI_Clear();
GUI_SetBkColor(GUI_CYAN);
Battery(); }
else{
ClearHome();
42
GUI_Clear();
GUI_SetBkColor(GUI_CYAN);
Home();
}
}
}
/******************************************************************************************
*************************************************/
void SenseSolar(void);
void SenseSolar(void){
GUI_PID_STATE _State;
screenPtr=Read_Ads7846();
if (screenPtr != (void*)0) { //If there is a touch event
getDisplayPoint(displayPtr, screenPtr, matrixPtr );
//Button 'Inverter_1'
if ((displayPtr->x >= 82) && (displayPtr->y >= 66) && (displayPtr->x <= 217) &&
(displayPtr->y <= 86)) {
ClearSolar();
GUI_Clear();
GUI_SetBkColor(GUI_CYAN);
Inverter_1();
/*show inverter_1 stats*/}
//Button 'Inverter_2'
else if ((displayPtr->x >= 82) && (displayPtr->y >= 103) && (displayPtr->x <= 217) &&
(displayPtr->y <= 123)) {
ClearSolar();
GUI_Clear();
GUI_SetBkColor(GUI_CYAN);
Inverter_2();
/*show inverter_2 stats*/}
//Button 'Inverter_3'
else if ((displayPtr->x >= 82) && (displayPtr->y >= 146) && (displayPtr->x <= 217) &&
(displayPtr->y <= 166)) {
ClearSolar();
GUI_Clear();
GUI_SetBkColor(GUI_CYAN);
Inverter_3();
/*show inverter_3 stats*/}
43
//Button 'back'
else if ((displayPtr->x >= 230) && (displayPtr->y >= 197) && (displayPtr->x <= 310) &&
(displayPtr->y <= 217)) {
ClearSolar();
GUI_Clear();
GUI_SetBkColor(GUI_CYAN);
Home();}
}
}
/******************************************************************************************
*****************************************************/
void SenseGrid(void);
void SenseGrid(void){
GUI_PID_STATE _State;
screenPtr=Read_Ads7846();
if (screenPtr != (void*)0) { //If there is a touch event
getDisplayPoint(displayPtr, screenPtr, matrixPtr );
//Button 'History'
if ((displayPtr->x >= 33) && (displayPtr->y >= 195) && (displayPtr->x <= 113) &&
(displayPtr->y <= 215)) {
ClearGrid();
GUI_Clear();
GUI_SetBkColor(GUI_CYAN);
/*show graph of grid power in and out over time*/}
}
//Button 'Back'
else if ((displayPtr->x >= 191) && (displayPtr->y >= 195) && (displayPtr->x <= 271) &&
(displayPtr->y <= 215)) {
ClearGrid();
GUI_Clear();
GUI_SetBkColor(GUI_CYAN);
Home();}
}
/******************************************************************************************
****************************************************/
44
void SenseBattery(void);
void SenseBattery(void){
GUI_PID_STATE _State;
screenPtr=Read_Ads7846();
if (screenPtr != (void*)0) { //If there is a touch event
getDisplayPoint(displayPtr, screenPtr, matrixPtr );
//Button 'Back'
if ((displayPtr->x >= 216) && (displayPtr->y >= 208) && (displayPtr->x <= 296) &&
(displayPtr->y <= 228)) {
ClearBattery();
GUI_Clear();
GUI_SetBkColor(GUI_CYAN);
Home();}
}
}
/******************************************************************************************
******************************************************/
void SensePowerUsage(void);
void SensePowerUsage(void){
GUI_PID_STATE _State;
screenPtr=Read_Ads7846();
if (screenPtr != (void*)0) { //If there is a touch event
getDisplayPoint(displayPtr, screenPtr, matrixPtr );
//Button 'Back'
if ((displayPtr->x >= 221) && (displayPtr->y >= 212) && (displayPtr->x <= 301) &&
(displayPtr->y <= 232)) {
ClearPowerUsage();
GUI_Clear();
GUI_SetBkColor(GUI_CYAN);
Home();}
}
void SenseInverter_1(void){
GUI_PID_STATE _State;
screenPtr=Read_Ads7846();
if (screenPtr != (void*)0) { //If there is a touch event
45
getDisplayPoint(displayPtr, screenPtr, matrixPtr );
//Button 'back'
if ((displayPtr->x >= 230) && (displayPtr->y >= 197) && (displayPtr->x <= 310) &&
(displayPtr->y <= 217)) {
ClearInverter_1();
GUI_Clear();
GUI_SetBkColor(GUI_CYAN);
Solar();}
}
void SenseInverter_2(void){
GUI_PID_STATE _State;
screenPtr=Read_Ads7846();
if (screenPtr != (void*)0) { //If there is a touch event
getDisplayPoint(displayPtr, screenPtr, matrixPtr );
//Button 'back'
if ((displayPtr->x >= 230) && (displayPtr->y >= 197) && (displayPtr->x <= 310) &&
(displayPtr->y <= 217)) {
ClearInverter_2();
GUI_Clear();
GUI_SetBkColor(GUI_CYAN);
Solar();}
}
void SenseInverter_3(void){
GUI_PID_STATE _State;
screenPtr=Read_Ads7846();
if (screenPtr != (void*)0) { //If there is a touch event
getDisplayPoint(displayPtr, screenPtr, matrixPtr );
//Button 'back'
if ((displayPtr->x >= 230) && (displayPtr->y >= 197) && (displayPtr->x <= 310) &&
(displayPtr->y <= 217)) {
ClearInverter_3();
GUI_Clear();
GUI_SetBkColor(GUI_CYAN);
Solar();}
}
}
46
IV – HMI CAN configuration
#include "stm32f10x.h"
#include "stm32f10x_can.h"
#include "stm32f10x_rcc.h"
#include "systick.h"
#include "stdlib.h"
#include "stdio.h"
/* PRIVATE VALUES---------------------------------*/
uint8_t CAN1_RxRdy,CAN2_RxRdy;
uint16_t CAN1_Val_Rx;
uint16_t CAN1_Val_Tx,CAN2_Val_Tx,CAN2_Val_Rx;
CanTxMsg TxMessage;
/*******************************************************************************
* Function Name : Can_Mode_Init
* Description : Initialize CAN-BUS, GPIOs and Interrupts
* Input : none
* Output : None
* Attention : Baud rate =Fpclk1/((tsjw+tbs1+tbs2)*brp)
*******************************************************************************/
void CAN_Mode_Init(void) {
GPIO_InitTypeDef GPIO_InitStructure;
CAN_InitTypeDef CAN_InitStructure;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
//CAN 1 Config
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_AFIO ,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
//Enable CAN1 clock
47
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOD, &GPIO_InitStructure);
//Struct init
CAN_StructInit(&CAN_InitStructure);
48
CAN_InitStructure.CAN_BS2=CAN_BS1_7tq;
//Tbs2=tbs2+1 time units CAN_BS2_1tq ~ CAN_BS2_8tq
CAN_InitStructure.CAN_Prescaler=5;
//The frequency factor (Fdiv) is brp+1 //
CAN_Init(CAN1, &CAN_InitStructure);
//Iinitialization of CAN1
NVIC_InitStructure.NVIC_IRQChannel = CAN1_RX0_IRQn;
49
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // The
main priority 1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //
Time priority 0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
/*******************************************************************************
* Function Name : CanWriteData
* Description : Can Write Date to CAN-BUS
* Input : None
* Output : None
* Return : None
* Attention : None
*******************************************************************************/
void CanWriteData1( CAN_TypeDef* CANx ,CanTxMsg *TxMessage )
{
/* transmit */
TxMessage->StdId = 0xA5; //specifies standard identifier ,must be less than
0x7FF
TxMessage->ExtId = 0x00; // specifies extended identifier
TxMessage->RTR = CAN_RTR_DATA; // set to data frame
TxMessage->IDE = CAN_ID_STD; //use standard identifier
TxMessage->DLC = 8; //data length 8 bytes
/*******************************************************************************
* Function Name : CAN1_RX0_IRQHandler
* Description : This function handles CAN1 RX0 interrupts
* Input : None
* Output : None
* Return : None
* Attention : None
*******************************************************************************/
void CAN1_RX0_IRQHandler(void)
50
{
CanRxMsg RxMessage;
CAN_Receive(CAN1,CAN_FIFO0, &RxMessage);
51
V – Inverter ADC
Six channels converted in continuous scan mode and the result stored in memory using DMA:
#include "stm32f10x_lib.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_adc.h"
/*
* channel 1 (PA2) = input ACS712 (CurrentIn)
* channel 2 (PA3) = output ACS712 (CurrentOut)
* channel 3 (PA4) = solar panel input voltage (PanelV)
* channel 4 (PA5) = boost stage 1 voltage (Stage1V)
* channel 5 (PA6) = inverter output voltage (Vout)
* channel 6 (PA7) = inverter temperature (Temp)
*
*/
void ADC_GPIO_Config1(void){
GPIO_InitTypeDef GPIO_InitStructure;
//Configure ADC pins (PA2-> Channel 2 to PA7 -> Channel 7 ) as analog inputs
GPIO_StructInit(&GPIO_InitStructure); // Reset init structure
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3| GPIO_Pin_4| GPIO_Pin_5|
GPIO_Pin_6| GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
52
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void ADC_Config1(void){
ADC_GPIO_Config1();
NVIC_ConfigADC();
ADC_InitTypeDef ADC_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
53
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 6;
ADC_Init(ADC1, &ADC_InitStructure);
/* Enable ADC1 */
ADC_Cmd(ADC1, ENABLE);
54
/*******************************************************************************
* Function Name : SortADC
* Description : This function reads ADC values and seperates the array
*******************************************************************************/
void SortADC (void){
CurrentIn = ADC_values[0];
CurrentOut = ADC_values[1];
PanelV = ADC_values[2];
Stage1V = ADC_values[3];
Vout = ADC_values[4];
Temp = ADC_values[5]; }
55
VI – Timer 1 PWM
Timer 1 is used to generate a 25kHz PWM signal with 75% duty cycle.
#include "stm32f10x_lib.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_tim1.h"
TIM1_TimeBaseInitTypeDef TIM1_TimeBaseStructure;
TIM1_OCInitTypeDef TIM1_OCInitStructure;
unsigned int CCR1Val = 1079;
unsigned int PrescalerValue = 0;
TIM1_TimeBaseStructure.TIM1_Period=1439;
TIM1_TimeBaseStructure.TIM1_Prescaler = 1;
TIM1_TimeBaseStructure.TIM1_ClockDivision = 0;
TIM1_TimeBaseStructure.TIM1_RepetitionCounter = 0;
TIM1_TimeBaseStructure.TIM1_CounterMode = TIM_CounterMode_Up;
TIM1_TimeBaseInit(&TIM1_TimeBaseStructure);
TIM1_OC1PreloadConfig(TIM1_OCPreload_Enable);
TIM1_ARRPreloadConfig(ENABLE);
56
TIM1_Cmd(ENABLE);
GPIO_Init(GPIOA, &GPIO_InitStructure);
57