Skip to content

Instantly share code, notes, and snippets.

@Lukelectro
Created February 18, 2023 16:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Lukelectro/907dfeec3a6217beafbbd1636f66cee1 to your computer and use it in GitHub Desktop.
Save Lukelectro/907dfeec3a6217beafbbd1636f66cee1 to your computer and use it in GitHub Desktop.
Atmega328p-based DC-AC converter to ring a T65 phone, simpler hardware, but horrible waveshape
#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}
*/
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
const uint8_t sinus[64]={127,139,152,164,176,187,198,208,217,226,233,239,245,249,252,254,255,254,252,249,245,239,233,226,217,208,198,
187,176,164,152,139,127,115,102,90,78,67,56,46,37,28,21,15,9,5,2,0,0,0,2,5,9,15,21,28,37,46,56,67,78,90,102,115};
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
}
int main(void) {
uint8_t i=0;
/* */
setup();
while (1) {
i++;
if(i>63) i=0;
setpoint = sinus[i];
_delay_us(257); // TODO: tune to get 20 Hz
}
}
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;
}
@Lukelectro
Copy link
Author

Schermafbeelding 2023-02-18 om 17 47 31

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment