Compare commits

..

4 Commits

Author SHA1 Message Date
86e8ae4228 documentation about measure 2023-03-18 16:05:24 +01:00
2773896fb5 modbus documentation done 2023-03-17 11:46:31 +01:00
c0b73609c0 Create Doxyfile 2023-03-15 12:56:59 +01:00
719fdc1c14 doxygen - main.c 2023-03-15 12:55:57 +01:00
13 changed files with 2977 additions and 260 deletions

2783
Doxyfile Normal file

File diff suppressed because it is too large Load Diff

125
main.c
View File

@ -1,108 +1,67 @@
/** /*!
Generated Main Source File * @file main.c
* @authors Simon Donnet-Monay & Remi Heredero
* @date 14 march 2023
* @brief Main is in two part.
* First for setup everything
* Second for loop on measure and set duty cycle for PWM
*/
Company:
Microchip Technology Inc.
File Name:
main.c
Summary:
This is the main file generated using PIC10 / PIC12 / PIC16 / PIC18 MCUs
Description:
This header file provides implementations for driver APIs for all modules selected in the GUI.
Generation Information :
Product Revision : PIC10 / PIC12 / PIC16 / PIC18 MCUs - 1.81.8
Device : PIC18F97J60
Driver Version : 2.00
*/
/*
(c) 2018 Microchip Technology Inc. and its subsidiaries.
Subject to your compliance with these terms, you may use Microchip software and any
derivatives exclusively with Microchip products. It is your responsibility to comply with third party
license terms applicable to your use of third party software (including open source software) that
may accompany Microchip software.
THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER
EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY
IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS
FOR A PARTICULAR PURPOSE.
IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE,
INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND
WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP
HAS BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO
THE FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL
CLAIMS IN ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT
OF FEES, IF ANY, THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS
SOFTWARE.
*/
#include "mcc_generated_files/mcc.h" #include "mcc_generated_files/mcc.h"
#include "lcd/lcd.h" #include "lcd/lcd.h"
#include "measure.h" #include "measure.h"
#include "modbus.h" #include "modbus.h"
#define MAX_COL 16 #define MAX_COL 16 //!< columns for lcd screen (and 2 rows)
/*
Main application
*/
extern uint16_t input_registers[2]; void main(void) {
void main(void)
{
// Initialize the device
SYSTEM_Initialize();
EPWM1_LoadDutyValue(0);
Lcd_Init(); /* SETUP FUNCTIONS: */
adc_init();
modbus_init(0x80); // Initialize the system with all mcc default configs
uint16_t offsetCurrent = 0; SYSTEM_Initialize();
offsetCurrent = measure_current(offsetCurrent);
Lcd_Init(); // Initialize lcd screen
// If using interrupts in PIC18 High/Low Priority Mode you need to enable the Global High and Low Interrupts adc_init(); // Initialize adc for measures
// If using interrupts in PIC Mid-Range Compatibility Mode you need to enable the Global and Peripheral Interrupts modbus_init(0x80); // Initialize all specific modbus function
// Use the following macros to:
// Enable the Global Interrupts // Enable the Global Interrupts
INTERRUPT_GlobalInterruptEnable(); INTERRUPT_GlobalInterruptEnable();
// Disable the Global Interrupts
//INTERRUPT_GlobalInterruptDisable();
// Enable the Peripheral Interrupts // Enable the Peripheral Interrupts
INTERRUPT_PeripheralInterruptEnable(); INTERRUPT_PeripheralInterruptEnable();
// Disable the Peripheral Interrupts
//INTERRUPT_PeripheralInterruptDisable();
/*
* Initialize offset current.
* 1. disable load
* 2. Measure current without load
*
* The goal it's to remove the offset due to the electronics parts
*/
EPWM1_LoadDutyValue(0);
const uint16_t offsetCurrent = measure_current(offsetCurrent);
while (1) // create a char array for display on lcd (with space for '\0')
{ char msg[MAX_COL+1];
EPWM1_LoadDutyValue(holding_registers[0]); /* LOOP MAIN PROGRAM: */
while (1) {
// Get the measure and save it and the appropriate register
input_registers[0] = measure_voltage(); input_registers[0] = measure_voltage();
input_registers[1] = measure_current(offsetCurrent); input_registers[1] = measure_current(offsetCurrent);
uint16_t valueV = input_registers[0];
uint16_t valueI = input_registers[1];
char msg[MAX_COL+1]; // Print on the first row of the lcd the Voltage
//LCD_2x16_WriteCmd(0x01); // clear display sprintf(msg, "U = %04d [mV] ", input_registers[0]);
sprintf(msg, "U = %04d [mV] ", valueV);
LCD_2x16_WriteMsg(msg,0); LCD_2x16_WriteMsg(msg,0);
sprintf(msg, "I = %04d [uA] ", valueI); // Print on the second row of the lcd the current
sprintf(msg, "I = %04d [uA] ", input_registers[1]);
LCD_2x16_WriteMsg(msg,1); LCD_2x16_WriteMsg(msg,1);
// Write the duty cycle for pwm from the appropriate register
EPWM1_LoadDutyValue(holding_registers[0]);
} }
} }
/**
End of File
*/

View File

@ -13,55 +13,58 @@
// Number of samples to do the averaging during measures // Number of samples to do the averaging during measures
#define AVERAGE_SAMPLES 8 #define AVERAGE_SAMPLES 8
void adc_init(void) void adc_init(void) {
{
// TODO -> complete adc initialisation // TODO -> complete adc initialisation
//offsetCurrent = measure_current(0); //offsetCurrent = measure_current(0);
} }
/** /**
* Read one ADC channel. This function is only * Read one ADC channel. This function is only
* local to this file. * local to this file.
* This function make the average on samples
* *
* @param channel : the channel to be measured * @param channel : the channel to be measured
* @return the ADC read value * @return the ADC read value with an average
*/ */
static uint16_t measure_adc(uint8_t channel) static uint16_t measure_adc(uint8_t channel) {
{ uint32_t value = 0;
return (uint16_t) (ADC_GetConversion(channel));
}
uint16_t measure_voltage()
{
uint32_t sum = 0;
// Make an average
for(int i = 0; i < AVERAGE_SAMPLES; i++) { for(int i = 0; i < AVERAGE_SAMPLES; i++) {
sum += measure_adc(VOLTAGE_CHANNEL); value += (uint16_t) (ADC_GetConversion(channel));
} }
sum /= AVERAGE_SAMPLES; value /= AVERAGE_SAMPLES;
sum = (sum * ADC_REFH) / ADC_RESOLUTION; return (uint16_t) (value);
return (uint16_t)(sum);
} }
uint16_t measure_current(uint16_t offset)
{ /**
uint32_t sum = 0; * Measure voltage
for(int i = 0; i< AVERAGE_SAMPLES; i++){ * @return
sum += measure_adc(CURRENT_CHANNEL); */
} uint16_t measure_voltage() {
uint32_t m = (sum / AVERAGE_SAMPLES); // m is bits uint32_t value = measure_adc(VOLTAGE_CHANNEL);
m = (m * ADC_REFH) / ADC_RESOLUTION; // m is mV
m *= 1000; // m is uV
m /= GAIN;
m /= RESISTOR; // m is uA
if(m <= offset){
m = 0;
} else {
m -= offset;
}
return (uint16_t)m; // Convert sum from bits to mV
value = (value * ADC_REFH) / ADC_RESOLUTION;
return (uint16_t)(value);
}
/**
*
* @param offset
* @return
*/
uint16_t measure_current(uint16_t offset) {
uint32_t value = measure_adc(CURRENT_CHANNEL);
// Convert from bits to uA
value = (value * ADC_REFH) / ADC_RESOLUTION; // [mV]
value *= 1000; // [uV]
value /= GAIN; // [uV]
value /= RESISTOR; // [uA]
// Return value without offset or null if it's too low
if(value > offset) return (uint16_t)(value-offset);
return 0;
} }

139
modbus.c
View File

@ -1,14 +1,17 @@
/*!
* @file modbus.c
* @authors Simon Donnet-Monay & Remi Heredero
* @date 14 march 2023
* @brief
*/
#include "modbus.h" #include "modbus.h"
#include "crc.h" #include "crc.h"
#include "uart.h"
#include <xc.h>
#include <stdint.h>
#include <stdio.h>
// Modbus functions // Modbus functions
#define READ_INPUT_REGISTERS 0x04 #define READ_INPUT_REGISTERS 0x04 // Modbus function for read input register
#define READ_HOLDING_REGISTERS 0x03 #define READ_HOLDING_REGISTERS 0x03 // Modbus function for read holding register
#define WRITE_SINGLE_REGISTER 0x06 #define WRITE_SINGLE_REGISTER 0x06 // Modbus function for write a single register
// Modbus data model // Modbus data model
uint8_t modbusAddress; uint8_t modbusAddress;
@ -32,47 +35,83 @@ uint8_t tx_buf[256];
// Current position pointer for storing receive position // Current position pointer for storing receive position
uint8_t recPtr = 0; uint8_t recPtr = 0;
void modbus_timer(void) /**
{ * End of MODBUS frame.
INTCONbits.TMR0IF = 0; */
recPtr = 0; void modbus_timer(void) {
TMR0_StopTimer(); INTCONbits.TMR0IF = 0; // Reset flag of the timer0 interrupt
modbus_analyse_and_answer(); recPtr = 0; // Reset position of the char in the frame
TMR0_StopTimer(); // Stop timer who detect the end of the frame
modbus_analyse_and_answer(); // Run analyse of this frame
} }
extern uint16_t measure_voltage(); extern uint16_t measure_voltage();
/**
* @brief Analyse the received frame and build an answer
* @return The error code if the frame isn't valid (TODO)
*/
uint8_t modbus_analyse_and_answer(void) { uint8_t modbus_analyse_and_answer(void) {
// TODO -> complete the modbus analyse and answer // Init lenght of the answer frame at 0
uint16_t length = 0; uint16_t length = 0;
// Check if the received frame is for this device
if(rx_buf[0] == modbusAddress){ if(rx_buf[0] == modbusAddress){
tx_buf[0] = rx_buf[0]; // Adress tx_buf[0] = rx_buf[0]; // Copy the address on the tx buffer
tx_buf[1] = rx_buf[1]; // Function tx_buf[1] = rx_buf[1]; // Copy the function number on the tx buffer
// Init the addresse Register local variable
uint16_t adresseRegister = ((uint16_t)rx_buf[2] << 8) | rx_buf[3]; uint16_t adresseRegister = ((uint16_t)rx_buf[2] << 8) | rx_buf[3];
switch(rx_buf[1]){ // Check the function from rx buffer switch(rx_buf[1]){ // Check the function from rx buffer
case READ_INPUT_REGISTERS:
// In case of the function is to read input register:
case READ_INPUT_REGISTERS:
// Define length as the number of register we want read
length = ((uint16_t)rx_buf[4] << 8) | rx_buf[5]; length = ((uint16_t)rx_buf[4] << 8) | rx_buf[5];
tx_buf[2] = (uint8_t)(length*2); // Data length
// Write this length on the tx buffer for the answer
tx_buf[2] = (uint8_t)(length*2);
// For each register, write the value on the tx buffer (register on 16bits)
for(uint16_t i = 0; i < length; i++){ // Data for(uint16_t i = 0; i < length; i++){ // Data
tx_buf[i*2+4] = input_registers[adresseRegister+i]; tx_buf[i*2+4] = input_registers[adresseRegister+i];
tx_buf[i*2+3] = (input_registers[adresseRegister+i] >> 8); tx_buf[i*2+3] = (input_registers[adresseRegister+i] >> 8);
} }
length*=2;
length+=3; // Transform length as the number of bytes on tx register
length*=2; // 2 bytes by register
length+=3; // + address + function + length
break; break;
// In case of the function is to read holding register
case READ_HOLDING_REGISTERS: case READ_HOLDING_REGISTERS:
// Define length as the number of register we want read
length = ((uint16_t)rx_buf[4] << 8) | rx_buf[5]; length = ((uint16_t)rx_buf[4] << 8) | rx_buf[5];
tx_buf[2] = (uint8_t)(length*2); // Data length
for(uint16_t i = 0; i < length; i++){ // Data // Write this length on the tx buffer for the answer
tx_buf[2] = (uint8_t)(length*2);
// For each register, write the value on the tx buffer (register on 16bits))
for(uint16_t i = 0; i < length; i++){
tx_buf[i*2+4] = holding_registers[adresseRegister+i]; tx_buf[i*2+4] = holding_registers[adresseRegister+i];
tx_buf[i*2+3] = (holding_registers[adresseRegister+i] >> 8); tx_buf[i*2+3] = (holding_registers[adresseRegister+i] >> 8);
} }
length*=2; // Transform length as the number of bytes on tx register
length+=3; length*=2; // 2 bytes by register
length+=3; // + address + function + length
break; break;
// In case of the funciton is to write a single register
case WRITE_SINGLE_REGISTER: case WRITE_SINGLE_REGISTER:
// Write the value on rx buffer on the holding register define by the adress register we define before
holding_registers[adresseRegister] = ((uint16_t)rx_buf[4] << 8) | rx_buf[5]; holding_registers[adresseRegister] = ((uint16_t)rx_buf[4] << 8) | rx_buf[5];
for (int i = 2; i <= 5; i++) {
// Copy data on the tx buffer
for (uint8_t i = 2; i <= 5; i++) {
tx_buf[i] = rx_buf[i]; tx_buf[i] = rx_buf[i];
length = i+1; length = i+1;
} }
@ -80,39 +119,63 @@ uint8_t modbus_analyse_and_answer(void) {
} }
} }
// Clear address on rx buffer (for validate we treat the data)
rx_buf[0] = 0; rx_buf[0] = 0;
// Send the answer frame
modbus_send(length); modbus_send(length);
// TODO return error code
} }
void modbus_char_recvd(void) /**
{ * Record a char when it's received on the modbus
*/
void modbus_char_recvd(void) {
//! Record the received char on the rx buffer and move position of the record pointer for the next char
rx_buf[recPtr++] = RCREG1; rx_buf[recPtr++] = RCREG1;
//! Reload and start the timer0 for restart to count the time between char
TMR0_Reload(); TMR0_Reload();
TMR0_StartTimer(); TMR0_StartTimer();
} }
void modbus_send(uint8_t length) /**
{ * Create the CRC and send the tx buffer
* @param length lenght of the frame without the CRC
*/
void modbus_send(uint8_t length) {
// Create the CRC
uint16_t crc = CRC16(tx_buf, length); uint16_t crc = CRC16(tx_buf, length);
// Write CRC on the tx buffer
tx_buf[length] = crc; tx_buf[length] = crc;
tx_buf[length+1] = crc >> 8; tx_buf[length+1] = crc >> 8;
length += 2; // add 2 CRC bytes for total size length += 2; //! add 2 CRC bytes for total size
// For all the bytes to be transmitted // Send each byte of the frame on the tx buffer
for (uint8_t i = 0; i < length; i++){ for (uint8_t i = 0; i < length; i++){
EUSART1_Write(tx_buf[i]); EUSART1_Write(tx_buf[i]);
} }
} }
void modbus_init(uint8_t address) /**
{ * Initialize the modbus with adress and handler function
modbusAddress = address; * @param address The adress of this device on modbus protocole
*/
void modbus_init(uint8_t address) {
// Init the modbus adress
modbusAddress = address;
// Save the modbus adress in the dedicated register
holding_registers[1] = address; holding_registers[1] = address;
// Set the handler for the character detection
EUSART1_SetRxInterruptHandler(modbus_char_recvd); EUSART1_SetRxInterruptHandler(modbus_char_recvd);
// Set the handler for the detection of end frame
TMR0_SetInterruptHandler(modbus_timer); TMR0_SetInterruptHandler(modbus_timer);
} }

View File

@ -7,6 +7,8 @@
#define MODBUS_H #define MODBUS_H
#include <stdint.h> #include <stdint.h>
#include <xc.h>
#include <stdio.h>
extern uint8_t modbusAddress; extern uint8_t modbusAddress;
extern uint16_t input_registers[2]; extern uint16_t input_registers[2];

View File

@ -1,14 +1,8 @@
# #
#Tue Mar 14 13:52:25 CET 2023 #Sat Mar 18 16:03:57 CET 2023
default.languagetoolchain.version=2.40 proj.dir=C\:\\Users\\remi\\MPLABXProjects\\Solar-Panel
default.Pack.dfplocation=C\:\\Program Files\\Microchip\\MPLABX\\v6.00\\packs\\Microchip\\PIC18F-J_DFP\\1.5.44 host.id=aq84-7qg1-w
conf.ids=default
default.languagetoolchain.dir=C\:\\Program Files\\Microchip\\xc8\\v2.40\\bin
host.id=3awj-afwq-rl
configurations-xml=4dc45b219db50423420a0eb2e1f688ad configurations-xml=4dc45b219db50423420a0eb2e1f688ad
default.com-microchip-mplab-mdbcore-snap-SnapToolImpl.md5=eaa336cefb7fc46db8b50b7b2b6e54ca com-microchip-mplab-nbide-embedded-makeproject-MakeProject.md5=bcd6e5453a11ce86aaffd5305e838602
com-microchip-mplab-nbide-embedded-makeproject-MakeProject.md5=6e02ca5e9f5042ffd365b42ab82d3a9b
user-defined-mime-resolver-xml=none
default.com-microchip-mplab-nbide-toolchain-xc8-XC8LanguageToolchain.md5=ab1e0737b447a24f7366e9fd8fe5a2f0
proj.dir=C\:\\Users\\remi\\MPLABXProjects\\solar_panel
host.platform=windows host.platform=windows
conf.ids=

View File

@ -24,7 +24,7 @@ CLEAN_SUBPROJECTS=${CLEAN_SUBPROJECTS_${SUBPROJECTS}}
# Project Name # Project Name
PROJECTNAME=solar_panel PROJECTNAME=Solar-Panel
# Active Configuration # Active Configuration
DEFAULTCONF=default DEFAULTCONF=default

View File

@ -6,5 +6,5 @@
CND_BASEDIR=`pwd` CND_BASEDIR=`pwd`
# default configuration # default configuration
CND_ARTIFACT_DIR_default=dist/default/production CND_ARTIFACT_DIR_default=dist/default/production
CND_ARTIFACT_NAME_default=solar_panel.production.hex CND_ARTIFACT_NAME_default=Solar-Panel.production.hex
CND_ARTIFACT_PATH_default=dist/default/production/solar_panel.production.hex CND_ARTIFACT_PATH_default=dist/default/production/Solar-Panel.production.hex

View File

@ -3,14 +3,7 @@
<editor-bookmarks xmlns="http://www.netbeans.org/ns/editor-bookmarks/2" lastBookmarkId="0"/> <editor-bookmarks xmlns="http://www.netbeans.org/ns/editor-bookmarks/2" lastBookmarkId="0"/>
<open-files xmlns="http://www.netbeans.org/ns/projectui-open-files/2"> <open-files xmlns="http://www.netbeans.org/ns/projectui-open-files/2">
<group> <group>
<file>file:/C:/Program%20Files/Microchip/xc8/v2.40/pic/sources/c99/common/doprnt.c</file> <file>file:/C:/Users/remi/MPLABXProjects/Solar-Panel/measure.c</file>
<file>file:/C:/Users/remi/MPLABXProjects/solar_panel/lcd/lcd.c</file>
<file>file:/C:/Users/remi/MPLABXProjects/solar_panel/modbus.h</file>
<file>file:/C:/Users/remi/MPLABXProjects/solar_panel/main.c</file>
<file>file:/C:/Program%20Files/Microchip/xc8/v2.40/pic/sources/c99/common/aomod.c</file>
<file>file:/C:/Users/remi/MPLABXProjects/solar_panel/modbus.c</file>
<file>file:/C:/Users/remi/MPLABXProjects/solar_panel/crc.c</file>
<file>file:/C:/Users/remi/MPLABXProjects/solar_panel/mcc_generated_files/tmr0.c</file>
</group> </group>
</open-files> </open-files>
</project-private> </project-private>

View File

@ -1,55 +0,0 @@
/************************************************************************//**
* \file main.c
* \brief Program main file for MODBUS controller
***************************************************************************/
#include <xc.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include "lcd/lcd.h"
#include "pwm.h"
#include "measure.h"
#include "modbus.h"
#include "uart.h"
#define _XTAL_FREQ 64000000L
/**
* Initialize clock, buttons, leds and interrupts
*/
void init_hw()
{
OSCCON3 = 0; // runs at 64MHz
LCD_Init(LCD_2x16);
adc_init();
modbus_init(0x80);
pwm_init();
uart_init();
// Interrupts configuration
PEIE = 1;
GIE = 1;
}
uint8_t tmpStr[30];
uint16_t u; ///< Voltage in milliovolts
uint16_t i; ///< Current in microamperes
uint16_t p; ///< Power in microwatts
uint16_t offset;
uint16_t pwm = 512;
void main(void)
{
init_hw();
LCD_2x16_WriteMsg((unsigned char *)"Welcome ! ", 0); // display on line 0
LCD_2x16_WriteMsg((unsigned char *)"*-*-*-*-*-*-*-*-", 1); // display on line 1
// TODO -> complete measure of current offset
while (true) {
// TODO -> complete the main loop
}
}

View File

@ -352,7 +352,7 @@
</entry> </entry>
<entry> <entry>
<key class="com.microchip.mcc.core.tokenManager.RegisterKey" moduleName="ADC" registerAlias="ADCON2"/> <key class="com.microchip.mcc.core.tokenManager.RegisterKey" moduleName="ADC" registerAlias="ADCON2"/>
<value>128</value> <value>0</value>
</entry> </entry>
<entry> <entry>
<key class="com.microchip.mcc.core.tokenManager.RegisterKey" moduleName="ADC" registerAlias="ADRESH"/> <key class="com.microchip.mcc.core.tokenManager.RegisterKey" moduleName="ADC" registerAlias="ADRESH"/>
@ -396,7 +396,7 @@
</entry> </entry>
<entry> <entry>
<key class="com.microchip.mcc.core.tokenManager.SettingKey" moduleName="ADC" registerAlias="ADCON2" settingAlias="ADFM"/> <key class="com.microchip.mcc.core.tokenManager.SettingKey" moduleName="ADC" registerAlias="ADCON2" settingAlias="ADFM"/>
<value>right</value> <value>left</value>
</entry> </entry>
<entry> <entry>
<key class="com.microchip.mcc.core.tokenManager.SettingKey" moduleName="ADC" registerAlias="ADI" settingAlias="enable"/> <key class="com.microchip.mcc.core.tokenManager.SettingKey" moduleName="ADC" registerAlias="ADI" settingAlias="enable"/>

7
uart.c
View File

@ -1,7 +0,0 @@
#include "uart.h"
void uart_send(uint8_t *tx_buf, uint8_t length){
for (uint8_t i = 0; i < length; i++){
EUSART1_Write(tx_buf[i]);
}
}

18
uart.h
View File

@ -1,18 +0,0 @@
/*
* File: uart.h
* Author: remi
*
* Created on March 3, 2023, 3:23 PM
*/
#ifndef UART_H
#define UART_H
#include "mcc_generated_files/mcc.h"
void uart_send(uint8_t *tx_buf, uint8_t length);
#endif /* UART_H */