LCD på AVR med C

Å koble til en LCD på Arduino er forholdsvis enkelt. Man trenger kun LCD, brødbrett, testledninger, 10k \Omega potensiometer, 220\Omega resistor, loddebolt, loddetinn og Arduino-brett med mikrokontroller.

Kretsen

Det første man bør gjøre er å lodde fast ei pinnerekke under LCD-kretskortet, for å få best mulig kontakt når displayet er tilkoblet:

Lar man være å gjøre dette, vil ikke skjermen virke. Det vil kun dukke opp firkantede svarte bokser, fordi noen pinner har for dårlig tilkobling.

Etterpå følger man så prosjektet «Liquid Crystal Displays (LCD) with Arduino» og får ca noe som ligner på følgende oppsett:

Alle disse koblingene kan virke komplekse og uoversiktlige, men det er de ikke. De gule ledningene er for data, de røde er for 5V og svart er minus/jord. Blå blir et slags unntak, da den er ingen av delene; den gir finjustert spenning inn på LCD for å regulere skjermkontrast. Høres kanskje innviklet ut, men det er det ikke.

Arduino-kode

For å vise tekst på displayet velges et av kodeeksemplene på prosjektnettsiden:

#include <LiquidCrystal.h>

const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

void setup() {
   lcd.begin(16, 2);
   lcd.print("Hello, world!");
}

void loop() {
   lcd.setCursor(0, 1);
   lcd.print(millis() / 1000);
}

Denne kodesnutten og de andre virker greit. Displayet viser tekst. Case closed.

Embedded C

Den reelle utfordringen er å skrive en fungerende løsning i Embedded C, så man kan tweake og gjøre med koden som man vil …

Pinner

Først trengs en oversikt over hvilke porter og pinner som er brukt. Med info fra prosjektnettsiden over og kart for hvilke Arduino-pinner som er koblet til hvilke ATmega328P-pinner, får man følgende oversikt:

LCD-pinne        Arduino-pinne          ATmega328P-pinne
--------------------------------------------------------
LCD RS           digital pin 12         PB4
LCD Enable (E)   digital pin 11         PB3

LCD D4           digital pin 5 	        PD5
LCD D5           digital pin 4	        PD4
LCD D6           digital pin 3	        PD3
LCD D7           digital pin 2	        PD2

De andre pinnene forandres ikke, de kan stå der de står. Heller ikke potensiometeret trenger å kobles om.

Kode

Jeg var (u)heldig og fant noe gammel kode som ordner LCD i C. I hvertfall mer eller mindre. Dermed slipper jeg å skrive alt fra scratch. Noe som sannsynligvis ville tatt flere uker.

Men en ting jeg ikke likte i denne koden var at porter og pinner var hardkodet med #define. Derfor måtte man ha oppdatert disse opplysningene hver gang noe var feil.

Dette valgte jeg å gjøre om, så all informasjon om porter og pinner mates fra main() eller der LCD-en styres fra. Dette gjøres via en struct. Da blir alt på en plass, dette er ryddigere. Syns i hvertfall jeg.

main.cpp

#define F_CPU 16000000UL

#include <avr/io.h>
#include <util/delay.h>

#include "LCD.hpp"

int main(void)
{
   // ---------------
   // Setup:

   /*
      LCD pin         Arduino pin        ATmega328P pin
      -------------------------------------------------
      LCD RS          digital pin 12     PB4
      LCD Enable (E)  digital pin 11     PB3

      LCD D4          digital pin 5      PD5
      LCD D5          digital pin 4      PD4
      LCD D6          digital pin 3      PD3
      LCD D7          digital pin 2      PD2
   */

   struct PortsAndPinsSetupLCD setup;

   // Ports
   setup.RS_PORT = &PORTB;
   setup.E_PORT = &PORTB;
   setup.D7_PORT = &PORTD;
   setup.D6_PORT = &PORTD;
   setup.D5_PORT = &PORTD;
   setup.D4_PORT = &PORTD;

   // Pins
   setup.RS_PIN = PB4;
   setup.E_PIN = PB3;
   setup.D7_PIN = PD2;
   setup.D6_PIN = PD3;
   setup.D5_PIN = PD4;
   setup.D4_PIN = PD5;

   // Data direction
   DDRB |= (1<<setup.RS_PIN);
   DDRB |= (1<<setup.E_PIN);
   DDRD |= (1<<setup.D7_PIN);
   DDRD |= (1<<setup.D6_PIN);
   DDRD |= (1<<setup.D5_PIN);
   DDRD |= (1<<setup.D4_PIN);


   // ---------------
   // Text:

   uint8_t text1[] = "LCD-display for";
   uint8_t text2[] = "AVR, programmert";
   uint8_t text3[] = "i Embedded C og";
   uint8_t text4[] = "virker dritbra!!";


   // ---------------
   // Testing:

   // Write
   lcd_init_4p(setup);

   while (1) {

      lcd_clear(setup);
      lcd_goto_line(0, setup);
      lcd_goto_line(0, setup);
      lcd_write_string_4p(text1, setup);
      lcd_goto_line(1, setup);
      lcd_write_string_4p(text2, setup);
      _delay_ms(3000);

      lcd_clear(setup);
      lcd_goto_line(0, setup);
      lcd_goto_line(0, setup);
      lcd_write_string_4p(text3, setup);
      lcd_goto_line(1, setup);
      lcd_write_string_4p(text4, setup);
      _delay_ms(3000);
   }
}

LCD.hpp

#ifndef LCD_HPP_
#define LCD_HPP_

#include <avr/io.h>
#include <util/delay.h>

/*
   First author:  Donald Weiman
   Date:          September 16, 2013
   Summary:       4-bit data interface, busy flag not implemented.

   Second author: Ove Bakken
   Date:          March 20, 2023
   Summary:       Slightly modified implementation.
                  Every functions is fed the necessary ports and pins.
                  Does not rely upon hardcoded #defines that needs changing.
*/

// Config
struct PortsAndPinsSetupLCD {

   // Ports
   volatile uint8_t *RS_PORT;
   volatile uint8_t *E_PORT;
   volatile uint8_t *D7_PORT;
   volatile uint8_t *D6_PORT;
   volatile uint8_t *D5_PORT;
   volatile uint8_t *D4_PORT;

   // Pins
   volatile uint8_t RS_PIN;
   volatile uint8_t E_PIN;
   volatile uint8_t D7_PIN;
   volatile uint8_t D6_PIN;
   volatile uint8_t D5_PIN;
   volatile uint8_t D4_PIN;
};

// Screen locations
#define START_LOCATION_LINE_1     0x00           // start of line 1
#define START_LOCATION_LINE_2     0x40           // start of line 2

// Screen instructions
#define INSTRUCTION_SET_4BIT      0b00101000     // 4-bit data, 2-line display, 5 x 7 font
#define INSTRUCTION_ON            0b00001100     // display on, cursor off, don't blink character
#define INSTRUCTION_OFF           0b00001000     // turn display off
#define INSTRUCTION_RESET         0b00110000     // reset the LCD
#define INSTRUCTION_CLEAR         0b00000001     // replace all characters with ASCII 'space'
#define INSTRUCTION_ENTRY_MODE    0b00000110     // shift cursor from left to right on read/write
#define INSTRUCTION_SET_CURSOR    0b10000000     // set cursor position
#define INSTRUCTION_RESET_CURSOR  0b00000010     // return cursor to first position on first line

// Function prototypes
void lcd_init_4p(PortsAndPinsSetupLCD);
void lcd_write_4p(uint8_t, PortsAndPinsSetupLCD);
void lcd_write_instruction_4p(uint8_t, PortsAndPinsSetupLCD);
void lcd_write_character_4p(uint8_t, PortsAndPinsSetupLCD);
void lcd_write_string_4p(uint8_t *, PortsAndPinsSetupLCD);
void lcd_clear(PortsAndPinsSetupLCD);
void lcd_goto_line(int, PortsAndPinsSetupLCD);

#endif /* LCD_HPP_ */

LCD.cpp

#include "LCD.hpp"

void lcd_init_4p(PortsAndPinsSetupLCD setup)
{
   // Power-up delay
   _delay_ms(100);

   // IMPORTANT - At this point the LCD module is in the 8-bit mode and it is expecting to receive
   //   8 bits of data, one bit on each of its 8 data lines, each time the 'E' line is pulsed.
   //
   // Since the LCD module is wired for the 4-bit mode, only the upper four data lines are connected to
   //   the microprocessor and the lower four data lines are typically left open.  Therefore, when
   //   the 'E' line is pulsed, the LCD controller will read whatever data has been set up on the upper
   //   four data lines and the lower four data lines will be high (due to internal pull-up circuitry).
   //
   // Fortunately the 'FunctionReset' instruction does not care about what is on the lower four bits so
   //   this instruction can be sent on just the four available data lines and it will be interpreted
   //   properly by the LCD controller.  The 'lcd_write_4p' subroutine will accomplish this if the
   //   control lines have previously been configured properly.

   *setup.RS_PORT &= ~(1<<setup.RS_PIN);         // select the Instruction Register (RS low)
   *setup.E_PORT &= ~(1<<setup.E_PIN);         // make sure E is initially low

   // Reset the LCD controller
   lcd_write_4p(INSTRUCTION_RESET, setup);        // first part of reset sequence
   _delay_ms(10);

   lcd_write_4p(INSTRUCTION_RESET, setup);        // second part of reset sequence
   _delay_us(200);

   lcd_write_4p(INSTRUCTION_RESET, setup);        // third part of reset sequence
   _delay_us(200);                         // this delay is omitted in the data sheet

   // Preliminary Function Set instruction - used only to set the 4-bit mode.
   // The number of lines or the font cannot be set at this time since the controller is still in the
   //  8-bit mode, but the data transfer mode can be changed since this parameter is determined by one
   //  of the upper four bits of the instruction.

   lcd_write_4p(INSTRUCTION_SET_4BIT, setup);      // set 4-bit mode
   _delay_us(80);

   lcd_write_instruction_4p(INSTRUCTION_SET_4BIT, setup);       // set mode, lines, and font
   _delay_us(80);

   // The next three instructions are specified in the data sheet as part of the initialization routine,
   //  so it is a good idea (but probably not necessary) to do them just as specified and then redo them
   //  later if the application requires a different configuration.

   lcd_write_instruction_4p(INSTRUCTION_OFF, setup);         // turn display OFF
   _delay_us(80);

   lcd_write_instruction_4p(INSTRUCTION_CLEAR, setup);         // clear display RAM
   _delay_ms(4);

   lcd_write_instruction_4p(INSTRUCTION_ENTRY_MODE, setup);   // set desired shift characteristics
   _delay_us(80);

   // This is the end of the LCD controller initialization as specified in the data sheet, but the display
   //  has been left in the OFF condition.  This is a good time to turn the display back ON.

   lcd_write_instruction_4p(INSTRUCTION_ON, setup);         // turn the display ON
   _delay_us(80);
}

void lcd_write_string_4p(uint8_t textString[], PortsAndPinsSetupLCD setup)
{
   volatile int i = 0;                                 // character counter*/
   while (textString[i] != 0)
   {
      lcd_write_character_4p(textString[i], setup);
      i++;

      _delay_us(80);
   }
}

void lcd_write_character_4p(uint8_t data, PortsAndPinsSetupLCD setup)
{
   *setup.RS_PORT |= (1<<setup.RS_PIN);               // select the Data Register (RS high)
   *setup.E_PORT &= ~(1<<setup.E_PIN);               // make sure E is initially low

   lcd_write_4p(data, setup);                        // write the upper 4-bits of the data
   lcd_write_4p(data << 4, setup);                     // write the lower 4-bits of the data

   _delay_us(80);
}

void lcd_goto_line(int num, PortsAndPinsSetupLCD setup)                        // 0 or 1
{
   lcd_write_instruction_4p(
      INSTRUCTION_SET_CURSOR | (num == 0 ? START_LOCATION_LINE_1 : START_LOCATION_LINE_2),
      setup
   );

   _delay_us(80);
}

void lcd_clear(PortsAndPinsSetupLCD setup)
{
   lcd_write_instruction_4p(INSTRUCTION_CLEAR, setup);         // clear display RAM
   _delay_ms(4);

   lcd_write_instruction_4p(INSTRUCTION_ENTRY_MODE, setup);   // set desired shift characteristics
   _delay_us(80);
}

void lcd_write_instruction_4p(uint8_t instruction, PortsAndPinsSetupLCD setup)
{
   *setup.RS_PORT &= ~(1<<setup.RS_PIN);               // select the Instruction Register (RS low)
   *setup.E_PORT &= ~(1<<setup.E_PIN);                  // make sure E is initially low

   lcd_write_4p(instruction, setup);                  // write the upper 4-bits of the data
   lcd_write_4p(instruction << 4, setup);               // write the lower 4-bits of the data

   _delay_us(80);
}

void lcd_write_4p(uint8_t byteData, PortsAndPinsSetupLCD setup)
{
   *setup.D7_PORT &= ~(1<<setup.D7_PIN);                  // assume that data is '0'
   if (byteData & 1<<7) *setup.D7_PORT |= (1<<setup.D7_PIN);   // make data = '1' if necessary

   *setup.D6_PORT &= ~(1<<setup.D6_PIN);                  // repeat for each data bit
   if (byteData & 1<<6) *setup.D6_PORT |= (1<<setup.D6_PIN);

   *setup.D5_PORT &= ~(1<<setup.D5_PIN);
   if (byteData & 1<<5) *setup.D5_PORT |= (1<<setup.D5_PIN);

   *setup.D4_PORT &= ~(1<<setup.D4_PIN);
   if (byteData & 1<<4) *setup.D4_PORT |= (1<<setup.D4_PIN);

   // write the data                                 // 'Address set-up time' (40 nS)
   *setup.E_PORT |= (1<<setup.E_PIN);                     // Enable pin high
   _delay_us(1);                                      // implement 'Data set-up time' (80 nS) and 'Enable pulse width' (230 nS)
   *setup.E_PORT &= ~(1<<setup.E_PIN);                    // Enable pin low
   _delay_us(1);                                      // implement 'Data hold time' (10 nS) and 'Enable cycle time' (500 nS)
}

Resultat

Jeg er veldig fornøyd med hvordan dette ble:

Krets, display og kode fungerer fin-fint. Så når jeg en gang i fremtiden trenger LCD i et mer reelt prosjekt er det lett å ordne dette.

TBC

Kom i gang med C for AVR

Etter å ha prokrastinert altfor lenge, var det idag endelig på tide å komme i gang med Embedded C for ATmega328p. Dette er valgt databrikke på Arduino UNO og derfor et trygt valg når man skal lære seg mikrokontrollerprogrammering.

ATmega328p er del av AVR-familien av mikrokontrollere som Atmel (kjøpt opp av Microchip i 2016) begynte å utvikle i 1996. Hvem som helst kan skaffe seg den. Enten ved å kjøpe løst eller i stedet skaffe seg f.eks. Arduino-startpakke. Jeg valgte Arduino-startpakke siden jeg også trengte brødbrett, testledninger, enkle komponenter, osv.

Arduino-brettet med mikrokontroller er enkelt å programmere via Arduino IDE. Dette er et neddummet programmeringsverktøy som gjør programmeringen lett. Og siden det finnes mange dokumenterte Arduino-prosjekter, med ulike vanskelighetsgrader, kan hvem som helst prøve seg. Derfor er det også et smart valg når man skal lære seg mikrokontrollerprogrammering.

Den reelle utfordringen blir å skrive ren embedded C-kode som kan erstatte jallakode skrevet i Arduino IDE. Dette krever forståelse av C, registermanipulering, osv.

Første prosjekt: Lysdiode + bryter

Som første prosjekt valgte jeg å kombinere Arduino-prosjektene Blink og How to Wire and Program a Button. Forklaringen er åpenbar; å kontrollere innsignal og utsignal på mikrokontrolleren er første viktige skritt.

Pinnene PB0 og PB1 (markert med rød understreking) er de jeg valgte å bruke. Jeg kunne også brukt mange av de andre, så lenge de støtter ønsket funksjonalitet.

Her må man lese databladet for å være sikker ….

Kretsen

Først måtte bryter og lysdiode kobles riktig på brødbrettet (easy peasy):

Når bryter ikke er innkoblet skal innsignalet (via hvit ledning) være lavt, dvs. 0V. Når bryteres trykkes ned, vil derimot signalet endre seg til 5V.

Utsignalet (rød ledning) er også 5V, men dette er for høyt for vanlige lysdioder, derav resistoren. Etter spenningsfallet over resistoren, blir gjenværende spenning over lysdioden mer passe.

Dette er samme konfigurasjon som i Arduino-prosjektene lenket til over. Forskjellen er at pinne 8 og 9 ble brukt i stedet.

Arduino-kode

Deretter gjenstod det kun litt enkel kodeskriving i Arduino IDE:

const int PIN_FOR_BUTTON = 8;
const int PIN_FOR_LED = 9;
const int LONG_DELAY = 5000;
const int SHORT_DELAY = 10;

void setup() {
   pinMode(PIN_FOR_BUTTON, INPUT);
   pinMode(PIN_FOR_LED, OUTPUT);
}

void loop() {
   if (digitalRead(PIN_FOR_BUTTON) == HIGH) {
      digitalWrite(PIN_FOR_LED, HIGH);
      delay(LONG_DELAY);
   } else {
      digitalWrite(PIN_FOR_LED, LOW);
      delay(SHORT_DELAY);
   }
}

Valgte pinner deklareres øverst, sammen med ventetidene. I setup() settes inn- og utmodus. Og i loop() sjekker man innsignalet og agerer når dette endres. Ganske selvforklarende.

Testing

Etter programmet var ferdig og lastet opp, ble det testet. Og det fungerte fint.

Ved å trykke på knappen tennes dioda. Også slukkes den 5 sekunder (5000 ms) etter knappen ble sluppet igjen:

Lyset kan virke skarpt, men resistoren var på 220\Omega, slik som i det lenkede Arduino-prosjektet. Neste gang vil jeg muligens øke resistansen enda mer.

Embedded C

Kodeskrivingen over kan knapt kalles utfordrende, da den ferdige koden virket omtrent på første forsøk. Embedded C ble derimot litt mer krevende. Først og fremst pga. registermanipuleringen som tar litt tid å bli vant til.

Men etter litt knoting, lesing og testing ble det en løsning:

#undef F_CPU
#define F_CPU 16000000 // CPU frequency to be considered by the delay macros

#include <avr/io.h>
#include <util/delay.h>

int main (void)
{
   /*
      ATmega328P on Arduino board
      1x switch on pin 0 on port B
      1x LED on pin 1 on port B
   */

   // Data direction register for port B, input = 0 and output = 1
   DDRB &= ~(1<<PB0); // Set pin 0 to input on port B
   DDRB |= 1<<PB1; // Set pin 1 to output on port B

   // Delay in ms
   const int LONG_DELAY = 5000;
   const int SHORT_DELAY = 10;

   while (1) {

      // Button down (active) = high
      if (PINB & 1<<PB0) { // If pin 0 set high on port B
         PORTB |= 1<<PB1; // Set pin 1 to high on port B
         _delay_ms(LONG_DELAY);

      // Button up (inactive) = low
      } else {
         PORTB &= ~(1<<PB1); // Set pin 1 to low on port B
         _delay_ms(SHORT_DELAY);
      }
   }
}

Her brukes makroer (DDRB, PB0, osv.), dette forenkler arbeidet og gjør koden lesbar.

Samme kode ville riktignok også fungert i Arduino IDE, men å bli vant til å kun bruke denne IDE-en er ikke særlig smart. Den er altfor simpel. Det er sunt å fikle med andre IDE-er, sette opp avrdude manuelt, osv. for å bli mest mulig stødig.

Min valgte IDE for Embedded C er inntil videre Eclipse Classic 3.7.0, med AVR-plugin installert. Dette er da på Linux-distro Zorin OS. For å laste opp til mikrokontrolleren benyttes avrdude, men dette gjøres sømløst fra Eclipse. Det er ikke knoting i terminal.

Tanken var å gå over til MPLAB X IDE fra Microchip, men førsteinntrykket skuffet.

Oppgave: Finn spenninger og energi konsumert i RL-krets

Det antas at bryteren i denne RL-kretsen slås over etter spolen har samlet opp maks energi. Spenningen over spolen, eller mer riktig spenningsfallet, er da blitt null. Den skaper ikke lenger resistans, men heller kortslutning.

Det betyr at eneste motstand strømmen (i_{s}=6.4A) har, er resistorene 10\Omega og 6\Omega rett før bryteren åpnes.

Startstrømmen i spolen etter bryteren trykkes (t>0):

I_{0}=\frac{10\Omega}{10\Omega+6\Omega}\cdot6.4A=4A (strøminversformelen)

Mens resistansen blir:

R_{eq}=4\Omega|(6\Omega+10\Omega)=4\Omega|16\Omega=3.2\Omega

Strømmen og spenningen for spolen (t>0) er dermed:

i_{L}(t)=I_{0}\cdot e^{-(R/L)t}A=4\cdot e^{-(3.2/0.32)t}A v_{L}(t)=L\frac{di}{dt}=0.32H\cdot\frac{d}{dt}i(t)=-12.8e^{-10t}V

a)

Spenningen v_{0} må bli:

v_{0}(t)=\frac{10\Omega}{10\Omega+6\Omega}\cdot-12.8e^{-10t}V=-8e^{-10t}V

b)

Energi i spolen etter bryteren trykkes (t>0):

w_{L}(\infty)=\int_{0}^{\infty}i_{L}(t)\cdot v(t)\cdot dt=2.56J

Energi benyttet av resistoren 4\Omega:

w_{R4\Omega}(\infty)=\int_{0}^{\infty}\frac{v_{L}(t)}{4\Omega}\cdot v_{L}(t)\;dt=2.048J

I prosent får man:

\frac{w_{R4\Omega}}{w_{L}}=\frac{2.048J}{2.56J}=0.8\rightarrow80\%.

Altså går mesteparten av energien i spolen til resistoren 4\Omega etter bryteren trykkes.

RL-krets

En RL-krets består av en energikilde, en resistor og en induktor (spole). Når energikilden er tilkoblet vil induktoren bygge opp en viss energi. Denne vil brukes opp av resistoren hvis energikilden kobles ut.

Mao. kan man en kort periode overleve uten å være tilkoblet en energikilde.

Strømmen etter utkoblingen blir som følger:

i(t)=I_{0}\cdot e^{-(R/L)t} når t\geq0

Størrelsen I_{0} er startstrømmen som induktoren yter, rett etter energikilden er koblet fra. Denne er like stor som før utkoblingen, fordi strøm gjennom en induktor ikke kan forandre størrelse brått. Etter utkoblingen avtar strømmen eksponentielt og blir null. Om man ønsker å finne spenningen for dette tidsrommet benyttes Ohms lov.

Med like stor strøm gjennom induktoren før og etter utkobling, er det lett å anta at spenningen også er den samme. Men det er den ikke. Før utkobling – hvis maks energi er oppsamlet i induktoren, så er spenningen null. Altså er det ikke noe spenningsfall, men dette forandrer seg øyeblikkelig når t>0.

Energien absorbert av resistoren etter energikilden er frakoblet blir:

w(t)=\frac{1}{2}L\cdot I_{0}^{2}\cdot(1-e^{-2(R/L)t})

TBC

Oppgave: Finn spenning i kondensator tilkoblet pulserende strømkilde

Løsning

Først kan man notere seg de gitte opplysningene:

C=0.1\cdot10^{-6}F.

v_{0}=15V

Strøm

Fra bildet over kan man avlese strømmen i hvert intervall. Denne er oppgitt i mA, mens t (for tid) er i mikrosekunder:

  i(t)=\begin{array}{ccc}  -50 & , & 0\leq t\leq10\\  100 & , & 10\leq t\leq20\\  160 & , & 20\leq t\leq40\\  0 & , & 40\leq t\leq\infty  \end{array}

Merk: Startspenningen v_{0}=15V er oppgitt i Volt (V) og ikke milliVolt (mV). Så man må bli kvitt strømmens mA størrelse, før den skal brukes til å finne V:

  i(t)=\begin{array}{ccc}  \frac{-50}{1000} & , & 0\leq t\leq10\\  \frac{100}{1000} & , & 10\leq t\leq20\\  \frac{160}{1000} & , & 20\leq t\leq40\\  \frac{0}{1000} & , & 40\leq t\leq\infty  \end{array}

Her er strømmen i hvert intervall nå oppgitt i Ampere (A). Så da er vi klare for å endelig finne spenningen!

Spenning

Formel v=\frac{1}{C}\int_{t_{0}}^{\;t}idt+v_{t_{0}} benyttes for å finne spenning fra strøm. Bokstaven i erstattes med korrekt størrelse over.

Første intervall

I første intervallet er strømmen i_{1}(t)=\frac{-50}{1000}A.

Dette gir v_{1}(0,t)=\frac{1}{0.1\cdot10^{-6}}\int_{0}^{\;t}(\frac{-50}{1000})dt+15=15-5\cdot10^{5}t

Størrelsen v_{t_{0}}=15V var her allerede opplyst, så arbeidet ble lettere.

Andre intervall

For å finne spenningen for resterende intervall må v_{t_{0}} beregnes. Altså spenningen på slutten av forrige intervall.

Og for første intervallet er spenningen på slutten:

v_{t=10\mu s}=v_{1}(10)=15-(5\cdot10^{5})\cdot(10\cdot10^{-6})=10V

Dermed blir spenningen i andre intervallet:

v_{2}(10,t)=\frac{1}{0.1\cdot10^{-6}}\int_{10\cdot10^{-6}}^{\;t}(\frac{100}{1000})dt+10=10^{6}t

Tredje intervall

Spenningen på slutten i andre intervallet:

v_{t=20\mu s}=v_{2}(20)=10^{6}\cdot(20\cdot10^{-6})=20V

Spenningen i tredje intervallet:

v_{3}(20,t)=\frac{1}{0.1\cdot10^{-6}}\int_{20\cdot10^{-6}}^{\;t}(\frac{160}{1000})dt+20=1.6\cdot10^{6}t-12

Fjerde intervall

Spenningen på slutten i forrige (tredje) intervall:

v_{t=40\mu s}=v_{3}(40)=1.6\cdot10^{6}\cdot(40\cdot10^{-6})-12=52V

Spenningen i fjerde og siste intervall:

v_{4}(40,t)=\frac{1}{0.1\cdot10^{-6}}\int_{40\cdot10^{-6}}^{\;t}(\frac{0}{1000})dt+52=52V

Resultat

Oppsummert blir spenningen i intervallene som følger:

  v(t)=\begin{array}{ccc}  15-5\cdot10^{5}t & , & 0\leq t\leq10\\  10^{6}t & , & 10\leq t\leq20\\  1.6\cdot10^{6}t-12 & , & 20\leq t\leq40\\  52 & , & 40\leq t\leq\infty  \end{array}

Graf som viser spenningen:

Oppgave: Finn strøm, effekt og energi for kondensator på 200 uF

Løsning

a)

Først må man finne spenningen for de bestemte intervallene:

t<0, v(t)=0,
0\leq t\leq2s, v(t)=10t,
2s\leq t\leq6s, v(t)=-10t+40,
6s\leq t\leq8s, v(t)=10t-80,
t>8s, v(t)=0

b)

Strømmen finner man med i(t)=C\frac{d}{dt}v(t). Dette er kapasitans ganget med den deriverte av spenningen over tid:

t<0, i(t)=0
0\leq t\leq2s, i(t)=\frac{200}{1000000}\frac{d}{dt}10t=\frac{2}{1000}
2s\leq t\leq6s, i(t)=\frac{200}{1000000}\frac{d}{dt}(-10t+40)=-\frac{2}{1000}
6s\leq t\leq8s, i(t)=\frac{200}{1000000}\frac{d}{dt}(10t-80)=\frac{2}{1000}
t>8s, i(t)=0

Effekt og energi blir p(t)=v(t)\cdot i(t) og w(t)=\frac{1}{2}Cv^{2}, hvor man enkelt setter inn riktige opplysninger og formler fra over.

Graf som viser effekt:

Graf som viser både effekt og energi:

c)

Når energien i kondensatoren øker (0\leq t\leq2 og 4\leq t\leq6), betyr dette at energi absorberes av kondensatoren. Når energien tømmes (2\leq t\leq4 og 6\leq t\leq8) betyr dette at energi i stedet ytes.

Kondensator

For å kortvarig oppbevare elektrisk energi i en elektrisk krets benyttes en kondensator (på engelsk “capacitor”). Dette er en mye brukt type komponent med flere bruksområder.

Vanligvis består den av to metallplater, separert av et isolasjonsmateriale. Det går ikke strøm mellom platene, i stedet hopper det over ladning etter denne har bygget seg opp på platene. Platene får forskjellig polaritet, så ene blir positiv og den andre negativ.

Formler

Når man regner på kondensatorer må man forholde seg til tid. Som regel brukes bokstaven t. Som med induktorer forholder man seg også her gjerne til millisekunder og området t>0.

Kapasitans

Ladningkapasiteten bestemmes av dens kapasitans (C). Denne oppgis i en tallstørrelse, med Farad (F) som måleenhet. Siden kapasitans på 1 F er ganske mye, ser man ofte tilsynelatende små kondensatorer med mikrofarad (\mu F) kapasitans eller mindre.

Sammenlignet med resistorer og induktorer er det vanskelig å se for seg hvordan kondensatorer virker. Spesielt når de er koblet i serie eller parallelt. Men man kan sammenligne med kanallåser, dvs. sluser som veldig sakte slipper store skip eller båter gjennom kanaler, og hvor det er forskjellige vannivåer:

Å slippe gjennom en slik sluse kan være en tidkrevende prosess. Så det er ganske opplagt at kanallåser (kondensatorer) i parallell vil øke kapasiteten. Som på bildet, hvor det er 2 stk. sluser rett siden av hverandre. Formelen for kapasitansen blir da, C_{eq}=C_{1}+C_{2}+C_{n}.

Kanallåser (kondensatorer) i serie vil derimot gjøre til at tempoet blir tregere, formel:

C_{eq}=\frac{1}{\frac{1}{C_{1}}+\frac{1}{C_{2}}+\frac{1}{C_{n}}}

Strøm

Strømmen en kondensator kan yte fra terminalene, bestemmes av kapasitansen og spenningsendring:

i(t)=C\frac{dv}{dt}

Her er \frac{dv}{dt} endringen i spenning over tid over kondensatoren. Til høyere denne er, til høyere blir strømmen som kondensatoren kan yte.

Spenning

Fra strømmen kan man finne spenningen:

v(t)=\frac{1}{C}\int_{0}^{\;t}i(t)dt+v_{0}

Her er v_{0} spenningen på startøyeblikket.

Effekt og energi

p(t)=v(t)\cdot i(t) er den kjedelige effektformelen.

w(t)=\frac{1}{2}Cv(t)^{2} er energien i kondensatoren.

Induktor

En induktor (på engelsk «inductor») er en elektrisk komponent som skaper et magnetfelt rundt seg, når strømmen gjennom komponenten endrer seg i størrelse. Magnetfeltet som skapes kan levere energi til andre komponenter som er nære.

Et mer vanlig navn – et navn som brukes i praksis, er spole. Dens konstruksjon er veldig enkel. Man lager viklinger av kobbertråd, rundt en kjerne som gjerne er av et magnetisk metall. Størrelse, antall viklinger, materialvalg, osv. er avhengig av bruksområde.

Formler

For å regne på induktorer må man først vite at det inngår en variabel for tid. Og som regel brukes bokstaven/symbolet t. Her forholder man seg gjerne til millisekunder og området t>0.

Induktans

Komponentens evne til å skape magnetfeltet oppgis som en tallstørrelse, med måleenhet Henry (H). Dette er induktansen til induktoren, ofte med symbol L når man gjør utregninger. Til høyere induktansen (L) er, til kraftigere blir magnetfeltet. Dette betyr også mer motstand (resistans) og høyere spenningsfall.

Induktorer minner litt om resistorer. Når de er koblet i serie kan man intuitivt summere induktansen med L_{eq}=L_{1}+L_{2}+L_{n}. Og hvis de i stedet er koblet parallelt blir det L_{eq}=\frac{1}{\frac{1}{L_{1}}+\frac{1}{R_{2}}+\frac{1}{R_{n}}}.

Spenning

Siden komponenten skaper motstand blir det et spenningsfall over den:

v(t)=L\cdot\frac{di}{dt}

Spenning(sfallet) er lik induktansen L ganget med \frac{di}{dt}, som er endringen i strømstørrelse (dvs. den deriverte av strømmen) for et gitt øyeblikk.

Strøm

Om formel for strøm er ukjent, kan man finne den hvis formel for spenning er kjent:

i(t)=\frac{1}{L}\int_{0}^{\;t}v(t)\;dt+i_{0}

Her er i_{0} strømmen som eksisterer i startøyeblikket, når t=0 eller ved senere valgt tidspunkt.

Effekt og energi

To andre formler man også kan få bruk for:

w(t)=\frac{1}{2}Li(t)^{2} som er energien i induktoren.

p(t)=i(t)\cdot v(t) som er den vanlige effektformelen.

Eksempel

En strømkilde genererer en strøm i=10te^{-5t}A. Denne strømkilden er koblet til en induktor med induktans L=100mH.

a) Lag en graf som viser strømmen

Graf som viser strømmen i=10te^{-5t}A:

b) På hvilket tidspunkt er strømmen størst?

Strømmen er størst i det øyeblikket strømmen går fra positiv økning til negativ. Makspunktet (dvs. snupunktet) vil derfor være når strømmens deriverte er lik 0.

Den deriverte av i=10te^{-5t} er i'=10te^{-5t}\frac{d}{dt}=10\cdot e^{-5t}(1-5t).

Ved å sette i'=10\cdot e^{-5t}(1-5t)=0 får man t=\frac{1}{5}s=0.2s. Altså er strømmen størst etter 0.2 sekunder.

c) Uttrykk spenningsfallet som en funksjon av tid

Her kommer formelen lenger opp inn i bildet.

Spenningsfallet over induktoren blir v=L\cdot\frac{di}{dt}=0.1\cdot10e^{-5t}(1-5t)V=e^{-5t}(1-5t)V.

d) Lag en graf som viser spenningen

Graf som viser spenningen v=e^{-5t}(1-5t)V:

e) Er spenningen og strømmen størst samtidig?

Nope. Spenningen er størst til å begynne med, når strømmen øker raskest. I det øyeblikket strømmen er på maks og snur (med endring lik 0), er spenningsfallet 0.

f) På hvilket tidspunkt endrer spenningen polaritet?

Spenningen endrer polaritet i samme øyeblikk strømmen begynner sin reduksjon. Dette er altså etter 0.2 sekunder.

g) Finnes det noen øyeblikk hvor spenningen endres drastisk over induktoren?

Tja. Akkurat i starten når strømkilden begynner å jobbe går spenningsfallet øyeblikkelig til 1V. Som allerede vist i grafen.

Matematisk får man nøyaktig samme svar:

v=e^{-5t}(1-5t)V med t=0 gir v=e^{-5\cdot0}(1-5\cdot0)V=1V

Operasjonsforsterker

Innenfor analog elektronikk er behandling av signaler et viktig felt. Her kommer operasjonsforsterkeren inn i bildet. På engelsk er dette «operational amplifier», med «op amp» som en mye brukt forkortelse – også på norsk.

Jobben til denne komponenten er å forsterke et signal som gis til den. Mer konkret er det differansen i spenning på inngangene som forsterkes. Signalet inn kan også inverteres, da får spenningen på utgangssignalet motsatt fortegn.

Operasjonsforsterkeren er en mye brukt komponent som masseproduseres og brukes i alt mulig rart utstyr i dagens samfunn.

Feedback

Siden mange operasjonsforsterkere kan forsterke svært mye, betyr det at de er veldig sensitive og trenger en form for «feedback» som demper inngangssignalet:

Hvis inngangssignalet IKKE dempes vil utgangssignalet som regel bli begrenset av maksspenningen som operasjonsforsterkeren klarer å yte. Dette er driftsspenningen og vanligvis mindre enn 20V. Alt over dette nivået "klippes" bort og gjør utsignalet rimelig ubrukelig.

Arbeidsområde

Når en operasjonsforsterker produserer et utsignal (med enten positiv eller negativ spenning), og denne er lavere enn driftsspenningen, sies det at den opererer innenfor sitt lineære arbeidsområde .. En endring i inngangssignalet vil da også gi en endring i utgangssignalet.

Formler

Innenfor det lineære arbeidsområdet er det så liten spenningsdifferanse på inngangene at man kan sette v_{p}=v_{n}. Altså sier man at spenningen er lik på inngangene. Dette forenkler utregningene betydelig.

Trenger man å sette opp Kirchhoff-ligninger for strøm kan man også anta at strøm inn på signalinngangene er 0. Men den er ikke nødvendigvis 0 på utgangen.

Velger man å forholde seg til disse forenklingene betyr det at man behandler operasjonsforsterkeren som om den var ideell (dvs. perfekt). Oppsiden er at det blir mindre hodebry når man skal regne. Nedsiden er at svaret kanskje kan avvike litt, eller bittelitt, fra det som ville vært tilfellet i virkeligheten.

Konfigurasjoner

Størrelsene på resistorene og hvordan inngangene på en operasjonsforsterker benyttes, kan gi veldig forskjellige resultat. Så det finnes flere forskjellige konfigurasjoner å velge mellom, alt ettersom hvilken arbeidsoppgave man ønsker at operasjonsforsterkeren skal utføre.

Her er 6 stk. forskjellige konfigurasjoner som skal forsøke å vise forskjellene:

I illustrasjonen over forsterker alle operasjonsforsterkerne 100 000 ganger. Altså er spenningen ut lik spenningsdifferansen på inngangene, men ganget 100 000 ganger (!).

Hvis spenningen på minus (inverteringsinngangen) er høyest, gjør dette at utgangssignalet får motsatt fortegn. Alle konfigurasjonene har feedback fra utgangssignalet så inngangssignalet blir dempet. Driftsspenningen og dermed det lineære arbeidsområdet er satt til v_{cc}\pm15V.

For alle konfigurasjonene kan man anta at operasjonsforsterkeren er ideell. Så signalinngangene trekker ikke strøm. Og for å få enklere utregninger benyttes v_{p}=v_{n} for inngangssignalet.

Konfigurasjon #1

Dette er enkleste eksemplet. Plussinngangen (ikke-inverterende inngang) blir v_{s}=v_{p}=1V. Takket være grenen for feedback blir utgangssignalet v_{o}=10V. Dette er godt innenfor maksgrensen på 15V. Mao. er v_{o}=10v_{s}, så g=10 er forsterkningen.

Legg merke til hvor lav spenningsdifferanse (99.99\mu V) det er på inngangene. Da kan man sette v_{s}=v_{p}=v_{n}=1V.

Sammenhengene i kretsen kan man finne ved å benytte Kirchhoffs strømlov, som så forenkles fordi man antar ideelle forhold. For å finne spenninger fra spenningskilder, i tilfelle disse er adskilt med resistorer, benyttes Ohms lov.

Formel for feedback-gren

v_{n}=v_{o}\cdot\frac{R_{n}}{R_{n}+R_{f}} som gir v_{o}=v_{n}\frac{R_{n}+R_{f}}{R_{1}}

Med tallstørrelser

v_{n}=10V\cdot\frac{1k\Omega}{1k\Omega+9k\Omega}=10V\cdot\frac{1}{10}=1V,

v_{o}=1V\cdot\frac{1k\Omega+9k\Omega}{1k\Omega}=1V\cdot10=10V

Uansett spenning fra v_{s} så vil v_{o} bli tidobbelt, så lenge operasjonsforsterkeren opererer innenfor det lineære arbeidsområdet.

Konfigurasjon #2

Dette eksemplet er ganske likt #1. Forskjellen er invertering så utgangssignalet får motsatt fortegn.

Plussinngangen har ikke spenning, v_{p}=0V. Mao. må v_{n} nesten være 0 den også. Ved utregning kan man da forenkle og sette v_{n}=v_{p}=0.

Formel for feedback

v_{o}=-\frac{R_{f}}{R_{n}}\cdot v_{s} som gir v_{s}=-\frac{R_{n}}{R_{f}}\cdot v_{o}

Med tallstørrelser

v_{o}=-\frac{10k\Omega}{1k\Omega}\cdot1V=-10\cdot1V=-10V v_{s}=-\frac{1k\Omega}{10k\Omega}\cdot(-10)=-\frac{1}{10}\cdot(-10V)=1V

Så lenge man er innenfor det lineære arbeidsområdet blir v_{o} det tidobbelte av v_{s}, men med motsatt fortegn.

Konfigurasjon #3

Denne ser annerledes ut enn konfigurasjon #1, men fungerer likt.

Operasjonsforsterkeren trekker ikke strøm, så det blir ikke noe spenningsfall over resistorene etter spenningskildene v_{a}=1V og v_{b}=1V. (Da antar man at operasjonsforsterkeren er ideell.) Man kan da sette v_{a}=v_{b}=v_{p}=v_{n} og gjøre samme utregninger som i konfigurasjon #1.

For å skape spenningsfall over resistorene etter v_{a}=1V og v_{b}=1V, måtte man gjort som i konfigurasjon #5.

Konfigurasjon #4

Dette er en såkalt summerende inverterende konfigurasjon. Med unntak av de to spenningskildene (med halvert spenning), så er denne konfigurasjonen lik konfigurasjon #2.

Også her er v_{p}=0. Dermed må v_{n} nesten være 0 også. Ved utregning kan man benytte v_{p}=v_{n}, sånn som tidligere.

Siden spenningen fra spenningskildene er halvert leveres halve strømmen. Men sammen leverer de like mye som i konfigurasjon #1, så resten av kretsen fungerer likt. Like mye strøm går gjennom R_{f}=10k\Omega, så spenningsfallet er det samme samt feedbacken.

Formel for feedback

v_{o}=-\frac{R_{f}}{R_{a}}\cdot v_{a}+-\frac{R_{f}}{R_{b}}\cdot v_{b}

v_{a}=-v_{b}-v_{o}\cdot\frac{R_{a}}{R_{f}} og v_{b}=-v_{a}-v_{o}\cdot\frac{R_{b}}{R_{f}}

Med tallstørrelser

v_{o}=-\frac{10k\Omega}{1k\Omega}(0.5V+0.5V)=-10V v_{a}=v_{b}=-0.5V+10V\cdot\frac{1}{10}=0.5V

I dette tilfellet er det kun to identiske spenningskilder med identiske resistorer. Så ganske enkelt. Men samme fremgangsmetode gjelder uansett antall spenningskilder, spenninger og størrelser på resistorer.

Konfigurasjon #5

Her også er det en summerende konfigurasjon. Men den er ikke-inverterende, fordi plussinngangen v_{p}=1V har høyere spenning enn minus v_{n}=999.900019mV.

Å sette v_{p}=v_{n} når man skal regne ut, hvis det er nødvendig, er fortsatt greit.

Formel for feedback

v_{o}=\frac{R_{f}}{R_{a}}\cdot v_{a}+\frac{R_{f}}{R_{b}}\cdot v_{b}

dette gir

v_{a}=v_{o}\frac{R_{a}}{R_{f}}-v_{b} og v_{b}=v_{o}\frac{R_{a}}{R_{f}}-v_{a}

Med tallstørrelser

v_{o}=\frac{10k\Omega}{1k\Omega}(0.5V+0.5V)=10V,

v_{a}=v_{b}=10V\cdot\frac{1}{10}-0.5V=0.5V

Formel for plussinngangen

v_{p}=v_{c}\cdot\frac{R_{4}}{R_{4}+R_{3}}

Med tallstørrelser

v_{p}=2V\cdot\frac{1k\Omega}{1k\Omega+1k\Omega}=2V\cdot\frac{1}{2}=1V

Konfigurasjon #6

Her er det en summerende og inverterende konfigurasjon.

Formel for plussinngangen

v_{p}=v_{c}\cdot\frac{R_{4}}{R_{4}+R_{3}}

Med tallstørrelser

v_{p}=1V\cdot\frac{900\Omega}{900\Omega+1k\Omega}=1V\cdot0.474=0.474V

Ved å sette opp en Kirchhoff-strømligning for feedback-grenen får man

\frac{v_{a}-v_{n}}{1.1k\Omega}+\frac{v_{b}-v_{n}}{0.9k\Omega}=\frac{v_{n}-v_{o}}{10k\Omega}

som blir

\frac{1.1V-0.474V}{1.1k\Omega}+\frac{0.9V-0.474V}{0.9k\Omega}=\frac{0.474V-v_{o}}{10k\Omega}

og v_{o}=-9.95V som jo stemmer ganske bra.

TBC

Oppgave: Definer opampkonfigurasjon og finn arbeidsområdet

Oppgave

Løsning

a)

Her kan man enkelt se at det er tilknyttet spenningskilder til den ikke-inverterende inngangen på forsterkeren. Og siden det er to stk. av disse, blir dette da en såkalt summerende konfigurasjon.

Konklusjon: Summerende ikke-inverterende operasjonsforsterkerkonfigurasjon.

b)

Her får man to ligninger:

v_{n}=v_{o}\frac{24k}{24k+96k}=0.2v_{o} (øverste gren, med feedback)

\frac{5-v_{p}}{16k}+\frac{v_{s}-v_{p}}{24k}\approx0 (nederste gren, med innsignal)

Hvis man antar at forsterkeren er i det lineære arbeidsområdet, kan man sette v_{p}\approx v_{n} og forenkle samt kombinere ligningene. Da får man:

v_{o}=\frac{16000v_{s}+120000}{8000}

c)

For å være i det lineære arbeidsområdet må v_{o} være innenfor v_{cc}\pm10. Da må man finne maks og minimum for v_{s} som gir dette resultatet:

10=\frac{16000v_{s}+120000}{8000} og

-10=\frac{16000v_{s}+120000}{8000} gir

-\frac{25}{2}\leq v_{s}\leq-\frac{5}{2}

Demonstrasjon:

For å matematisk kontrollere dette svaret prøver man hver ekstremalverdi i ligningen over. Dette skal da gi v_{o}\pm10.