Sunteți pe pagina 1din 16

UNIVERSIDADE FEDERAL DE

UBERLÂNDIA
FACULDADE DE ENGENHARIA
ELÉTRICA - FEELT

LASEC
FEELT TIMER COM ISR

Última Atualização 01/05/2016


1° Edição 06/2015

Sumário

TIMER ......................................................................................................................... 3 
PROCESSAMENTO DE INTERRRUPÇÃO ................................................................ 7 
CONTEXTO: ............................................................................................................ 8 
COMUNICAÇÃO ENTRE ROTINA PRINCIPAL E A ROTINA DE TRATAMENTO
DE INTERRUPÇÃO: ................................................................................................ 9 
INTERRUPÇÃO NO ATmega328 USANDO A LINGUAGEM DE PROGRAMAÇÃO C
.................................................................................................................................. 13 
BIBLIOGRAFIA ......................................................................................................... 16 

Disciplina de Sistemas Embarcados ‐ Faculdade de Engenharia Elétrica – UFU Página 2


1° Edição 06/2015

TIMER

O Timer é um contador que é incrementado a cada intervalo de tempo (em alguns


microcontroladores, o intervalo pode ser configurado). Os timers funcionam como um relógio
que pode ser usado para contar o tempo, medir a duração de certos eventos, dentre outras
aplicações.

O Arduíno UNO vem equipado com um microcontrolador ATmega168 ou ATmega328 (que


diferem apenas na quantidade de memória interna). Esses microcontroladores possuem os
seguintes timers: timer0, timer1 e timer2.

O timer0 e o timer2 são contadores de 8bits, ou seja, contam de 0 a 255, e o timer1 é um


contador de 16bits, conta de 0 a 65535.

Cada timer é composto por um contador que é incrementado a cada ciclo de clock. O clock do
Arduíno UNO opera a 16MHz, esta é a velocidade mais rápida que o contador do timer pode
ser incrementado. A 16MHz cada ciclo de clock representa 1/16000000 de segundos (≈63ns),
deste modo, levará 10/16000000 para o contador sair de 0 e atingir o valor 9 e levará
100/16000000 para o contador sair de 0 e atingir o valor 99.

Em muitas situações, incrementar o timer em uma frequência de 16MHz é muito rápido, pois o
timer0 e o timer2 podem armazenar um valor máximo de 255. Quando um contador atinge o
máximo, ele retornará para o valor zero no próximo ciclo de clock (esta situação é denominada
de overflow). Isto significa que a 16MHz o timer0 e o timer2 levará 256/16000000 segundos
(≈16us) para atingir o valor máximo e o timer1 demora 65536/16000000 (~4 ms) segundos para
atingir o valor máximo, pois é um contador de 16 bit. Assim, se o timer precisar ser utilizado
para gerar uma interrupção a cada segundo, mesmo o valor máximo do timer1 de 16 bit não
será suficiente.

No modo Clear Timer on Compare or CTC mode Configuration, a interrupção do timer é


ativada quando o contador atinge o valor armazenado no registrador de comparação (compare
match register). Uma vez que o contador do timer atinge o valor, ele irá retornar ao valor zero
no próximo ciclo de clock e logo em seguida irá continuar contando até atingir o valor do
registrador de comparação novamente.

Para configurar a frequência em que o timer irá gerar a interrupção é necessário encontrar o
valor do registrador de comparação e configurar a frequência em que o timer irá incrementar.

A velocidade com que o timer é incrementado pode ser controlada por meio da seguinte
equação:

(timer speed (Hz)) = (Arduino clock speed (16MHz)) / prescaler

Disciplina de Sistemas Embarcados ‐ Faculdade de Engenharia Elétrica – UFU Página 3


1° Edição 06/2015

Assim sendo, um prescaler igual a 1 irá incrementar o contador a uma frequência de 16MHz,
um prescaler igual a 8 irá incrementá-lo a 2MHz e um igual a 64 irá incrementar o contador a
250kHz. Como indicado na tabela abaixo, o prescaler do ATMEGA 328p, pode assumir os
valores 1, 8, 64, 256 e 1024, dependendo do valor dos bits CS12, CS11 e CS10.

Isto posto, a frequência de interrupção pode ser calculada com a seguinte equação:

interrupt frequency (Hz) = (Arduino clock speed 16000000Hz) / (prescaler * (compare match
register + 1))

Rearranjando a equação acima é possível achar o valor do compare match register que irá gerar
a frequência desejada:

compare match register = [ 16000000Hz/ (prescaler * desired interrupt frequency) ] - 1

Deste modo, para se obter uma interrupção a cada segundo (frequência de 1Hz):

compare match register = [16000000 / (prescaler * 1) ] -1

Com um prescaler de 1024, o valor do compare match register é:

compare match register = [16000000 / (1024 * 1) ] -1

compare match register = 15624

Disciplina de Sistemas Embarcados ‐ Faculdade de Engenharia Elétrica – UFU Página 4


1° Edição 06/2015

Considerando que 256 < 15624 < 65535, deve-se usar o timer1 para que esta interrupção seja
implementada.

Para que o timer1 seja inicializado e para que possa operar no modo Limpar o Timer na
Comparação (Clear Timer on Compare or CTC mode Configuration), o código de inicialização
do timer1 a seguir pode ser utilizado.

Link para download do programa (Timer1-ISR.zip):

https://mega.nz/#F!BwsC1ZqT!aw7S24tYwCI0ziwHMgymGg

Timer1 no modo CTC (Clear Timer on Compare)

void InitializeTimer1(void )
{
// ICIE1: Timer/Counter1, Input Capture Interrupt Enable
// OCIE1B: Timer/Counter1, Output Compare B Match Interrupt Enable
// OCIE1A: Timer/Counter1, Output Compare A Match Interrupt Enable
// TOIE1: Timer/Counter1, Overflow Interrupt Enable
TIMSK1 &= ~((1 << ICIE1) | (1 << OCIE1B) | (1 << OCIE1A) | (1 << TOIE1));
//TIMSK1 |= (1 << TOIE1);
TIMSK1 |= (1 << OCIE1A);

//--- Clear Timer on Compare or CTC mode Configuration ------------------


// In CTC mode the counter is cleared to zero when the counter value (TCNT1)
// matches either the OCR1A (WGM13:0 = 4) - (Item 16.9.2 - Pag. 122)
TCCR1B |= (1<<WGM12);

//--- Prescaler Configuration ------------------------------------


TCCR1B |= (1 << CS12) | (1 << CS10); // clk/1024 (From prescaler)
//TCCR1B |= (1 << CS12); // clk/256 (From prescaler)
//TCCR1B |= (1 << CS11) | (1 << CS10); // clk/64 (From prescaler)
//TCCR1B |= (1 << CS11); // clk/8 (From prescaler)
//TCCR1B |= (1 << CS10); // clkI/1 (No prescaling)
}

Disciplina de Sistemas Embarcados ‐ Faculdade de Engenharia Elétrica – UFU Página 5


1° Edição 06/2015

Para maiores informações sofre o Timer/Counter1 de 16 bit consultar a Pag. 111 do manual
Atmel-8-bit-AVR-Microcontroller-ATmega328, disponibilizado em:

https://mega.nz/#F!wwkmTZaT!WEgW1kLqINnnhwwxsiKgEw

Disciplina de Sistemas Embarcados ‐ Faculdade de Engenharia Elétrica – UFU Página 6


1° Edição 06/2015

PROCESSAMENTO DE INTERRRUPÇÃO

Quando um programa precisa de uma amostra de sinal ele pode obtê-la por meio de polling.
Este conceito é mostrado na figura a seguir:1

Visão conceitual de polling feito por software

Cada bloco na figura indica uma instrução a ser executada e as linhas mostram o caminho que
o Contador de Programa (PC) irá seguir. Este exemplo mostra a execução do Loop Principal
(Main Loop) que realiza duas chamadas de funções diferentes. Uma das chamadas é para a
Função A (Function A) e a outra é para a Função B (Function B).

O processo de polling por software consome processamento da CPU e se o programa for escrito
de tal modo que realize polling a partir de várias fontes. Ex: canais de A/D, teclado, botões,
buffer de dados, dentre outras entradas, a CPU ficará tão ocupada executando o polling por
software que não restará recurso computacional para realizar outras tarefas.

Para não sobrecarregar a CPU através do polling por software, o processo de polling pode ser
realizado por meio de interrupção. Assim, ao invés do polling ser realizado de modo
determinístico, ele pode ocorrer a qualquer momento. Quando uma interrupção ocorre, a CPU
para o que estiver fazendo e pula para a Rotina de Tratamento de Interrupção (RTI). A RTI é
uma função especial escrita para lidar com o evento que necessita ser tratado de modo urgente
pela CPU. Deste modo, a vantagem da interrupção é que a CPU não precisará ficar verificando
constantemente se um evento ocorreu, ela irá atender o evento somente quando ele ocorrer, isto
é, quando a interrupção acontecer. Interrupções de hardware foram introduzidas como forma
de evitar o desperdício de tempo valioso do processador em polling por software, a espera de
eventos externos.

1 As informações apresentadas neste item (Processamento de Interrupção), com exceção do programa, foram retiradas do livro Introduction to
Embedded Systems: Using ANSI C and the Arduino Development Environment, do autor David Russell.

Disciplina de Sistemas Embarcados ‐ Faculdade de Engenharia Elétrica – UFU Página 7


1° Edição 06/2015

(a) Programa com a Rotina de Tratamento de Interrupção - RTI (Interrupt Service Routine
-ISR) e (b) Chamando a RTI a partir de qualquer ponto do programa

CONTEXTO:
O contexto consiste no conteúdo dos registradores gerais da CPU, além dos registradores de
uso específico, como o program counter (PC), o stack pointer (SP) e o registrador de status.

Disciplina de Sistemas Embarcados ‐ Faculdade de Engenharia Elétrica – UFU Página 8


1° Edição 06/2015

Para que a Rotina de Tratamento de Interrupção não interfira na execução das instruções que
estavam sendo executadas antes da interrupção ocorrer, o conteúdo dos registradores de uso
geral, program counter, stack pointer e status register devem ser salvos no início da Rotina de
Tratamento de Interrupção e restabelecidos ao final de sua execução. Este procedimento evita
que as instruções que estavam utilizando os registradores de uso geral, antes da interrupção
ocorrer, possam finalizar as operações que estavam realizando após o retorno da interrupção.

A figura a seguir mostra uma interrupção que ocorre na terceira instrução da rotina principal
(Main Loop). Como resultado, o endereço da quarta instrução (fornecido pelo program
counter), bem como os registradores de uso geral que serão usados pela Função A, serão
armazenados como parte do contexto. Quando a Função A terminar, o contexto e o program
counter serão restaurados. Assim, a CPU prossegue executando a quarta instrução da rotina
principal com os valores dos registradores de uso geral correspondentes ao término da terceira
instrução, como se uma interrupção não houvesse ocorrido.

Visão conceitual de um software que possui uma Rotina de Tratamento de Interrupção. A interrupção
ocorre na terceira instrução do algoritmo apresentado em (a).

COMUNICAÇÃO ENTRE ROTINA PRINCIPAL E A ROTINA DE TRATAMENTO


DE INTERRUPÇÃO:
A Rotina de Tratamento de Interrupção não pode ser chamada diretamente, por isso, não há
como passar parâmetros ou receber um de retorno. Deste modo, a única maneira da RTI
comunicar com o resto do programa é usando memória compartilhada como variáveis globais,
por exemplo. Entretanto, como a interrupção pode ocorrer a qualquer momento, deve-se tomar
o cuidado na leitura ou escrita de variáveis que podem ser acessadas pela RTI.

Como pode ser observado na figura a seguir, a razão disso ocorrer é porque as instruções C são
compostas por diversas instruções de máquina, especialmente quando se lida com variáveis de
16, 32, 64 bit ou maiores. Isto ocorre porque a arquitetura do ATmega328, por exemplo é de 8

Disciplina de Sistemas Embarcados ‐ Faculdade de Engenharia Elétrica – UFU Página 9


1° Edição 06/2015

bit e, consequentemente, necessita executar várias instruções para realizar uma operação com
variáveis de 16, 32, 64 bit ou maiores.

Pelo fato da interrupção poder interromper a execução de um determinado trecho do programa


a qualquer momento, a Rotina de Tratamento de Interrupção pode mudar o valor das variáveis
globais que possui acesso.

Por exemplo, vamos supor que uma variável de 32bit está sendo copiada pela CPU do
ATmega328. Considerando que este microcontrolador possui uma arquitetura de 8 bit e que
necessitará executar várias instruções para realizar a cópia, se no meio da operação ocorrer uma
interrupção, a RTI poderá mudar o valor da variável que está sendo copiada e quando ocorrer
o retorno da interrupção, isto é, quando a CPU for terminar a cópia interrompida da variável,
seu valor terá mudado e o resultado será de uma cópia com a metade do valor antigo e metade
do valor novo. O que não corresponde nem com a realidade antiga e nem com a nova.

Para resolver esse problema, a memória que é compartilhada entre a RTI e qualquer outra parte
do programa, precisa ser protegida. Esta proteção é realizada desabilitando-se a interrupção
global antes de ler a variável global e habilitando-a novamente depois que a variável for lida.

O programa a seguir mostra como realizar uma operação de leitura/escrita na variável global
timerCountISR, de modo a evitar que a RTI altere o valor dela durante a operação. Neste
exemplo, a interrupção ocorre quando o contador do timer1 atinge a condição de overflow, isto
é, passa do valor máximo 65535, para o valor inicial 0.

Link para download do programa (SMP-Timer1-ISR.zip):

https://mega.nz/#F!BwsC1ZqT!aw7S24tYwCI0ziwHMgymGg

SMP = Shared Memory Protection ( Proteção de Memória Compartilhada )

timer-isr.c

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

Disciplina de Sistemas Embarcados ‐ Faculdade de Engenharia Elétrica – UFU Página 10


1° Edição 06/2015

#include <avr/interrupt.h>

#define SREG_GLOBAL_INT 7 // Bit 7 – I: Global Interrupt Enable

unsigned long timerCountISR =0; // number of TIMER1 Overflow


bool ledON =false;

ISR(TIMER1_OVF_vect)
{
timerCountISR++;
}

void InitializeTimer (void )


{
// ICIE1: Timer/Counter1, Input Capture Interrupt Enable
// OCIE1B: Timer/Counter1, Output Compare B Match Interrupt Enable
// OCIE1A: Timer/Counter1, Output Compare A Match Interrupt Enable
// TOIE1: Timer/Counter1, Overflow Interrupt Enable
TIMSK1 &= ~((1 << ICIE1) | (1 << OCIE1B) | (1 << OCIE1A) | (1 << TOIE1));
TIMSK1 |= (1 << TOIE1);

//--- Prescaler Configuration ------------------------------------


//TCCR1B |= (1 << CS12) | (1 << CS10); // clk/1024 (From prescaler)
//TCCR1B |= (1 << CS12); // clk/256 (From prescaler)
//TCCR1B |= (1 << CS11) | (1 << CS10); // clk/64 (From prescaler)
TCCR1B |= (1 << CS11); // clk/8 (From prescaler)
//TCCR1B |= (1 << CS10); // clkI/1 (No prescaling)
}

int main(void)
{
unsigned long timerCountCopyISR = 0;
DDRB |= (1 << DDB5); // habilita o pino 13 para saída digital (OUTPUT).

InitializeTimer ();

while(1)
{
//----- Memory Protection ---------------------------------------
SREG &= ~(1 << SREG_GLOBAL_INT); //Disable global interrupts.
timerCountCopyISR = timerCountISR; // Copy timerCountISR variable
SREG |= (1 << SREG_GLOBAL_INT); // Restore the global interrupt bit.
//----- End of Memory Protection --------------------------------

if (timerCountCopyISR > 20)


{
if (ledON == true)
{
ledON =false;
PORTB |= (1 <<PORTB5); // Turn LED on
}
else
{
ledON =true;
PORTB &= ~(1 <<PORTB5); // Turn LED off
}

Disciplina de Sistemas Embarcados ‐ Faculdade de Engenharia Elétrica – UFU Página 11


1° Edição 06/2015

timerCountCopyISR = 0;

//----- Memory Protection ---------------------------------------


SREG &= ~(1 << SREG_GLOBAL_INT); //Disable global interrupts.
timerCountISR = 0;
SREG |= (1 << SREG_GLOBAL_INT); // Restore the global interrupt bit.
//----- End of Memory Protection --------------------------------
}
}
}

Disciplina de Sistemas Embarcados ‐ Faculdade de Engenharia Elétrica – UFU Página 12


1° Edição 06/2015

INTERRUPÇÃO NO ATmega328 USANDO A


LINGUAGEM DE PROGRAMAÇÃO C

Muito cuidado deve ser tomado quando se escreve um programa em assembly ou linguagem C
que possui uma Rotina de Tratamento de Interrupção devido a mudança de contexto. Entretanto,
a maioria dos compiladores fornecem macros especiais ou funções que permitem ao compilador
gerenciar o contexto.

No caso do compilador AVR-GCC usado no ATmega328P, existe um conjunto de nomes


predefinidos de funções que correspondem a cada sinal de interrupção que pode ocorrer no
processador.

Quando um algoritmo precisa manusear uma interrupção, basta escrever uma função com o
nome apropriado da função. Cada nome corresponde a um endereço na memória a partir da qual
a RTI será alocada. Os endereços dessas rotinas são chamados vetores de interrupção.

A lista completa de interrupções com seus respectivos nomes de função, são apresentados na
Tabela a seguir:
NÚMERO ORIGEM DA NOME DA FUNÇÃO
ENDREÇO DESCRIÇÃO
DO VETOR INTERRUPÇÃO ISR C
1 0x0000 RESET System reset (power-on)
2 0x0002 INT0 INT0_vect External Interrupt Request 0
3 0x0004 INT1 INT1_vect External Interrupt Request 1
4 0x0006 PCINT0 PCINT0_vect Pin Change Interrupt Request 0
5 0x0008 PCINT1 PCINT1_vect Pin Change Interrupt Request 1
6 0x000A PCINT2 PCINT2_vect Pin Change Interrupt Request 2
7 0x000C WDT WDT_vect Watchdog Time-out Interrupt
8 0x000E TIMER2 COMPA TIMER2_COMPA_vect Timer/Counter2 Compare Match A
9 0x0010 TIMER2 COMPB TIMER2_COMPB_vect Timer/Counter2 Compare Match B
10 0x0012 TIMER2 OVF TIMER2_OVF_vect Timer/Counter2 Overflow
11 0x0014 TIMER1 CAPT TIMER1_CAPT_vect Timer/Counter1 Capture Event
12 0x0016 TIMER1 COMPA TIMER1_COMPA_vect Timer/Counter1 Compare Match A
13 0x0018 TIMER1 COMPB TIMER1_COMPB_vect Timer/Counter1 Compare Match B
14 0x001A TIMER1 OVF TIMER1_OVF_vect Timer/Counter1 Overflow
15 0x001C TIMER0 COMPA TIMER0_COMPA_vect Timer/Counter0 Compare Match A
16 0x001E TIMER0 COMPB TIMER0_COMPB_vect Timer/Counter0 Compare Match B
17 0x0020 TIMER0 OVF TIMER0_OVF_vect Timer/Counter0 Overflow
18 0x0022 SPI, STC SPI_STC_vect SPI Serial Transfer Complete
19 0x0024 USART, RX USART_RX_vect USART Receive Complete
20 0x0026 USART, UDRE USART_UDRE_vect USART Data Register Empty
21 0x0028 USART,TX USART_TX_vect USART Transmit Complete
22 0x002A ADC ADC ADC_vect ADC Conversion Complete
23 0x002C EE READY EE_READY_vect EEPROM Ready
24 0x002E ANALOG COMP ANALOG_COMP_vect Analog Comparator
25 0x0030 TWI TWI_vect 2-wire Serial Interface
26 0x0032 SPM READY SPM_READY_vect Store Program Memory Ready

Disciplina de Sistemas Embarcados ‐ Faculdade de Engenharia Elétrica – UFU Página 13


1° Edição 06/2015

Além do nome específico da função, existe um conjunto definido de macros no arquivo


cabeçalho avr/interrupt.h que são usados para dizer ao compilador como gerenciar certas
funções.

ISR() é o macro usado para registrar e marcar uma função como sendo um manipulador de
interrupção. Este macro foi definido para usar o nome da função como sendo o primeiro
parâmetro seguido dos seguintes atributos opcionais SR_BLOCK, ISR_NOBLOCK,
ISR_NAKED e ISR_ALIASOF(vect).

Cada atributo proporciona os seguintes recursos:

ISR_BLOCK - (default behavior) corresponde a uma Rotina de Tratamento de Interrupção - ISR sem um
atributo especificado. A interrupção global (global interrupts) do ATmega328P é desabilitada no início da
RTI sem que o compilador modifique este estado, isto é, a interrupção global não é reestabelecida
automaticamente, esta operação deve ser realizada pelo programador.

ISR_NOBLOCK - A interrupção global (global interrupts) são reativadas pelo código gerado pelo
compilador no início da RTI, permitindo que outras interrupções de maior prioridade possam ocorrer dentro
da RTI. Isto significa que a RTI pode ser interrompida por interrupções de maior prioridade.

ISR_NAKED - O compilador não gera código para realizar o gerenciamento de contexto. A RTI necessita
salvar, restaurar e adicionar a instrução reti ao final da RTI para retornar o program counter para sua posição
anterior.

ISR_ALIASOF(vect) - A RTI é ligada a uma RTI especificada por um parâmetro de vetor propiciando a
uma RTI manusear múltiplos sinais de interrupção.

Neste exemplo, o timer1 está operando no modo Clear Timer on Compare or CTC mode
Configuration. Deste modo, a interrupção do timer é ativada quando o contador atinge o valor
armazenado no registrador de comparação (compare match register). Neste caso a interrupção
ocorre quando o valor do contador do timer1 é igual ao valor do registrador de comparação
(OCR1A).

timer-isr.c

#include <stdint.h>
#include <avr/io.h>
#include <util/delay.h>
#include <stdbool.h>
#include <avr/interrupt.h>

#define SREG_GLOBAL_INT_ENABLE 7

bool ledON =false;

ISR(TIMER1_COMPA_vect)
{
if (ledON == true)
{
ledON =false;

Disciplina de Sistemas Embarcados ‐ Faculdade de Engenharia Elétrica – UFU Página 14


1° Edição 06/2015

PORTB |= (1 <<PORTB5); // Turn LED on


}
else
{
ledON =true;
PORTB &= ~(1 <<PORTB5); // Turn LED off
}
}

void InitializeTimer1(void )
{
// ICIE1: Timer/Counter1, Input Capture Interrupt Enable
// OCIE1B: Timer/Counter1, Output Compare B Match Interrupt Enable
// OCIE1A: Timer/Counter1, Output Compare A Match Interrupt Enable
// TOIE1: Timer/Counter1, Overflow Interrupt Enable
TIMSK1 &= ~((1 << ICIE1) | (1 << OCIE1B) | (1 << OCIE1A) | (1 << TOIE1));
//TIMSK1 |= (1 << TOIE1);
TIMSK1 |= (1 << OCIE1A);

//--- Clear Timer on Compare or CTC mode Configuration ------------------


// In CTC mode the counter is cleared to zero when the counter value (TCNT1)
// matches either the OCR1A (WGM13:0 = 4) - (Item 16.9.2 - Pag. 122)
TCCR1B |= (1<<WGM12);

//--- Prescaler Configuration ------------------------------------


TCCR1B |= (1 << CS12) | (1 << CS10); // clk/1024 (From prescaler)
//TCCR1B |= (1 << CS12); // clk/256 (From prescaler)
//TCCR1B |= (1 << CS11) | (1 << CS10); // clk/64 (From prescaler)
//TCCR1B |= (1 << CS11); // clk/8 (From prescaler)
//TCCR1B |= (1 << CS10); // clkI/1 (No prescaling)
}

int main(void)
{

DDRB |= (1 << DDB5); // habilita o pino 13 para saída digital (OUTPUT).

InitializeTimer1();

// compare match register = [16000000Hz/ (prescaler * desired interrupt frequency) ] - 1


// compare match register = 16000000/(1024 * 1Hz)] -1
// compare match register = 15624
OCR1A = 15624; // 1Hz com clk/1024 (From prescaler)

while(1)
{
// Restore the global interrupt bit to previous value.
SREG |= (1 << SREG_GLOBAL_INT_ENABLE);
}
}

Disciplina de Sistemas Embarcados ‐ Faculdade de Engenharia Elétrica – UFU Página 15


1° Edição 06/2015

BIBLIOGRAFIA

1. RUSSELL, D. Introduction to Embedded Systems: Using ANSI C and the Arduino Development
Environment, Morgan & Claypool, 2010.

Universidade Federal de Uberlândia


Faculdade de Engenharia Elétrica
Disciplina de Sistemas Embarcados

www.omegaflix.com

Prof. Fábio V. R. da Silva

Disciplina de Sistemas Embarcados ‐ Faculdade de Engenharia Elétrica – UFU Página 16

S-ar putea să vă placă și