Segmentový display TM1637: Rozdiel medzi revíziami
Zo stránky SensorWiki
Bez shrnutí editace |
Bez shrnutí editace |
||
(13 medziľahlých úprav od rovnakého používateľa nie je zobrazených.) | |||
Riadok 22: | Riadok 22: | ||
== Analýza a opis riešenia == | == Analýza a opis riešenia == | ||
[[Súbor:Zapojenie_display.png|600px|thumb|center|Vizualizácia zapojenia.Tlačidlo Start/Stop v lavo, tlačidlo Reset v pravo .]] | |||
'''TM1637''' | |||
TM1637 je obvod ovládajúci 7-segmentový displej. Tento obvod umožňuje jednoduchú komunikáciu s displejom a riadenie zobrazených číslic a segmentov. | |||
Komunikácia s TM1637 prebieha pomocou jednoduchého sériového rozhrania, ktoré využíva dva piny - CLK (Clock) a DIO (Data Input/Output). CLK riadi synchronizáciu dátových prenosov a DIO slúži na prenos dát. | |||
Displej je rozdelený podľa programu tak, aby na ľavej strane od dvojbody zobrazoval sekundy a na pravej strane milisekundy. | |||
Maximálna hodnota ktorú môžeme vidieť na displeji je 99:99. Stopky na tajto hodnote nezastavia ale pokračujú ako keby išli od začiatku (nastane pretečenie). | |||
'''Princím funkcie''' | |||
Pri spustení programu sa na displeji objavia 4 nuly a dvojbodka v strede. Po zatlačení tlačidla Start/Stop sa stopky spustia.Na displeji beží čas až pokým sa opäť nestlačí tlačidlo Start/Stop.Vtedy sa stopky zastavia a na displeji svieti čas, v ktorom boli stopky zastavené. Po opätovnom stlačení tlačidla čas začne bežať od mista kde bol zastavený. | |||
Tlačidlo Reset slúži na nastavenie začiatku (stav 00:00 na displeji),nezaisťuje však to, aby sa stopky zastavili. | |||
[[Súbor:Schema_zapojenia123.png|500px|thumb|center|Schéma zapojenia TM1637 displeja a tlačidiel pre ovládanie Stopiek.]] | [[Súbor:Schema_zapojenia123.png|500px|thumb|center|Schéma zapojenia TM1637 displeja a tlačidiel pre ovládanie Stopiek.]] | ||
Riadok 34: | Riadok 42: | ||
=== Algoritmus a program === | === Algoritmus a program === | ||
V prvom kroku sme nastavili frekvenciu CPU, následne sme načítali knižnice. Následne bolo potrebné zadefinovať konkrétne segmenty pre konkrétne čísla pomocou pola digitToSegment. V ďalšom kroku bolo potrebné inicializovať displej a vymazať ho takým spôsobom, že sme nastavili piny CLK a DIO ako vstupy a zapli pre piny pull-up rezistory. Následne vytvoríme funkciu "startCom", ktorá nastaví komunikáciu pre displej na začiatok tak, že nastaví pin DIO ako výstupný a vynuluje ho. Ďalšia funkcia "stopCom", ukončí komunikáciu pre displej nastavením pinu DIO ako výstupný, jeho vynulovaním a nastavením pinu CLK ako vstupný pin s pull-up rezistorom. Pauza je možná pomocou _delay_us(). Nami vytvorená funkcia „displayWriteByte“ zapisuje bajt na dislplej nasledovne: inicializuje premenné, vykoná cyklus pre 8 bitov dát v ktorom sa vykonajú nasledovné kroky: | |||
1. Nastavenie pinu CLK ako výstupný. | |||
2. Vynulovanie pinu CLK. | |||
3. Nastavenie alebo vynulovanie pinu DIO podľa hodnoty bitu. | |||
4. Prerušenie pomocou _delay_us(). | |||
5. Nastavenie pinu CLK ako vstupný s pull-up rezistorom. | |||
6. Prerušenie_delay_us(). | |||
7. Posunutie bitovej hodnoty dát na ďalší bit. | |||
Potom sa už iba overí či boli dáta prijaté správne, nastaví sa pin CLK ako výstupný, vynuluje sa pin CLK. Následne ideme nastaviť bodky na displeji, vytvoríme funkciu "displayShowDots", ktorá ich nastaví. Bodky sa pridajú do jednotlivých číslic podľa hodnoty premennej "dots". V ďalšom kroku vytvoríme funkciu "displayEncodeDigit", ktorá zakóduje číslicu pre displej podľa "digitToSegment". Následne vytvoríme ďalšiu funkciu "displaySetSegments", ktorá nastaví segmenty displeja. Najprv sa vykoná príkaz COMM1 pre displej, potom príkaz COMM2 s adresou prvej číslice. Potom sa pošlú dátové byty pre jednotlivé segmenty, komunikácia sa ukončí, vykoná sa príkaz COMM3 s nastavením jasu displeja. Funkcia "displayClear" nastaví všetky segmenty na 0. Vytvoríme funkciu "displayShowNumberBaseEx", ktorá zobrazí číslo v danom základe na displeji. Desatinné číslo na displeji zobrazíme pomocou našej funkcie "displayShowNumberDecEx". Následne sme inicializovali tlačidlá pomocou našej funkcie "initButtons". Program obsahuje aj ošetrenie zákmitov tlačidiel. Definujeme premenné pre počítanie času a stav časovača. Tlačidlo start/stop je definované na pine PD2 a tlačidlo reset je definované na pine PD3. Zobrazíme počiatočný stav na displeji (00:00). Vytvoríme prerušenie pre pretečenie časovača, tým sme nastavili časovač a povolili prerušenia. V nekonečnom cykle sa zobrazuje uplynutý čas na displeji. | |||
Riadok 366: | Riadok 390: | ||
</tabs> | </tabs> | ||
Prikladám aj zabalený zdrojový kód. | |||
Zdrojový kód: [[Médiá:TM1637_stopwatch.zip|TM1637_stopwatch.zip]] | Zdrojový kód: [[Médiá:TM1637_stopwatch.zip|TM1637_stopwatch.zip]] | ||
=== Overenie === | === Overenie === | ||
Na displeji je vypísaný čas medzi spustením a zastavením (čas je 04:28). | |||
[[Súbor:Zapojenie123456.jpg|900px|thumb|center|Stopky.]] | |||
[[Súbor: | |||
'''Video:''' | '''Video:''' | ||
<center><youtube> | <center><youtube>WI9CY0qi2fM</youtube></center> | ||
Kľúčové slová 'Category', ktoré sú na konci stránky nemeňte. | Kľúčové slová 'Category', ktoré sú na konci stránky nemeňte. | ||
[[Category:AVR]] [[Category:MIPS]] | [[Category:AVR]] [[Category:MIPS]] |
Aktuálna revízia z 21:17, 7. jún 2023
Záverečný projekt predmetu MIPS / LS2023 - Juraj Štefánik
Zadanie
Segmentový display TM1637 - napíšte rutiny na zobrazenie čísla a času na tomto displeji. Predvedenie funkcie si vymyslite, napr. stopky a pod.
Predvedenie funkcie je realizované pomocou programu pre stopky,ktoré sú ovládané dvomi tlačidlami. Prvé tlačidlo Slúži pre Štart/Stop a druhé pre Reset.
Literatúra:
Analýza a opis riešenia
TM1637 TM1637 je obvod ovládajúci 7-segmentový displej. Tento obvod umožňuje jednoduchú komunikáciu s displejom a riadenie zobrazených číslic a segmentov.
Komunikácia s TM1637 prebieha pomocou jednoduchého sériového rozhrania, ktoré využíva dva piny - CLK (Clock) a DIO (Data Input/Output). CLK riadi synchronizáciu dátových prenosov a DIO slúži na prenos dát. Displej je rozdelený podľa programu tak, aby na ľavej strane od dvojbody zobrazoval sekundy a na pravej strane milisekundy. Maximálna hodnota ktorú môžeme vidieť na displeji je 99:99. Stopky na tajto hodnote nezastavia ale pokračujú ako keby išli od začiatku (nastane pretečenie).
Princím funkcie Pri spustení programu sa na displeji objavia 4 nuly a dvojbodka v strede. Po zatlačení tlačidla Start/Stop sa stopky spustia.Na displeji beží čas až pokým sa opäť nestlačí tlačidlo Start/Stop.Vtedy sa stopky zastavia a na displeji svieti čas, v ktorom boli stopky zastavené. Po opätovnom stlačení tlačidla čas začne bežať od mista kde bol zastavený. Tlačidlo Reset slúži na nastavenie začiatku (stav 00:00 na displeji),nezaisťuje však to, aby sa stopky zastavili.
Algoritmus a program
V prvom kroku sme nastavili frekvenciu CPU, následne sme načítali knižnice. Následne bolo potrebné zadefinovať konkrétne segmenty pre konkrétne čísla pomocou pola digitToSegment. V ďalšom kroku bolo potrebné inicializovať displej a vymazať ho takým spôsobom, že sme nastavili piny CLK a DIO ako vstupy a zapli pre piny pull-up rezistory. Následne vytvoríme funkciu "startCom", ktorá nastaví komunikáciu pre displej na začiatok tak, že nastaví pin DIO ako výstupný a vynuluje ho. Ďalšia funkcia "stopCom", ukončí komunikáciu pre displej nastavením pinu DIO ako výstupný, jeho vynulovaním a nastavením pinu CLK ako vstupný pin s pull-up rezistorom. Pauza je možná pomocou _delay_us(). Nami vytvorená funkcia „displayWriteByte“ zapisuje bajt na dislplej nasledovne: inicializuje premenné, vykoná cyklus pre 8 bitov dát v ktorom sa vykonajú nasledovné kroky:
1. Nastavenie pinu CLK ako výstupný.
2. Vynulovanie pinu CLK.
3. Nastavenie alebo vynulovanie pinu DIO podľa hodnoty bitu.
4. Prerušenie pomocou _delay_us().
5. Nastavenie pinu CLK ako vstupný s pull-up rezistorom.
6. Prerušenie_delay_us().
7. Posunutie bitovej hodnoty dát na ďalší bit.
Potom sa už iba overí či boli dáta prijaté správne, nastaví sa pin CLK ako výstupný, vynuluje sa pin CLK. Následne ideme nastaviť bodky na displeji, vytvoríme funkciu "displayShowDots", ktorá ich nastaví. Bodky sa pridajú do jednotlivých číslic podľa hodnoty premennej "dots". V ďalšom kroku vytvoríme funkciu "displayEncodeDigit", ktorá zakóduje číslicu pre displej podľa "digitToSegment". Následne vytvoríme ďalšiu funkciu "displaySetSegments", ktorá nastaví segmenty displeja. Najprv sa vykoná príkaz COMM1 pre displej, potom príkaz COMM2 s adresou prvej číslice. Potom sa pošlú dátové byty pre jednotlivé segmenty, komunikácia sa ukončí, vykoná sa príkaz COMM3 s nastavením jasu displeja. Funkcia "displayClear" nastaví všetky segmenty na 0. Vytvoríme funkciu "displayShowNumberBaseEx", ktorá zobrazí číslo v danom základe na displeji. Desatinné číslo na displeji zobrazíme pomocou našej funkcie "displayShowNumberDecEx". Následne sme inicializovali tlačidlá pomocou našej funkcie "initButtons". Program obsahuje aj ošetrenie zákmitov tlačidiel. Definujeme premenné pre počítanie času a stav časovača. Tlačidlo start/stop je definované na pine PD2 a tlačidlo reset je definované na pine PD3. Zobrazíme počiatočný stav na displeji (00:00). Vytvoríme prerušenie pre pretečenie časovača, tým sme nastavili časovač a povolili prerušenia. V nekonečnom cykle sa zobrazuje uplynutý čas na displeji.
#include <avr/io.h>
#define F_CPU 16000000UL //define CPU frequency
//Required libraries
#include <stdio.h>
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#define set_bit(ADDRESS,BIT) (ADDRESS |= (1<<BIT))
#define clear_bit(ADDRESS,BIT) (ADDRESS &= ~(1<<BIT))
#define CLK PD5 // CLK -> pin 5 portD.5
#define DIO PD4 // DIO -> pin 4 portD.4
#define START_STOP_PIN PD2 // START_STOP_PIN -> pin 2 portD.2
#define RESET_PIN PD3 // RESET_PIN -> pin 3 portD.3
#define BIT_DELAY 100 //delay 100ms
//define for TM1637
#define TM1637_I2C_COMM1 0x40
#define TM1637_I2C_COMM2 0xC0
#define TM1637_I2C_COMM3 0x80
//
// A
// ---
// F | | B
// -G-
// E | | C
// ---
// D
const uint8_t digitToSegment[] = {
// XGFEDCBA
0b00111111, // 0
0b00000110, // 1
0b01011011, // 2
0b01001111, // 3
0b01100110, // 4
0b01101101, // 5
0b01111101, // 6
0b00000111, // 7
0b01111111, // 8
0b01101111, // 9
0b01110111, // A
0b01111100, // b
0b00111001, // C
0b01011110, // d
0b01111001, // E
0b01110001 // F
};
static const uint8_t minusSegments = 0b01000000;
uint8_t brightness = (0x7 & 0x7) | 0x08;
void displayInit(){
clear_bit(DDRD,CLK); // set CLK as input
set_bit(PORTD,CLK); // CLK as input pull-up on
clear_bit(DDRD,DIO); // set DIO as input
set_bit(PORTD,DIO); // DIO as input pull-up on
}
void startCom(){
set_bit(DDRD,DIO); // set DIO as output
clear_bit(PORTD,DIO); // set DIO as output
_delay_us(BIT_DELAY);
}
void stopCom(){
set_bit(DDRD,DIO); // set DIO as output
clear_bit(PORTD,DIO); // set DIO as output
_delay_us(BIT_DELAY);
clear_bit(DDRD,CLK); // set CLK as input
set_bit(PORTD,CLK); // CLK as input pull-up on
_delay_us(BIT_DELAY);
clear_bit(DDRD,DIO); // set DIO as input
set_bit(PORTD,DIO); // DIO as input pull-up on
_delay_us(BIT_DELAY);
}
int displayWriteByte(uint8_t byte){
uint8_t data = byte;
// 8 Data Bits
for(uint8_t i = 0; i < 8; i++) {
// CLK low
set_bit(DDRD,CLK);
clear_bit(PORTD,CLK);
_delay_us(BIT_DELAY);
// Set data bit
if (data & 0x01){
clear_bit(DDRD,DIO); // set DIO as input
set_bit(PORTD,DIO); // DIO as input pull-up on
} else{
set_bit(DDRD,DIO); // set DIO as output
clear_bit(PORTD,DIO);
}
_delay_us(BIT_DELAY);
// CLK high
clear_bit(DDRD,CLK); // set CLK as input
set_bit(PORTD,CLK); // CLK as input pull-up on
_delay_us(BIT_DELAY);
data = data >> 1;
}
// Wait for acknowledge
// CLK to zero
set_bit(DDRD,CLK); // set CLK as output
clear_bit(PORTD,CLK);
clear_bit(DDRD,DIO); // set DIO as input
set_bit(PORTD,DIO); // DIO as input pull-up on
_delay_us(BIT_DELAY);
// CLK to high
clear_bit(DDRD,CLK); // set CLK as input
set_bit(PORTD,CLK); // CLK as input pull-up on
_delay_us(BIT_DELAY);
uint8_t ack = !bit_is_clear(PIND, DIO);
if (ack == 0)
set_bit(DDRD,DIO); // set DIO as output
clear_bit(PORTD,DIO);
_delay_us(BIT_DELAY);
set_bit(DDRD,CLK); // set CLK as output
clear_bit(PORTD,CLK);
_delay_us(BIT_DELAY);
return ack;
}
void displayShowDots(uint8_t dots, uint8_t* digits){
for(int i = 0; i < 4; ++i)
{
digits[i] |= (dots & 0x80);
dots <<= 1;
}
}
uint8_t displayEncodeDigit(uint8_t digit){
return digitToSegment[digit & 0x0f];
}
void displaySetSegments(const uint8_t segments[], uint8_t length, uint8_t pos){
// Write COMM1
startCom();
displayWriteByte(TM1637_I2C_COMM1);
stopCom();
// Write COMM2 + first digit address
startCom();
displayWriteByte(TM1637_I2C_COMM2 + (pos & 0x03));
// Write the data bytes
for (uint8_t k=0; k < length; k++)
displayWriteByte(segments[k]);
stopCom();
// Write COMM3 + brightness
startCom();
displayWriteByte(TM1637_I2C_COMM3 + (brightness & 0x0f));
stopCom();
}
void displayClear(){
uint8_t data[] = { 0, 0, 0, 0 };
displaySetSegments(data, 4, 0);
}
// Function to display a number in a given base with custom options
void displayShowNumberBaseEx(int8_t base, uint16_t num, uint8_t dots, const int leading_zero,
uint8_t length, uint8_t pos)
{
int negative = 0; //false
if (base < 0) {
base = -base;
negative = 1; //true
}
uint8_t digits[4];
if (num == 0 && !leading_zero) {
// Singular case - take care separately
for(uint8_t i = 0; i < (length-1); i++)
digits[i] = 0;
digits[length-1] = displayEncodeDigit(0);
}
else {
//uint8_t i = length-1;
//if (negative) {
// // Negative number, show the minus sign
// digits[i] = minusSegments;
// i--;
//}
for(int i = length-1; i >= 0; --i)
{
uint8_t digit = num % base;
if (digit == 0 && num == 0 && leading_zero == 0)
// Leading zero is blank
digits[i] = 0;
else
digits[i] = displayEncodeDigit(digit);
if (digit == 0 && num == 0 && negative) {
digits[i] = minusSegments;
negative = 0;
}
num /= base;
}
}
if(dots != 0)
{
displayShowDots(dots, digits);
}
displaySetSegments(digits, length, pos);
}
// ak dots 0x40 - dvojbodka v strede svieti
// ak dots 0 - dvojbodka v strede nesvieti
// Function to display a decimal number with custom options
void displayShowNumberDecEx(int num, uint8_t dots, const int leading_zero,
uint8_t length, uint8_t pos){
displayShowNumberBaseEx(num < 0? -10 : 10, num < 0? -num : num, dots, leading_zero, length, pos);
}
// Function to display a decimal number with default options
void displayShowNumberDec(int num, const int leading_zero, uint8_t length, uint8_t pos){
displayShowNumberDecEx(num, 0x40, leading_zero, length, pos);
}
// Function to initialize the buttons
void initButtons(){
clear_bit(DDRD, START_STOP_PIN); // set START_STOP_PIN as input
set_bit(PORTD, START_STOP_PIN); // START_STOP_PIN as input pull-up on
clear_bit(DDRD, RESET_PIN); // set RESET_PIN as input
set_bit(PORTD, RESET_PIN); // RESET_PIN as input pull-up on
}
volatile unsigned int elapsedTime = 0;
volatile int running = 0;
// Timer1 overflow interrupt service routine
ISR (TIMER1_OVF_vect)
{
elapsedTime++;
if(elapsedTime > 9999) elapsedTime=0;
TCNT1 = 0xFF63; // initialize (CLEAR) counter
}
// External interrupt 0 (start/stop button)
ISR (INT0_vect)
{
_delay_ms(10);
if(bit_is_clear(PIND,START_STOP_PIN)){
running = !running;
TCCR1B = running ? 0b00000000 : 0b00000101;
}
_delay_ms(10);
}
// External interrupt 1 (reset button)
ISR (INT1_vect)
{
_delay_ms(10);
if(bit_is_clear(PIND,RESET_PIN)){
elapsedTime = 0;
}
_delay_ms(10);
}
int main(void){
displayInit();
displayClear();
initButtons();
displayShowNumberDec(0, 1, 4, 0); // pociatocny stav 00:00
TCNT1 = 0xFF63; // initialize (CLEAR) counter
TCCR1B = 0b00000000; // turn off timer
TIFR1 = (1<<TOV1); // clear Timer 1 Overflow Flag
TIMSK1 = (1<<TOIE1); //overflow input enable
EIMSK = 0b00000011; //enable int0 and int1
EICRA = 0b00001010; //detect falling edge on both pins
sei(); // enable interrupts
set_bit(DDRB,PB5);
while (1) {
displayShowNumberDec(elapsedTime, 1, 4, 0);
}
return 0;
}
Prikladám aj zabalený zdrojový kód.
Zdrojový kód: TM1637_stopwatch.zip
Overenie
Na displeji je vypísaný čas medzi spustením a zastavením (čas je 04:28).
Video:
Kľúčové slová 'Category', ktoré sú na konci stránky nemeňte.