You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

177 lines
4.8 KiB
C

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <stdlib.h>
#include <stddef.h>
#include "yaMBSiavr.h"
#define AVRG 512
#define SUPPLY_VOLTS 3.3
#define REF_OHMS 993.5
#define ADC_0V_COUNT 170
#define ADC_MAX_VOLT 1.95
uint16_t adc_buffer[AVRG];
volatile float ch_values[9];
void init_clk(void)
{
// ========= System Clock configuration =========
// Set to external 16Mhz crystal, using the PLL at *2
// set it to be a 12-16Mhz crystal with a slow start-up time.
OSC.XOSCCTRL = OSC_FRQRANGE_2TO9_gc | OSC_XOSCSEL_XTAL_16KCLK_gc ;
OSC.CTRL |= OSC_XOSCEN_bm ; // enable it
while( (OSC.STATUS & OSC_XOSCRDY_bm) == 0 ){} // wait until it's stable
// The external crystal is now running and stable.
// (Note that it's not yet selected as the clock source)
// Now configure the PLL to be eXternal oscillator * 2
OSC.PLLCTRL = OSC_PLLSRC_XOSC_gc | 2;
// now enable the PLL...
OSC.CTRL |= OSC_PLLEN_bm ; // enable the PLL...
while( (OSC.STATUS & OSC_PLLRDY_bm) == 0 ){} // wait until it's stable
// And now, *finally*, we can switch from the internal 2Mhz clock to the PLL
CCP = CCP_IOREG_gc; // protected write follows
CLK.CTRL = CLK_SCLKSEL_PLL_gc; // The System clock is now PLL (16Mhz * 2)
// ==============================================
}
void init_timer_100us(){
TCC0.CTRLA |= TC_CLKSEL_DIV256_gc;
TCC0.PER = 13; // ==> 9615Hz
TCC0.INTCTRLA |= TC_OVFINTLVL_HI_gc;
}
void adc_init(void){
ADCA.CTRLB = ADC_FREERUN_bm;
ADCA.REFCTRL = ADC_REFSEL_VCC_gc;
ADCA.PRESCALER = ADC_PRESCALER_DIV512_gc;
uint8_t CalibrationByteL;
uint8_t CalibrationByteH;
NVM_CMD = NVM_CMD_READ_CALIB_ROW_gc;
CalibrationByteL = pgm_read_byte(offsetof(NVM_PROD_SIGNATURES_t, ADCACAL0));
CalibrationByteH = pgm_read_byte(offsetof(NVM_PROD_SIGNATURES_t, ADCACAL1));
NVM_CMD = NVM_CMD_NO_OPERATION_gc;
ADCA.CALL = CalibrationByteL;
ADCA.CALH = CalibrationByteH;
ADCA.CTRLA = ADC_ENABLE_bm;
ADCA.CH0.CTRL = ADC_CH_INPUTMODE_SINGLEENDED_gc;
ADCA.CH0.MUXCTRL = ADC_CH_MUXPOS_PIN0_gc;
}
uint16_t adc_read(void){
ADCA.CH0.CTRL |= ADC_CH_START_bm;
while(!(ADCA.CH0.INTFLAGS & ADC_CH_CHIF_bm));
ADCA.CH0.INTFLAGS = ADC_CH_CHIF_bm;
return ADCA.CH0.RES;
}
float avrg(uint16_t * values, uint16_t length){
uint64_t res = 0;
//ommit first and last sample
for(uint16_t i=1; i < length-1; i++){
res += values[i];
}
return (float)(res)/(length-2);
}
void dma_init(void){
DMA.CTRL = DMA_ENABLE_bm;
DMA.CH0.CTRLB = DMA_CH_TRNINTLVL_LO_gc;
DMA.CH0.TRFCNT = AVRG * 2;
DMA.CH0.CTRLA = DMA_CH_BURSTLEN_2BYTE_gc | DMA_CH_SINGLE_bm;
DMA.CH0.TRIGSRC = DMA_CH_TRIGSRC_ADCA_CH0_gc;
//repeat endless
DMA.CH0.CTRLA |= DMA_CH_REPEAT_bm;
DMA.CH0.REPCNT = 0;
DMA.CH0.SRCADDR0 = ((uintptr_t)&ADCA.CH0.RES) & 0xFF;
DMA.CH0.SRCADDR1 = (((uintptr_t)&ADCA.CH0.RES) >> 0x08) & 0xFF;
DMA.CH0.SRCADDR2 = (((uintptr_t)&ADCA.CH0.RES) >> 0x0F) & 0xFF;
DMA.CH0.DESTADDR0 = ((uintptr_t)adc_buffer) & 0xFF;
DMA.CH0.DESTADDR1 = ((uintptr_t)adc_buffer >> 0x08) & 0xFF;
DMA.CH0.DESTADDR2 = ((uintptr_t)adc_buffer >> 0x0F) & 0xFF;
DMA.CH0.ADDRCTRL = DMA_CH_SRCRELOAD_BURST_gc | DMA_CH_DESTRELOAD_TRANSACTION_gc | DMA_CH_SRCDIR_INC_gc | DMA_CH_DESTDIR_INC_gc;
DMA.CH0.CTRLA |= DMA_CH_ENABLE_bm;
}
void modbusGet(void) {
if (modbusGetBusState() & (1<<ReceiveCompleted))
{
switch(rxbuffer[1]) {
case fcReadHoldingRegisters: ;
modbusExchangeRegisters((uint16_t*)ch_values, 0, 18);
break;
default:
modbusSendException(ecIllegalFunction);
break;
}
}
}
int main(void){
init_clk();
PORTD.DIRSET = 1 << 5;
PORTD.OUTTGL = 1 << 5;
_delay_ms(500);
PORTD.OUTTGL = 1 << 5;
adc_init();
dma_init();
PORTCFG.MPCMASK = 0xFF;
PORTC.PIN1CTRL = PORT_OPC_PULLUP_gc;
init_timer_100us();
PMIC.CTRL |= PMIC_LOLVLEN_bm | PMIC_MEDLVLEN_bm | PMIC_HILVLEN_bm;
sei();
modbusSetAddress(1);
modbusInit();
for(;;){
modbusGet();
}
}
ISR(TCC0_OVF_vect){
modbusTickTimer();
}
ISR(DMA_CH0_vect){
static uint8_t pin = 0;
DMA.INTFLAGS |= DMA_CH0TRNIF_bm; // clear flag
DMA.CH0.TRIGSRC = DMA_CH_TRIGSRC_OFF_gc;
ADCA.CTRLB = 0; // stop ADC
uint8_t old_pin = pin;
pin=(pin+1)%9;
ADCA.CH0.MUXCTRL = pin << ADC_CH_MUXPOS_gp; // give MUX time to switch during averaging
float temp = avrg(adc_buffer, AVRG);
temp = temp - ADC_0V_COUNT;
temp = (temp)/ (4096-ADC_0V_COUNT) * ADC_MAX_VOLT;
temp = (temp * REF_OHMS)/(SUPPLY_VOLTS - temp);
temp = (temp-100)/0.3927; //pt100 formula
ch_values[old_pin] = temp;
DMA.CH0.TRIGSRC = DMA_CH_TRIGSRC_ADCA_CH0_gc;
ADCA.CTRLB = ADC_FREERUN_bm; // start ADC again
}