📄 analog.c
字号:
/* vim: set sw=8 ts=8 si : */
/*********************************************
* Author: Guido Socher, Copyright: GPL
*
* Digital analog conversion of channel ADC0 and ADC1 in
* free running mode.
**********************************************/
#include <inttypes.h>
#include <avr/signal.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include "avr_compat.h"
#include "dac.h"
#include "hardware_settings.h"
volatile static unsigned char channel=0;
volatile static unsigned char voltagecontrol=0; // 0=current limit, 1=voltage control
volatile static uint16_t conversioncount=0; // changed at every conversion
volatile static unsigned char prev_voltagecontrol=0;
// adc measurement results:
volatile static int analog_result[2]; // datatype int is 16 bit
// the value that is requested (control loop calibrates to this).
volatile static unsigned int target_val[2]; // datatype int is 16 bit
static unsigned char adlow,adhigh;
static int dac_val=0;
/* You must enable interrupt with sei() in the main program
* before you call init_analog
*/
void init_analog(void)
{
// initialize the adc result to very high values
// to keep the control-loop down until proper measurements
// are done:
analog_result[0]=0;
analog_result[1]=1025;
target_val[0]=0; // initialize to zero
target_val[1]=0; // initialize to zero
/* enable analog to digital conversion in free run mode
* without noise canceler function. See datasheet of atmega8 page 195
* We set ADPS2=1,ADPS1=0,ADPS0=1 to have a clock division factor of 32.
* This is needed to stay in the recommended range of 50-200kHz
* ADEN: Analog Digital Converter Enable
* ADIE: ADC Interrupt Enable
* ADIF: ADC Interrupt Flag
* ADFR: ADC Free Running Mode
* ADCSR: ADC Control and Status Register
* ADPS2..ADPS0: ADC Prescaler Select Bits
* REFS: Reference Selection Bits (page 203)
*/
// int-ref with external capacitor at AREF pin:
// 2.56V int ref=REFS1=1,REFS0=1
// write only the lower 3 bit for channel selection
// 2.56V ref, start with channel 0
ADMUX=(1<<REFS1)|(1<<REFS0)|(channel & 0x07);
ADCSR=(1<<ADEN)|(1<<ADIE)|(1<<ADFR)|(1<<ADIF)|(1<<ADPS2)|(1<<ADPS0);
/* start conversion */
sbi(ADCSR,ADSC);
//wait until both channels are measured twice
while(conversioncount < 4);
}
int is_dacval(void)
{
return(dac_val);
}
signed char is_current_limit(void)
{
// return 1 if current control loop active
if (voltagecontrol==0){
return(1);
}
return(0);
}
/* set the target adc value for the control loop
* values for item: 0 = u, 1 = i, units must be of the same as the values
* from the dac.
*/
void set_target_adc_val(unsigned char item,unsigned int val)
{
// here we can directly write to target_val
target_val[item]=val;
}
/* get the result of an analog conversion.
* The problem here is that we need to read more than 8 bit. Therefore
* it will always result in two assembler instruction. In other words
* it is not an atomic operation. To get it atomic we use the
* conversion count variable. If it was unchanged then the reading was
* like an atomic read.
*/
int getanalogresult(unsigned char channel)
{
int rval;
uint16_t conv_cnt_before;
while(1){
conv_cnt_before=conversioncount;
rval=analog_result[channel];
// make sure there was no AD conversion
// while we were reading the result. If so then
// read again. conversioncount is volatile.
if (conversioncount == conv_cnt_before){
break;
}
}
return(rval);
}
// the control loop changes the dac:
static void control_loop(unsigned char channel){
int tmp;
tmp=target_val[0] - analog_result[0];
if (tmp < 1){
voltagecontrol=0; // I control
}else{
voltagecontrol=1; // U control
tmp=target_val[1] - analog_result[1];
}
if (conversioncount>2){
// go only once in a while close
// to prevent permanent lsb toggle
if (tmp > -2 && tmp < 2){
tmp=0;
}
}
// slowly up:
if (tmp >2){
tmp=1;
}
if (tmp <-2){
tmp=-1;
}
dac_val=dac_val + tmp;
if (dac_val<0){
dac_val=0;
}
dac(dac_val);
}
/* the following function will be called when analog conversion is done.
* It will be called every 13th cycle of the converson clock. At 4Mhz
* and a ADPS factor of 32 this is: ((4000 kHz/ 32) / 13)= 9.6KHz intervalls
*/
SIGNAL(SIG_ADC) {
adlow=inp(ADCL); /* read low first !! */
adhigh=inp(ADCH); /* read low first !! */
// toggel the channel between 0 an 1. This will however
// not effect the next conversion as that one is already
// ongoing.
channel++;
if (channel>1){
channel=0;
}
// 2.56V ref
ADMUX=(1<<REFS1)|(1<<REFS0)|(channel & 0x07);
// channel=1 = U, channel=0 = I
analog_result[channel]=((adhigh<<8)|(adlow & 0xFF));
// short circuit protection at about 4.5A (with 0.5 Ohm shunt):
if (analog_result[0] > 900){
dac(30);
dac_val=30;
goto ENDSIG;
}
if (channel == 1){
// only when we have new current measurements
goto ENDSIG;
}
control_loop(voltagecontrol);
// end of interrupt handler
ENDSIG:
conversioncount++;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -