1) Cel lekcji
Wyświetlacz 7-segmentowy to zestaw diod LED w kształcie „kresek” (a–g) + kropka (dp). Sterujesz tym jak zwykłymi LED-ami, ale zamiast jednej diody masz ich 8.
- poznasz nazwy segmentów i zasady świecenia,
- zrobisz mapowanie pinów MCU → segmenty,
- zbudujesz tablicę znaków (0–9 + kilka liter),
- uruchomisz 1 cyfrę na Twoim schemacie.
2) Typ wyświetlacza i logika sterowania
Na Twoim schemacie jest wyświetlacz 4-cyfrowy ze wspólną anodą (opis przy U3) oraz sterowanie wspólnymi anodami przez tranzystory PNP BC557 (Q1–Q4). :contentReference[oaicite:0]{index=0}
- anoda cyfry jest podciągana do +5V (u Ciebie przez PNP),
- segment świeci, gdy jego katoda jest w stanie LOW,
- czyli: segment ON = 0, segment OFF = 1.
- używamy tylko jednej cyfry (np. DP1 / CA1),
- pozostałe cyfry są wyłączone.
To jest logika „odwrócona”. Jeśli skopiujesz tablice z Internetu dla wspólnej katody, będą świecić odwrotnie.
3) Podłączenie na Twoim schemacie (mapowanie pinów)
Zgodnie ze schematem (strona 1), segmenty są podłączone do PORTD (PD0–PD7) przez przełącznik DIP (SW6) i rezystory (R12–R19), a wspólne anody cyfr (CA1–CA4) są przełączane tranzystorami PNP sterowanymi z PORTC (PC0–PC3) przez DIP (SW7) i rezystory bazowe. :contentReference[oaicite:1]{index=1}
Mapowanie: PORTD → segmenty
| Pin ATmega328P | Segment | Uwagi (wspólna anoda) |
|---|---|---|
| PD0 | a | ON = 0 |
| PD1 | b | ON = 0 |
| PD2 | c | ON = 0 |
| PD3 | d | ON = 0 |
| PD4 | e | ON = 0 |
| PD5 | f | ON = 0 |
| PD6 | g | ON = 0 |
| PD7 | dp | kropka, ON = 0 |
Wybór cyfry: PORTC → CA1..CA4 (przez PNP)
| Pin ATmega328P | Cyfra | Stan włączający |
|---|---|---|
| PC0 | CA1 (DP1 / 1. pozycja) | LOW (włącza PNP) |
| PC1 | CA2 | LOW |
| PC2 | CA3 | LOW |
| PC3 | CA4 | LOW |
4) „Mapa segmentów” i format tablicy znaków
Przyjmujemy, że bity w bajcie odpowiadają segmentom w takiej kolejności (zgodnie z PORTD):
- bit0=a (PD0)
- bit1=b (PD1)
- bit2=c (PD2)
- bit3=d (PD3)
- bit4=e (PD4)
- bit5=f (PD5)
- bit6=g (PD6)
- bit7=dp (PD7)
segment ON = 0, więc wygodnie jest trzymać tablicę w logice „1 = segment świeci” i na końcu robić negację (~) przed wysłaniem na PORTD.
5) Kod: inicjalizacja wyświetlacza (1 cyfra)
Ustawiamy PORTD jako wyjście (segmenty) oraz PC0..PC3 jako wyjścia (wybór cyfry).
#include <avr/io.h>
#include <util/delay.h>
// Segmenty: PD0..PD7 (a,b,c,d,e,f,g,dp)
#define SEG_DDR DDRD
#define SEG_PORT PORTD
// Wybór cyfry (anody przez PNP): PC0..PC3
#define DIG_DDR DDRC
#define DIG_PORT PORTC
#define DIG1 PC0
#define DIG2 PC1
#define DIG3 PC2
#define DIG4 PC3
static void seg7_init_1digit(void)
{
// segmenty jako wyjścia
SEG_DDR = 0xFF;
// na wspólnej anodzie: stan wysoki = segment OFF
SEG_PORT = 0xFF;
// wybór cyfr jako wyjścia
DIG_DDR |= (1<<DIG1) | (1<<DIG2) | (1<<DIG3) | (1<<DIG4);
// wyłącz wszystkie cyfry: PNP wyłączony gdy baza "wysoko"
DIG_PORT |= (1<<DIG1) | (1<<DIG2) | (1<<DIG3) | (1<<DIG4);
// włącz tylko cyfrę 1 (CA1): baza nisko = PNP ON
DIG_PORT &= ~(1<<DIG1);
}
sprawdź, czy na schemacie masz ustawione DIPy SW6/SW7 tak, aby połączenia były aktywne (segmenty i wybór cyfry). :contentReference[oaicite:2]{index=2}
6) Tablica cyfr 0–9 (logika „1 = segment świeci”)
Definiujemy maski segmentów dla cyfr w klasycznej notacji a–g:
// bity: 0=a,1=b,2=c,3=d,4=e,5=f,6=g,7=dp
// tutaj: 1 oznacza "segment ma świecić"
static const uint8_t SEG_DIGIT[10] = {
// 0: a b c d e f
0b00111111,
// 1: b c
0b00000110,
// 2: a b d e g
0b01011011,
// 3: a b c d g
0b01001111,
// 4: b c f g
0b01100110,
// 5: a c d f g
0b01101101,
// 6: a c d e f g
0b01111101,
// 7: a b c
0b00000111,
// 8: a b c d e f g
0b01111111,
// 9: a b c d f g
0b01101111
};
7) Funkcja wyświetlania cyfry (wspólna anoda = negacja)
static inline void seg7_show_raw(uint8_t seg_mask_on)
{
// wspólna anoda: ON = 0, OFF = 1
SEG_PORT = (uint8_t)~seg_mask_on;
}
static void seg7_show_digit(uint8_t digit, uint8_t dp_on)
{
if (digit > 9) digit = 0;
uint8_t m = SEG_DIGIT[digit];
if (dp_on) m |= (1<<7); // dp w tej konwencji: 1 = świeci
seg7_show_raw(m);
}
8) Test 1: „chodzący segment” (a→b→…→dp)
To najlepszy test poprawności mapowania PD0..PD7. Jeśli świeci inny segment niż powinien, masz zamienione przewody/DIP.
static void test_walk_segments(void)
{
for (uint8_t i = 0; i < 8; i++)
{
seg7_show_raw((1<<i)); // świeci tylko jeden segment
_delay_ms(400);
}
seg7_show_raw(0);
}
9) Test 2: licznik 0–9
static void test_count_0_9(void)
{
for (uint8_t d = 0; d <= 9; d++)
{
seg7_show_digit(d, 0);
_delay_ms(600);
}
}
10) Pełny program lekcji (gotowy do wgrania)
#include <avr/io.h>
#include <util/delay.h>
#define SEG_DDR DDRD
#define SEG_PORT PORTD
#define DIG_DDR DDRC
#define DIG_PORT PORTC
#define DIG1 PC0
#define DIG2 PC1
#define DIG3 PC2
#define DIG4 PC3
static const uint8_t SEG_DIGIT[10] = {
0b00111111, // 0
0b00000110, // 1
0b01011011, // 2
0b01001111, // 3
0b01100110, // 4
0b01101101, // 5
0b01111101, // 6
0b00000111, // 7
0b01111111, // 8
0b01101111 // 9
};
static void seg7_init_1digit(void)
{
SEG_DDR = 0xFF;
SEG_PORT = 0xFF;
DIG_DDR |= (1<<DIG1) | (1<<DIG2) | (1<<DIG3) | (1<<DIG4);
DIG_PORT |= (1<<DIG1) | (1<<DIG2) | (1<<DIG3) | (1<<DIG4);
// tylko pierwsza cyfra aktywna
DIG_PORT &= ~(1<<DIG1);
}
static inline void seg7_show_raw(uint8_t seg_mask_on)
{
SEG_PORT = (uint8_t)~seg_mask_on; // wspólna anoda
}
static void seg7_show_digit(uint8_t digit, uint8_t dp_on)
{
if (digit > 9) digit = 0;
uint8_t m = SEG_DIGIT[digit];
if (dp_on) m |= (1<<7);
seg7_show_raw(m);
}
static void test_walk_segments(void)
{
for (uint8_t i = 0; i < 8; i++)
{
seg7_show_raw((1<<i));
_delay_ms(350);
}
seg7_show_raw(0);
}
static void test_count_0_9(void)
{
for (uint8_t d = 0; d <= 9; d++)
{
seg7_show_digit(d, 0);
_delay_ms(600);
}
}
int main(void)
{
seg7_init_1digit();
while (1)
{
test_walk_segments();
_delay_ms(400);
test_count_0_9();
_delay_ms(800);
// przykład: 3. z kropką
seg7_show_digit(3, 1);
_delay_ms(900);
}
}
- segmenty PD0..PD7 podłączone i aktywne przez SW6
- wybór cyfry (PC0) aktywny przez SW7
- wspólna anoda: segment świeci przy stanie LOW
11) Zadania obowiązkowe
- Zrób funkcję seg7_show_hex(n) dla 0–15 (0..9 + A,b,C,d,E,F).
- Zrób test: licz od 9 do 0 (w dół) z kropką zapalaną co drugi krok.
- Zrób „animację”: wyświetlaj kolejno segmenty tworząc kształt koła (a→b→c→d→e→f→a…).
12) Zadania dodatkowe (dla lepszych)
- Zrób funkcję seg7_blank() (wyłącz wszystkie segmenty) i użyj jej między wyświetleniami (np. 20 ms).
- Zrób 5 znaków statusu: H, L, -, _, P (własne maski segmentów).
- Połącz z przyciskiem: klik zmienia aktualną cyfrę 0..9.
13) Zadania PRO (przygotowanie pod lekcję o multipleksie)
- Zamiast _delay_ms użyj tick 1 ms z lekcji 13 do odmierzania czasu testów.
- Zrób funkcję seg7_enable_digit(n), która steruje PC0..PC3 (na razie włączaj tylko jedną na raz).
- Wprowadź bufor: uint8_t buf[4] z maskami segmentów (w kolejnej lekcji to wykorzystasz do 4 cyfr).
Kolejna lekcja to multipleks 4 cyfr: szybkie przełączanie CA1..CA4 i odświeżanie bufora.