Skip to content

Instantly share code, notes, and snippets.

@Lukelectro
Created March 1, 2023 13:29
Show Gist options
  • Save Lukelectro/088d031f97973bd58bc315573a6a49ad to your computer and use it in GitHub Desktop.
Save Lukelectro/088d031f97973bd58bc315573a6a49ad to your computer and use it in GitHub Desktop.
neon indicator binary counter: 4 bits on 2 lamps (using both electrodes)
#include <avr/io.h>
#include <avr/interrupt.h>
#define F_CPU 8e6
#include <util/delay.h>
/*
FUSES = {
.low = 0xE2, //INTRC 8 MHZ, no CKDIV8
.high = 0xD9, // geen bootloader
.extended = 0xFF, // geen BOD
};
LOCKBITS = 0xFF; // {LB=PROG_VER_DISABLED, BLB0=LPM_SPM_DISABLE, BLB1=LPM_SPM_DISABLE}
*/
//const
int16_t setpoint = 128; //With 1.1V internal ADC reference and a 8 bit result, 128 means 0.55 V approximately for the output divider
void setup()
{
/*GPIO*/
DDRC = 0xBE; // PC0 input, PC6=reset input, rest ouput
DDRB = 0x3F; // all output, except crystal input PB6,7
DDRD = 0xF7; // all output, except PD3 (switch?)
/* Timer 1, for PWM output and ADC samplerate (PWM /8)
* Has to be set to inverted output, because otherwise OCRA1 == 0 results in a small spike
* on inverted, OCR1A == TOP == ICR1 means a constantly low signal and OCR1A = 0 a nearly constantly high signal
*/
ICR1 = 80; // use 80 as top, so 8M/80=100 kHz PWM
TCCR1A = 0b11000010; // use OC1A output (inverted), fast PWM, ICR1 as top
TCCR1B = 0b00011001; // WGM mode fast PWM ICR1 as top, clock timer from clkIO unprescaled, start
TIMSK1 = (1<<TOIE0); // enable interrupt on overflow (at ICR1 value)
/*ADC*/
ADMUX = 0b11100000; // set reference voltage to 1V1 internal, use ADC0 as input, left adjust result (so only higher 8 bits have to be read)
ADCSRA = (1<<ADEN)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS0); // ADC enable, interrupt enable, clock source clkio/32 (8M/32 < 200kHz)
DIDR0 = 0x01; // disable digital input buffer on ADC0 input
sei(); // enable interrupts
}
void disp(uint8_t n)
/* diplay number n (0-15),
* binairy,
* on 2 neon indicator lamps
* for half a second
*/
{
uint8_t a_bits = n & 0x05;
uint8_t b_bits = n & 0x0A;
for(uint16_t i=0;i<500;i++)
{
PORTD &= 0x0F;
PORTD |= a_bits<<4;
_delay_us(500);
PORTD &= 0x0F;
PORTD |= b_bits<<4;
_delay_us(500);
PORTD &= 0x0F;
}
}
int main(void) {
uint8_t i,j;
/* */
setup();
while (1) {
for(i=0x10;i!=0;i=i<<1) /* a simple shift on portD4..7*/
{
PORTD &= 0x0F;
PORTD |= i;
_delay_ms(50);
}
for(i=0x80;i>8;i=i>>1) /* a simple shift on portD4..7*/
{
PORTD &= 0x0F;
PORTD |= i;
_delay_ms(50);
}
disp(j++);
}
}
ISR(TIMER1_OVF_vect){
static uint8_t i=0;
i++;
if(i>=7){
ADCSRA |= (1<<ADSC); // start a new adc conversion every 8th OVF. OVF at 100 kHz, ADC max is 15ksps at max resolution
i=0;
}
PIND = 0x01; // toggle PD0 for debug
}
ISR(ADC_vect){
/*
* read adc result, and set new PWM value
* because PWM is inverted to prevent the 1 cycle high output
* 80 means output LOW, 0 means 100% dutycycle high,
* 23 means 70% dutycycle high.
*
* If PWM where not inverted an OCR1A 0 would mean 0 output instead of
* a thin needle of 1 clk cycle, then this would be a bit simpler:
* if error > 0 {if error<56 OCR=error }else ocr=56 else ocr = 0;
*/
uint8_t adcresult = ADCH;
int16_t error = (setpoint - adcresult)*8; // P factor
if(error>0){
if(error < 57) OCR1A = 80-error; else OCR1A = 40; // limit to 50% dutycycle (40) max
}else OCR1A = 80;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment