Stránka 1 z 1

Pomoc s kodem v C pro ATtiny13A

Napsal: 28 kvě 2021, 14:15
od JirkaZ
Zdravím,

byl by někdo z přítomných schopen a zejména ochoten mi pomoct s kódem v C pro ATtiny13A?

O co jde: potřebuji udělat úplně jednoduchý modul zajišťující pomalý analogový track and hold (podrobněji na schématu v příloze).

Jako skvělou inspiraci jsem našel https://adnbr.co.uk/articles/adc-and-pwm-basics . Je tam asi skoro všechno*, nicméně jako programátorský laik nejsem schopen ty ukázky "učesat a slepit" do jednoho zdrojového souboru, který bych předhodil překladači (nejspíš avr-gcc).

Jako alternativu (pro mě asi lepší) zvažuji Arduino IDE, kde jsem si už doinstaloval podporu pro ATtiny13, ale zůstávám "zaseknutý" na oné neschopnosti kód z příkladů "učesat a slepit".

(*) V ukázkových příkladech chybí mnou požadovaná klíčová funkce /HOLD a není tam ani použitý watchdog

Výstupem pro mě by tedy měl být jeden textový soubor (co nejvíce podobný těm příkladům a s ponechanými komentáři), který bych si už dále sám upravoval a současně se na tom učil.

Pokud by to nebylo triviální, můžeme se dohodnout na vhodné odměně.

Díky

--
Jirka

Napsal: 28 kvě 2021, 14:43
od ZdenekHQ
Mám dojem, že jdeš s kanónem na vrabce.

Ten obvod se správně jmenuje "sample and hold". Pokud je sepnuto, napětí na výstupu odpovídá napětí na vstupu. Pokud je rozepnuto, drží hodnotu. Procesor netřeba. Technicky to lze zapojit tak, že se na "hold" přepíná až při změně řídícího signálu, jinak ten kondík vůbec není v cestě signálu.

Nakonec případná analogová filtrace bývá taky daleko efektivnější.

Převod na PWM na výstupu je taky daleko jednodušší, nejsi limitovaný taktem procesoru.

Napsal: 28 kvě 2021, 14:53
od JirkaZ
ZdenekHQ píše:Mám dojem, že jdeš s kanónem na vrabce.

Ten obvod se správně jmenuje "sample and hold". Pokud je sepnuto, napětí na výstupu odpovídá napětí na vstupu. Pokud je rozepnuto, drží hodnotu. Procesor netřeba.

Nakonec případná analogová filtrace bývá taky daleko efektivnější.

Převod na PWM na výstupu je taky daleko jednodušší.


Bohužel nikoliv.

Analogový sample and hold je sice teoreticky možný, ale reálně není s rozumnou součástkovou základnou schopen udržet hold hodnotu déle než řekněme jednotky, max. desítky minut. Já to potřebuju teoreticky "na furt", lépe řečeno do doby vypnutí obvodu.

Jsem založením analogový hw elektronik s praxí mnoho desítek let a nepochybuj o tom, že kdyby to bylo možné, už dávno jsem tu dvojici OZ, spínač, kondenzátor a pár odporů použil...

Napsal: 28 kvě 2021, 14:54
od ZdenekHQ
Ta informace "na furt" je někde v zadání?

Napsal: 28 kvě 2021, 14:58
od JirkaZ
ZdenekHQ píše:Ta informace "na furt" je někde v zadání?


Ne, v zadání není, je nepřímo obsažena už v principu řešení a pro něj není podstatná.

Opravdu nepotřebuju nic jiného, než na co jsem se ptal. Čili ani nespecifikuju momentálně nepodstatné věci, vše důležité je ve schématu.

Napsal: 28 kvě 2021, 15:00
od ZdenekHQ
Děláš klasickou chybu, že navrhneš pouze vlastní řešení a nenapíšeš PROČ. Pak se nediv podobným reakcím. Tak hodně štěstí.

Napsal: 28 kvě 2021, 21:38
od lesana87
Tak jsem ti to slepila dohromady, ale nemám to teď na čem vyzkoušet. :D

Kód: Vybrat vše


// 9.6 MHz, built in resonator
#define F_CPU 9600000
#define PWM_OUT 1 //PWM output on PB1
#define HOLD 4 // HOLD signal on PB4

#include <avr/io.h>

void adc_setup (void)
{
    // Set the ADC input to PB2/ADC1
    ADMUX |= (1 << MUX0);
    ADMUX |= (1 << ADLAR);

    // Set the prescaler to clock/128 & enable ADC
    ADCSRA |= (1 << ADPS1) | (1 << ADPS0) | (1 << ADEN);
}

int adc_read (void)
{
    // Start the conversion
    ADCSRA |= (1 << ADSC);

    // Wait for it to finish
    while (ADCSRA & (1 << ADSC));

    return ADCH;
}

void pwm_setup(void)
{
    // Set Timer 0 prescaler to clock/8.
    // At 9.6 MHz this is 1.2 MHz.
    TCCR0B |= (1 << CS01) | (1 << CS00);
    // Set to 'Fast PWM' mode
    TCCR0A |= (1 << WGM01) | (1 << WGM00);
    // Clear OC0B output on compare match, upwards counting.
    TCCR0A |= (1 << COM0B1);
}

void pwm_write (int val)
{
    OCR0B = val;
}

// ... adc_setup, adc_read, pwm_setup, pwm_write ...

int main (void)
{
    int adc_in;

    // LED is an output.
    DDRB |= (1 << PWM_OUT);
    DDRB &= ~(1<<HOLD); // Makes HOLD pin of PORTB as Input

    adc_setup();
    pwm_setup();
    while (1) {
        // Get the ADC value
        adc_in = adc_read();
        if (PINB & (1<<HOLD)) {
             // Now write it to the PWM counter
            pwm_write(adc_in);
        }
    }
}

Napsal: 28 kvě 2021, 21:52
od JirkaZ
lesana87 píše:Tak jsem ti to slepila dohromady, ale nemám to teď na čem vyzkoušet. :D


Díky moc, ATtiny13 budu mít k dispozici až příští týden, dám vědět.

Mimochodem jsem si nevšiml, že na původním odkazu https://adnbr.co.uk/articles/adc-and-pwm-basics je až dole další odkaz na https://gist.github.com/adnbr/9289235 a tam je ten mnou požadovaný "učesaný a slepený" kód z příkladů vcelku... Samozřejmě tam není funkce /HOLD.

Watchdog v Tebou slepeném kódu zatím zřejmě není, šel by dopsat?

Napsal: 29 kvě 2021, 10:42
od Mahoney
Šel.

Napsal: 29 kvě 2021, 18:55
od bdn
Přidání watchdogu pro kód výše.
první číslo znamená, číslo řádku, kam nakopírovat...

Kód: Vybrat vše

/* 7 */ #include <avr/wdt.h>
/* 57 */ wdt_enable(WDTO_500MS);
/* 59 */  wdt_reset();

Mimochodem kód výše je 8bit. ADC hodnota a 8bit. PWM. Modernější např. attiny1614 má 16bit. timer, takže PWM zde může být 16bit. rozlišení...

Napsal: 30 kvě 2021, 20:57
od JirkaZ
bdn píše:Přidání watchdogu pro kód výše.
první číslo znamená, číslo řádku, kam nakopírovat...

Kód: Vybrat vše

/* 7 */ #include <avr/wdt.h>
/* 57 */ wdt_enable(WDTO_500MS);
/* 59 */  wdt_reset();



Díky moc, zkusím v týdnu, až budu fyzicky mít tu ATtiny13A. Napíšu sem výsledek.

bdn píše:Mimochodem kód výše je 8bit. ADC hodnota a 8bit. PWM. Modernější např. attiny1614 má 16bit. timer, takže PWM zde může být 16bit. rozlišení...


Ano, já vím, že ATtiny13 má osmibitový PWM a že v tom kódu se využívá jen horních 8 bitů z 10bit ADC (autor to popisuje na zdrojové stránce).

Jenže: existuje ATtiny (popř. prostě nějaký Atmel) v osmivývodovém pouzdře a s 10bit ADC i PWM? Při zběžném hledání jsem neuspěl, našel jsem pouze PICy a tam bych potřeboval, aby mi někdo vhodný program napsal úplně komplet... Osmivývodové pouzdro potřebuju kvůli místu na DPS.

Napsal: 31 kvě 2021, 14:45
od Mahoney
Ten kód bude víceméně stejný, jen pojistky, inicializace a jména registrů se budou lišit. Vhodné MCU jsou PIC12F683, PIC12F1501, PIC12F1822 (či 12F1840). Víc teď nemůžu pomoci, snad se toho někdo chytne.

Napsal: 01 čer 2021, 22:22
od FHonza
JirkaZ píše:Jenže: existuje ATtiny (popř. prostě nějaký Atmel) v osmivývodovém pouzdře a s 10bit ADC i PWM?

ATtiny212, ATtiny412

Napsal: 02 čer 2021, 08:54
od JirkaZ
FHonza píše:
JirkaZ píše:Jenže: existuje ATtiny (popř. prostě nějaký Atmel) v osmivývodovém pouzdře a s 10bit ADC i PWM?

ATtiny212, ATtiny412


Aha, díky... ATtiny212 je dokonce o fous levnější než ATtiny13 a sem tam se dá i sehnat. No, uvidíme...

Napsal: 08 čer 2021, 22:02
od JirkaZ
Tak po dalších neumělých úpravách z mé strany vzniklo něco jako funkční kód, osobně úspěšně ověřený s ATtiny13A.

Maximalizoval jsem frekvenci PWM (původní kód poskytoval cca 550 Hz, nyní cca 35,71 kHz), dvojnásobně zrychlil A/D převodník, použil pro něj vnitřní referenci 1,1 V . Jo a 8 bitů PWM stačí (lépe řečeno musí stačit)...

Pro mě je zajímavé, že tento kód lze vložit do Arduino IDE (samozřejmě při nastavení správné "desky", zde MCU na ATtiny13), kompilace normálně proběhne bez chyb a stejně tak se exportuje i výsledný *.hex. Čili není třeba se zabývat s avr-gcc v konzoli a s hledáním správných parametrů překladu.

Pak už jen vhodný programátor a je hotovo ;-)

Tímto děkuji všem zúčastněným...

Kód: Vybrat vše

// Track and hold (sample and hold) circuit with ATtiny13A
// voltage input on pin 7 (PB2/ADC1), PWM output on pin 6 (PB1),
// control input (H=track, L=hold) on pin 3 (PB4)
// fclk 9.6 MHz, built in resonator
// original source: https://adnbr.co.uk/articles/adc-and-pwm-basics
// original source: https://gist.github.com/adnbr/9289235
// modification: http://www.ebastlirna.cz/modules.php?name=Forums&file=viewtopic&p=1175950

#define F_CPU 9600000
#define PWM_OUT 1 //PWM output on PB1
#define HOLD 4 // HOLD signal on PB4

#include <avr/io.h>
#include <avr/wdt.h>

void adc_setup (void)
{
    // Set the ADC input to PB2/ADC1
    ADMUX |= (1 << MUX0);
    ADMUX |= (1 << ADLAR);
    ADMUX |= (1 << REFS0); // set ADC ref. to internal ref. 1.1V

    // Set the prescaler to clock/128 & enable ADC (fclk for ADC cca 75 kHz)
    //ADCSRA |= (1 << ADPS1) | (1 << ADPS0) | (1 << ADEN);
    // Set the prescaler to clock/64 & enable ADC (fclk for ADC cca 150 kHz)
    ADCSRA |= (1 << ADPS1) | (0 << ADPS0) | (1 << ADEN);
}

int adc_read (void)
{
    // Start the conversion
    ADCSRA |= (1 << ADSC);

    // Wait for it to finish
    while (ADCSRA & (1 << ADSC));

    return ADCH;
}

void pwm_setup(void)
{
    // Set Timer 0 prescaler to fclk/n
    //TCCR0B |= (1 << CS01) | (1 << CS00);//n=64, fpwm cca 550 Hz
    //TCCR0B |= (1 << CS01) | (0 << CS00);//n=8, fpwm cca 4.464 kHz
    TCCR0B |= (0 << CS01) | (1 << CS00);//n=1=no prescaling, fpwm cca 35.71 kHz
    // Set to 'Fast PWM' mode
    TCCR0A |= (1 << WGM01) | (1 << WGM00);
    // Clear OC0B output on compare match, upwards counting.
    TCCR0A |= (1 << COM0B1);
}

void pwm_write (int val)
{
    OCR0B = val;
}

// ... adc_setup, adc_read, pwm_setup, pwm_write, watchdog ...

int main (void)
{
    int adc_in;

    // LED is an output.
    DDRB |= (1 << PWM_OUT);
    DDRB &= ~(1<<HOLD); // Makes HOLD pin of PORTB as Input

    adc_setup();
    pwm_setup();
    wdt_enable(WDTO_500MS);
    while (1) {
        // Get the ADC value
        wdt_reset();
        adc_in = adc_read();
        if (PINB & (1<<HOLD)) {
             // Now write it to the PWM counter
            pwm_write(adc_in);
        }
    }
}