This shows you the differences between two versions of the page.
pm:lab:lab0xc0-2 [2020/03/22 14:42] alexandru.predescu |
pm:lab:lab0xc0-2 [2020/03/29 13:04] (current) dumitru.tranca old revision restored (2020/03/27 17:14) |
||
---|---|---|---|
Line 1: | Line 1: | ||
<hidden> | <hidden> | ||
- | Formatul: | + | Formatul si observatii: |
- | * Conferinta globala pentru prezentarea labului si call cu fiecare pentru verificare | + | * Conferinta globala pentru prezentare si call cu fiecare pentru feedback individual si verificare. |
- | * La ex 5, 6 sunt 2 variante, una mai simpla (un singur digit) cu 2p si una mai complicata (2 digiti cu multiplexare) cu 3p. Rezulta punctajul total de 10p/12p. | + | * Daca dureaza mai mult de 2 ore, sau daca sunt deja mai multi care au terminat, se poate verifica direct pe channel ca sa mearga mai repede. (chiar daca se mai inspira unii de la altii, macar s-au chinuit 2 ore). La mine a durat luni aproape 3 ore chiar si asa. |
+ | * Atentie la rezistente sa nu puna 10k pe LED-uri si sa se incurce ca nu lumineaza. | ||
+ | * Save periodic la schema. | ||
+ | * La ex 5, 6 sunt 2 variante, una mai simpla (un singur digit) cu 2p si una mai complicata (2 digiti cu multiplexare) cu 3p. | ||
+ | * Total 10p/12p. | ||
</hidden> | </hidden> | ||
/*<hidden>*/ | /*<hidden>*/ | ||
- | ====== Lab0xc1: Interrupts and Timers ====== | + | ====== Laboratorul 0xC1: Interrupts and Timers. Build a 7-segment digital counter ====== |
- | This lab covers the basics for working with **External Interrupts**, **Pin Change Interrupts** and **Timers** on the Atmega324. You will use buttons to interact with your design and you will create your own display using the good old 7-segment display. You can check out the lab [[pm:lab:lab2|Laboratorul 2: Întreruperi, Timere]] and datasheet ({{:pm:doc8272.pdf|Datasheet ATmega324}}) for more information and references about timers and interrupts. | + | This lab covers the basics for working with **External Interrupts**, **Pin Change Interrupts** and **Timers** on the Atmega324. You will use timers and interrupts to create a digital counter and you will use buttons, LEDs and the good old 7-segment display to interact with your design in Proteus. You can check out the lab [[pm:lab:lab2|Laboratorul 2: Întreruperi, Timere]] and datasheet ({{:pm:doc8272.pdf|Datasheet ATmega324}}) for more information and references about timers and interrupts. |
===== Interrupts and Timers ===== | ===== Interrupts and Timers ===== | ||
Line 29: | Line 33: | ||
<note tip> | <note tip> | ||
- | External Interrupts and Pin Change Interrupts are both used to detect GPIO events. There are only 3 External Interrupts on the Atmega324 and they can be configured for edge detection (rising edge, falling edge), while Pin Change Interrupts are available for all the ports (and pins) to detect logic level changes. | + | External Interrupts and Pin Change Interrupts are both used to detect GPIO events. There are only 3 External Interrupts on the Atmega324 and they can be configured for edge detection (rising edge, falling edge) on pins PD2, PD3 and PB2, while Pin Change Interrupts are available for all the ports (and pins) to detect logic level changes (but require more overhead to detect the pin that triggered the interrupt). |
</note> | </note> | ||
Line 72: | Line 76: | ||
// disable global interrupts | // disable global interrupts | ||
cli(); | cli(); | ||
- | |||
// configure pin change interrupt vector | // configure pin change interrupt vector | ||
PCICR |= (1 << PCIE1); // enable the pin change interrupt, set PCIE1 to enable PCMSK1 scan | PCICR |= (1 << PCIE1); // enable the pin change interrupt, set PCIE1 to enable PCMSK1 scan | ||
- | |||
// enable pin change interrupt | // enable pin change interrupt | ||
PCMSK1 |= (1 << PCINT9); // Turns on PCINT9 (PB1) | PCMSK1 |= (1 << PCINT9); // Turns on PCINT9 (PB1) | ||
- | |||
// enable global interrupts | // enable global interrupts | ||
sei(); | sei(); | ||
Line 86: | Line 87: | ||
ISR(PCINT1_vect) | ISR(PCINT1_vect) | ||
{ | { | ||
- | // cod întrerupere de tip pin change | + | // interrupt code |
if ((PINB & (1 << PB1)) == 0) | if ((PINB & (1 << PB1)) == 0) | ||
{ | { | ||
- | // întrerupere generată de pinul PB1 | + | // interrupt generated by PB1 |
} | } | ||
} | } | ||
Line 98: | Line 98: | ||
==== Timers ==== | ==== Timers ==== | ||
- | Timers are used to count fixed time intervals without busy-waiting (delay). They are defined by the counter register //TCNT//, a prescaler and some interrupt vectors that can be defined to trigger when the counter reaches a given threshold (e.g //OCRnA//, //OCRnB//). | + | Timers are used to count fixed time intervals without busy-waiting (delay). They are defined by the counter register //TCNT//, a prescaler and some interrupt vectors that can be defined to trigger when the counter reaches a preset threshold (e.g //OCRnA//, //OCRnB//). |
Timers can be configured for multiple use cases: | Timers can be configured for multiple use cases: | ||
Line 126: | Line 126: | ||
As we know, all the peripherals, including Timers are connected to the CPU by some registers for configuration and input-output: | As we know, all the peripherals, including Timers are connected to the CPU by some registers for configuration and input-output: | ||
- | * TCCRn: In this register (used to configure the timer), there are 8 bits, but only last 3 bits CS02, CS01 and CS00 are used. These are CLOCK SELECT bits used to setup the prescaler. | + | * TCCRn: In this register (used to configure the timer), there are 8 bits, but only last 3 bits CS02, CS01 and CS00 are used. These are ClockSelect bits used to setup the prescaler. |
* TCNTn: This is the timer counter register. | * TCNTn: This is the timer counter register. | ||
* TIMSKn: This register is used to enabled/disable the Timer interrupts. | * TIMSKn: This register is used to enabled/disable the Timer interrupts. | ||
Line 199: | Line 199: | ||
**1.** (1p) Now we are dealing with timers, so accurate timing is important. | **1.** (1p) Now we are dealing with timers, so accurate timing is important. | ||
- | In the previous lab, the microcontroller was configured to use the internal 8MHz oscillator on the Atmega for clock generation, with a clock divider (''CLKDIV8'' fuse bit) set by default, making for a 1MHz actual clock. However, if we want higher precision/more speed we can use an external oscillator (crystal/quartz) with frequencies up to 20MHz for the Atmega324. | + | In the previous lab, the microcontroller was configured (by default) to use the internal 8MHz oscillator on the Atmega for clock generation, with a clock divider (''CLKDIV8'' fuse bit) set by default, making for a 1MHz actual clock. However, if we want higher precision/more speed we can use an external oscillator (crystal/quartz) with frequencies up to 20MHz (with Vcc = 5V) for the Atmega324. |
- | For this, we have to set the fuse bits as shown in the figure below. Right click on the microcontroller and select //Edit Component// to open the configuration editor and change the fuse bits to use external oscillator (''CKSEL'' Fuses) and disable the clock divider (clear 'CLKDIV8') fuse bit. Configure the clock speed at 12MHz (for convenience, to match the clock on the PM boards): | + | For this, we have to set the fuse bits as shown in the figure below. Right click on the microcontroller and select //Edit Component// to open the configuration editor and change the fuse bits to use external oscillator (''CKSEL'' Fuses) and disable the clock divider (clear 'CLKDIV8') fuse bit. Configure the clock speed at 12MHz (for convenience, to match the clock on the PM boards and use the same Makefile): |
<note tip> | <note tip> | ||
Line 209: | Line 209: | ||
{{ :pm:lab:lab2:config_ext_osc.png?400 |}} | {{ :pm:lab:lab2:config_ext_osc.png?400 |}} | ||
- | **2.** (1p) Add a quartz crystal from the parts list and connect it to the ''XTAL1'' and ''XTAL2'' pins on the Atmega324. Right click on the crystal to open the configuration editor and set the frequency to 12MHz. | + | **2.** (1p) Add a quartz crystal from the parts list and connect it to the ''XTAL1'' and ''XTAL2'' pins on the Atmega324. Right click on the crystal to open the configuration editor and set the frequency to 12MHz. Note: for physical designs, you need additional 22pF capacitors on each pin (with the other sides connected to GND). |
**3.** (2p) Connect a push button to ''PD3'' and a generic LED to ''PD4''. Pick a resistor for the LED to draw less than 10mA. The LED will show the state (enabled/disabled) of the 7-segment digital counter that we'll build next. When the user pushes the button, the program will enable/disable the counter instantly (use interrupts on ''PD3'' and the PCINT ISR on ''PORTD'' to detect the button transition). | **3.** (2p) Connect a push button to ''PD3'' and a generic LED to ''PD4''. Pick a resistor for the LED to draw less than 10mA. The LED will show the state (enabled/disabled) of the 7-segment digital counter that we'll build next. When the user pushes the button, the program will enable/disable the counter instantly (use interrupts on ''PD3'' and the PCINT ISR on ''PORTD'' to detect the button transition). | ||
Line 215: | Line 215: | ||
{{ :pm:lab:lab2:schematic.png?600 |}} | {{ :pm:lab:lab2:schematic.png?600 |}} | ||
- | **4.** (2p) Configure Timer1 to generate interrupts every second. Now, use a software counter (aka. variable) to count seconds ([0..9] for 5.1 and [0..99] for 5.2). Now, add another LED to ''PD2'' to check if the counter is working. Make the LED toggle on each timer interrupt. Watch the LED blinking. What is the frequency of the LED blinking? | + | **4.** (2p) Configure Timer1 to generate interrupts every second. Now, use a software counter (aka. variable) to count seconds (0-9 for 5.1 and 0-99 for 5.2). Now, add another LED to ''PD2'' to check if the counter is working. Make the LED toggle on each timer interrupt. Watch the LED blinking. What is the frequency of the LED blinking? |
<note tip> | <note tip> | ||
- | Remember to use the ''volatile'' keyword (e.g. volatile uint8_t counter;) for variables that you use in interrupts. Also, try to use standard types from ''stdlib.h'' (e.g. int8_t, uint8_t, uint16_t, uint32_t). | + | Remember to use the ''volatile'' keyword (e.g. volatile uint8_t counter;) for variables that you use in interrupts. Also, try to use standard types from ''stdlib.h''/''stdint.h'' (e.g. int8_t, uint8_t, uint16_t, uint32_t). |
</note> | </note> | ||
- | **5.** (2p/3p) Place and connect the components on the schematic for building a 7-segment display | + | **5.** (2p/3p) Place and connect the components on the schematic for building the 7-segment display |
- | * **Entry Level.** Start by connecting a single 7-segment digit to the microcontroller. Pick a 7-segment common cathode display and place it on the schematic. Connect the anodes to ''PORTA'' [0..6] through a resistor network (single resistors work too) and the cathode to ground. Write a program that counts seconds from 0 to 9 and then starts over. | + | * **Entry Level.** Start by connecting a single 7-segment digit to the microcontroller. Pick a 7-segment common cathode display and place it on the schematic. Connect the anodes to ''PORTA'' 0..6 through a resistor network (single resistors work too) and the cathode to ground. Write a program that counts seconds from 0 to 9 and then starts over. |
- | * **Master of Segments** The 7-segment display has to show numbers from 00 to 99. Pick a 7-segment common cathode display and place two of them on the schematic. Each digit will share the anodes that are connected to ''PORTA'' [0..6] through a resistor network (single resistors work too). Pick a transistor (2n2222A is a good choice for switching) and place two of them to drive the cathodes. Connect the base of each transistor to a microcontroller pin with a resistor of 1k in between. Let's use ''PA7'' and ''PC2''. Write a program that counts seconds from 0 to 99 and then starts over. | + | * **Master of Segments.** The 7-segment display has to show numbers from 00 to 99. Pick a 7-segment common cathode display and place two of them on the schematic. Each digit will share the anodes that are connected to ''PORTA'' 0..6 through a resistor network (single resistors work too). Pick a transistor (//2n2222A// is a good choice for switching) and place two of them to drive the cathodes. Connect the base of each transistor to a microcontroller pin with a resistor of 1k in between. Let's use ''PA7'' and ''PC2''. Write a program that counts seconds from 0 to 99 and then starts over. |
<note tip> | <note tip> | ||
Line 236: | Line 236: | ||
{{ :pm:lab:lab2:schematic_digit_2.png?400 |}} | {{ :pm:lab:lab2:schematic_digit_2.png?400 |}} | ||
- | **6.** (2p/3p) In the main loop, get the digits from the counter (variable) and use them to drive the two 7-segment displays. Power the segments as shown in the truth table and show each of the two digits with a small delay between them. You can actually use the //_delay_ms// function in the main loop and this will not affect your timing (counting is done in interrupts). Note: In simulation, you will have to use at least 100 ms and you won't be able to see them both at the same time. | + | **6.** (2p/3p) In the main loop, get the digits from the counter (variable) and use them to drive the two 7-segment displays. Power the segments as shown in the truth table and show each of the two digits with a small delay between them. You can actually use the //_delay_ms// function in the main loop and this will not affect your timing (counting is done with timer interrupts). Note: In simulation, you will have to use at least 100 ms and you won't be able to see them both at the same time. |
<note tip> | <note tip> | ||
Line 243: | Line 243: | ||
---- | ---- | ||
+ | <hidden> | ||
+ | * Solutie laborator: {{:pm:diverse:carantina-lab0xc1-sol.zip|}} | ||
+ | </hidden> | ||
+ | * Schema: {{:pm:diverse:schematic-0xc1.zip|}} | ||
* Lab archive: {{:pm:diverse:carantina-lab0xc1.zip|}} | * Lab archive: {{:pm:diverse:carantina-lab0xc1.zip|}} |