7. Serial

Introdução

O uso de portas de entrada/saída de uso genérico (GPIOs) como as referidas numa outra página permite a troca de bits isolados de informação. Com efeito para se ligar ou desligar um LED basta usar um pino de saída, enquanto que para receber o estado dum botão um pino é igualmente suficiente. Mas o que fazer quando se tem que enviar ou receber dados mais complexos?

De facto, em certos projetos pode ser necessário trocar texto ou valores numéricos com dispositivos eletrónicos ligados à placa Raspberry Pi, como LCDs, sensores diversos, etc. Em geral toda essa informação é decomposta em bytes que são depois enviados/recebidos por um protocolo série.

Série vs Paralelo

Num protocolo série os bits que compõem os bytes de informação não são enviados todos em simultâneo (como no caso dum protocolo paralelo) mas sim um após outro ao longo do tempo. Desta forma são necessários menos pinos do interface GPIO da placa Raspberry Pi, e o número de fios a interligar a placa com o dispositivo é também menor.

NOTE: Na imagem é o bit mais significativo (MSB) o que é enviado primeiro mas como iremos ver, dependendo do protocolo a ordem pode variar!

Protocolos série

Os protocolos série disponíveis na placa Raspberry Pi, e que serão descritos ao longo desta página, são os seguintes:

Nesta página para além de se apresentarem as principais ideias sobre cada um deles, serão também descritas as funções da biblioteca pigpio que possibilitam o uso destes protocolos.

 


UART

O protocolo UART (Universal Asynchronous Receiver/Transmitter) é o mais antigo e como o nome indica é assíncrono. Isto porque não existe qualquer sinal de relógio que marque a cadência a que os bits são enviados/recebidos. Neste caso os dois intervenientes nesta comunicação (a placa e o dispositivo) acordam previamente qual o número de bits por segundo a usar. Este parâmetro é vulgarmente designado de baudrate e pode ser escolhido de entre um conjunto de valores standard: 2400, 4800, 9600, … 115200, etc.

UART

Na ausência de dados (Bus Idle) estas duas linhas apresentam o nível lógico 1. Os bits de dados são antecedidos por um Start Bit igual a 0, e seguidos por um Stop Bit igual a 1. Os bits de dados (em geral 8) são enviados do menos para o mais significativo. Antes do envio do Stop Bit (que pode ter uma duração de 1, 1.5 ou 2 bits) pode ainda ser enviado um bit de paridade: par (Even) ou ímpar (Odd), destinado à deteção de eventuais erros de transmissão.

UART_bits

No caso da placa Raspberry Pi, os pinos da interface necessários à implementação do protocolo UART são os indicados a amarelo, nomeadamente o pino UART_TXD (onde os bits são transmitidos pela placa), e o UART_RXD (onde os bits são recebidos).

RPi-GPIO

Projeto GPS

Neste exemplo vamos ligar a placa Raspberry Pi a um recetor GPS EM-506. Este recetor usa um protocolo série UART com as seguintes características:

  • 4800 bits por segundo de baudrate
  • 8 bits de dados
  • sem bit de paridade
  • 1 stop bit

O seguinte programa (gps.py) acumula durante 3 segundos as mensagens que são enviadas regularmente pelo recetor GPS, imprimindo-as de seguida:

import platform
import pigpio
import time

if platform.uname()[4][0:3] == "arm":
    pi = pigpio.pi()
else:
    pi = pigpio.pi("10.0.0.100")

gps = pi.serial_open("/dev/ttyAMA0", 4800)
time.sleep(3) # wait 3 seconds for gps data
rdy = pi.serial_data_available(gps)
if rdy > 0:
    (b, d) = pi.serial_read(gps, rdy)
    print(d.decode("utf-8")) # bytearray to string
pi.serial_close(gps)

pi.stop()

Como o manual do recetor indica, periodicamente são enviadas diversas mensagens de texto (segundo um protocolo NMEA) que são lidas pela placa Raspberry Pi. Nessas mensagens estão presentes diversas informações nomeadamente as coordenadas GPS. Eis algumas dessas mensagens:

$GPGGA,121833.665,4110.6688,N,00835.7032,W,1,04,8.2,144.0,M,51.4,M,,0000*42
$GPGSA,A,3,12,15,13,25,,,,,,,,,9.1,8.2,4.1*37
$GPRMC,121833.665,A,4110.6688,N,00835.7032,W,0.21,154.58,110515,,,A*70
$GPGGA,121834.665,4110.6686,N,00835.7033,W,1,04,8.1,144.1,M,51.4,M,,0000*48
$GPGSA,A,3,12,15,13,25,,,,,,,,,9.1,8.1,4.1*34
$GPGSV,3,1,09,24,72,004,,12,64,218,33,15,49,152,39,18,30,259,*7E

As coordenadas detetadas (41º 10.6688′ N, 008º 35.7032′ W) estão no formato graus/minutos. Uma vez convertidas para graus (41.1778133°, -008.5950533°) podem depois aqui ser localizadas num mapa Google:

FEUPgps

As principais funções da biblioteca pigpio para o uso do protocolo UART são as seguintes:

A imagem seguinte mostra a chegada pela linha RxD dos primeiros 3 carateres duma mensagem enviada pelo recetor GPS: $ (ASCII 0x24), G (ASCII 0x47), P (ASCII 0x50)

UART_wf

De notar a presença para cada caráter dum start bit, 8 bits de dados (um LSB em primeiro lugar) e um stop bit.

PySerial

Uma alternativa ao uso das funções desta biblioteca será o uso do package Python pYSerial. Este pacote disponibiliza um maior naipe de funções sem contudo a funcionalidade do controlo remoto característica da biblioteca pigpio.

Últimas considerações

O facto de no protocolo UART não existir linha de relógio faz com que as velocidades de transmissão não possam ser demasiado elevadas (em geral o valor máximo não vai além de 115200 bps). Pelo contrário os protocolos a apresentar seguidamente são síncronos pois enviam os dados de forma sincronizada com uma linha de relógio, permitindo assim maiores velocidades.

Adicionalmente os próximos protocolos seguem uma filosofia mestre-escravo (master-slave), onde a placa Raspberry Pi toma o papel de “master” e o dispositivo a ela ligado, o de “slave“. Ou seja, é a placa que gera o sinal de relógio e comanda o processo de leitura e escrita dos dados.

 


SPI

No protocolo SPI (Serial Peripheral Interface), quer o master quer o slave possuem internamente um registo de deslocamento que serão ligados entre si por duas linhas de dados:

  • MOSI – Master Output Slave Input
  • MISO – Master Input Slave Output

400px-SPI_8-bit_circular_transfer.svg

Os bits são deslocados ao ritmo do sinal de relógio (SCLK) gerado pelo master.

Para se poder ligar diferentes slaves, o master controla o valor presente em determinadas linhas de seleção (designadas na figura seguinte por SSx). O slave que tiver a sua linha ativa, saberá que os dados na linha MOSI são para si, e/ou que pode gerar dados na linha MISO. Deste modo não há qualquer conflito ao nível das linhas MISO pois só uma estará ativa (as restantes estarão em “alta impedância”).

350px-SPI_three_slaves.svg

No caso da placa Raspberry Pi, os pinos da interface necessários à implementação do protocolo SPI são os indicados a roxo. É possível distinguir para além dos sinais SPI_MOSI, SPI_MISO e SPI_SCLK, dois sinais de seleção: SPI_CE0 e SPI_CE1.

RPi-GPIO

Projeto LCD

Para exemplificar o uso do protocolo SPI, será aqui apresentado um projeto que controla um pequeno LCD. Trata-se dum LCD monocromático, com uma resolução de 48×84 pixels, que era usado nos antigos telemóveis NOKIA 5510. O controlador deste LCD é o PDC8544 da Philips, sugerindo-se a consulta do seu manual para perceber o seu funcionamento.

LCD_bb2

De notar que este LCD só recebe dados e comandos, nunca enviando qualquer tipo de informação por SPI e daí que não possua o sinal MISO. Existem contudo sinais adicionais aos já descritos:

  • RST – Sinal usado para fazer o reset ao LCD
  • D/C – Sinal que distingue se o valor enviado é um dado (D/C igual a 1) se um comando
  • LED – Sinal usado para iluminar por PWM o LCD (não esquecer a resistência de 220Ω)

Estes sinais serão ligados aos pinos GPIO4, GPIO22 e GPIO25 respetivamente.

A organização do ecrã do LCD está representada na seguinte figura. A memória RAM que contém a informação dos 48×84 pixels estão organizados em 504 bytes (6 linhas de 84 bytes). Cada byte tem o bit mais significativo (MSB) na parte inferior, e o menos significativo (LSB) na parte superior:

NOKIAram

O seguinte código (nokia5110.py) define a classe Nokia5110, onde estão definidos um conjunto de métodos para interagir com o LCD, nomeadamente:

  • dim(percentage) – Ilumina em percentage% o LCD
  • clear() – limpa o ecrã do LCD
  • printat(row, col, msg) – Imprime a mensagem msg na linha row (de 0 a 5), a partir da coluna col (de 0 a 13).
import pigpio
import font

RST = 4
D_C = 22
LED = 25

class Nokia5110():
    def __init__(self, pi):
        self.pi = pi
        self.lcd = self.pi.spi_open(0, 1000000, 0)

        self.pi.set_mode(D_C, pigpio.OUTPUT)
        self.pi.set_mode(RST, pigpio.OUTPUT)
        self.pi.set_mode(LED, pigpio.OUTPUT)
        pi.write(RST, True)
        pi.write(LED, False)

        self.init()
        self.clear()
        
    def init(self):
        self.pi.gpio_trigger(RST, 50, 0)
        self.pi.write(D_C, False)
        self.pi.spi_write(self.lcd, [0x21, 0xB1, 0x05, 0x14, 0x20, 0x0C])
    
    def dim(self, percentage):
        self.pi.set_PWM_frequency(LED, 1000)
        self.pi.set_PWM_dutycycle(LED, percentage)        
    
    def gotoXY(self, x, y):
        self.pi.write(D_C, False)
        self.pi.spi_write(self.lcd, [0x80 | x, 0x40 | y])
        
    def clear(self):
        self.gotoXY(0, 0)
        self.pi.write(D_C, True)
        self.pi.spi_write(self.lcd, bytearray(6*84))
    
    def printat(self, row, col, msg):
        self.gotoXY(6*col, row)
        dat = []
        for c in msg:
            f = 5*(ord(c)-32)
            dat = dat + font.font[f:f+5] + [0]
        self.pi.write(D_C, True)
        self.pi.spi_write(self.lcd, dat)
    
    def close(self):
        self.pi.spi_close(self.lcd)

De notar a necessidade de se fazer a importação da lista font (presente no ficheiro font.py), onde estão definidos os bitmaps dos diversos carateres (desde o caráter com o código ASCII 0x20 correspondendo ao “espaço”):

font = [0x00, 0x00, 0x00, 0x00, 0x00, 
    0x00, 0x00, 0x5F, 0x00, 0x00, 
    0x00, 0x07, 0x00, 0x07, 0x00, 
    0x14, 0x7F, 0x14, 0x7F, 0x14, 
    0x24, 0x2A, 0x7F, 0x2A, 0x12, 
    0x23, 0x13, 0x08, 0x64, 0x62, 
    0x36, 0x49, 0x56, 0x20, 0x50, 
    0x00, 0x08, 0x07, 0x03, 0x00, 
    0x00, 0x1C, 0x22, 0x41, 0x00, 
    0x00, 0x41, 0x22, 0x1C, 0x00, 
    0x2A, 0x1C, 0x7F, 0x1C, 0x2A, 
    0x08, 0x08, 0x3E, 0x08, 0x08, 
    0x00, 0x80, 0x70, 0x30, 0x00, 
    0x08, 0x08, 0x08, 0x08, 0x08, 
    0x00, 0x00, 0x60, 0x60, 0x00, 
    0x20, 0x10, 0x08, 0x04, 0x02, 
    0x3E, 0x51, 0x49, 0x45, 0x3E, 
    0x00, 0x42, 0x7F, 0x40, 0x00, 
    0x72, 0x49, 0x49, 0x49, 0x46, 
    0x21, 0x41, 0x49, 0x4D, 0x33, 
    0x18, 0x14, 0x12, 0x7F, 0x10, 
    0x27, 0x45, 0x45, 0x45, 0x39, 
    0x3C, 0x4A, 0x49, 0x49, 0x31, 
    0x41, 0x21, 0x11, 0x09, 0x07, 
    0x36, 0x49, 0x49, 0x49, 0x36, 
    0x46, 0x49, 0x49, 0x29, 0x1E, 
    0x00, 0x00, 0x14, 0x00, 0x00, 
    0x00, 0x40, 0x34, 0x00, 0x00, 
    0x00, 0x08, 0x14, 0x22, 0x41, 
    0x14, 0x14, 0x14, 0x14, 0x14, 
    0x00, 0x41, 0x22, 0x14, 0x08, 
    0x02, 0x01, 0x59, 0x09, 0x06, 
    0x3E, 0x41, 0x5D, 0x59, 0x4E, 
    0x7C, 0x12, 0x11, 0x12, 0x7C, 
    0x7F, 0x49, 0x49, 0x49, 0x36, 
    0x3E, 0x41, 0x41, 0x41, 0x22, 
    0x7F, 0x41, 0x41, 0x41, 0x3E, 
    0x7F, 0x49, 0x49, 0x49, 0x41, 
    0x7F, 0x09, 0x09, 0x09, 0x01, 
    0x3E, 0x41, 0x41, 0x51, 0x73, 
    0x7F, 0x08, 0x08, 0x08, 0x7F, 
    0x00, 0x41, 0x7F, 0x41, 0x00, 
    0x20, 0x40, 0x41, 0x3F, 0x01, 
    0x7F, 0x08, 0x14, 0x22, 0x41, 
    0x7F, 0x40, 0x40, 0x40, 0x40, 
    0x7F, 0x02, 0x1C, 0x02, 0x7F, 
    0x7F, 0x04, 0x08, 0x10, 0x7F, 
    0x3E, 0x41, 0x41, 0x41, 0x3E, 
    0x7F, 0x09, 0x09, 0x09, 0x06, 
    0x3E, 0x41, 0x51, 0x21, 0x5E, 
    0x7F, 0x09, 0x19, 0x29, 0x46, 
    0x26, 0x49, 0x49, 0x49, 0x32, 
    0x03, 0x01, 0x7F, 0x01, 0x03, 
    0x3F, 0x40, 0x40, 0x40, 0x3F, 
    0x1F, 0x20, 0x40, 0x20, 0x1F, 
    0x3F, 0x40, 0x38, 0x40, 0x3F, 
    0x63, 0x14, 0x08, 0x14, 0x63, 
    0x03, 0x04, 0x78, 0x04, 0x03, 
    0x61, 0x59, 0x49, 0x4D, 0x43, 
    0x00, 0x7F, 0x41, 0x41, 0x41, 
    0x02, 0x04, 0x08, 0x10, 0x20, 
    0x00, 0x41, 0x41, 0x41, 0x7F, 
    0x04, 0x02, 0x01, 0x02, 0x04, 
    0x40, 0x40, 0x40, 0x40, 0x40, 
    0x00, 0x03, 0x07, 0x08, 0x00, 
    0x20, 0x54, 0x54, 0x78, 0x40, 
    0x7F, 0x28, 0x44, 0x44, 0x38, 
    0x38, 0x44, 0x44, 0x44, 0x28, 
    0x38, 0x44, 0x44, 0x28, 0x7F, 
    0x38, 0x54, 0x54, 0x54, 0x18, 
    0x00, 0x08, 0x7E, 0x09, 0x02, 
    0x18, 0xA4, 0xA4, 0x9C, 0x78, 
    0x7F, 0x08, 0x04, 0x04, 0x78, 
    0x00, 0x44, 0x7D, 0x40, 0x00, 
    0x20, 0x40, 0x40, 0x3D, 0x00, 
    0x7F, 0x10, 0x28, 0x44, 0x00, 
    0x00, 0x41, 0x7F, 0x40, 0x00, 
    0x7C, 0x04, 0x78, 0x04, 0x78, 
    0x7C, 0x08, 0x04, 0x04, 0x78, 
    0x38, 0x44, 0x44, 0x44, 0x38, 
    0xFC, 0x18, 0x24, 0x24, 0x18, 
    0x18, 0x24, 0x24, 0x18, 0xFC, 
    0x7C, 0x08, 0x04, 0x04, 0x08, 
    0x48, 0x54, 0x54, 0x54, 0x24, 
    0x04, 0x04, 0x3F, 0x44, 0x24, 
    0x3C, 0x40, 0x40, 0x20, 0x7C, 
    0x1C, 0x20, 0x40, 0x20, 0x1C, 
    0x3C, 0x40, 0x30, 0x40, 0x3C, 
    0x44, 0x28, 0x10, 0x28, 0x44, 
    0x4C, 0x90, 0x90, 0x90, 0x7C, 
    0x44, 0x64, 0x54, 0x4C, 0x44, 
    0x00, 0x08, 0x36, 0x41, 0x00, 
    0x00, 0x00, 0x77, 0x00, 0x00, 
    0x00, 0x41, 0x36, 0x08, 0x00, 
    0x02, 0x01, 0x02, 0x04, 0x02, 
    0x3C, 0x26, 0x23, 0x26, 0x3C, 
    0x1E, 0xA1, 0xA1, 0x61, 0x12]

As principais funções da biblioteca pigpio para o uso do protocolo SPI são as seguintes:

  • spi_open – Abre uma ligação SPI com determinadas características*
  • spi_close – Fecha a ligação SPI
  • spi_write – Envia dados
  • spi_read – Recebe dados
  • spi_xfer – Envia e recebe dados em simultâneo

* Uma dessas características é o modo em que o sinal de relógio SCLK é gerado, nomeadmente o seu valor em repouso (CPOL) e a transição (CPHA) em que é amostrado o sinal MISO:

spi-modes

O programa lcd.py imprime no LCD um conjunto de mensagens contendo nomeadamente a hora e data:

import platform
import pigpio
import nokia5110
import time

if platform.uname()[4][0:3] == "arm":
    pi = pigpio.pi()
else:
    pi = pigpio.pi("10.0.0.100")

lcd = nokia5110.Nokia5110(pi)
lcd.dim(80)
lcd.printat(1, 3, "EDM 2015")
lcd.printat(2, 1, "Project: LCD")
lcd.printat(3, 3, time.strftime("%H:%M:%S"))
lcd.printat(4, 2, time.strftime("%d/%m/%Y"))
lcd.close()

pi.stop()

As seguintes formas de onda mostram a evolução temporal dos sinais SPI quando do envio dos comandos após o reset do LCD (linha 25 do código nokia5110.py):

SPI_wf

 


I2C

O protocolo I2C (Inter-Integrated Circuit) é implementado apenas com duas linhas:

  • SDA – Serial DAta
  • SCL – Serial CLock

A existência duma só linha de dados impede que tal como ocorre no protocolo SPI a comunicação seja full-duplex, ou seja que se possa realizar em simultâneo o envio e a receção de dados.

Como a figura seguinte mostra, todos os dispositivos ficam “dependurados” nesse barramento. Não existem quaisquer linhas de seleção pelo que o mecanismo de escolha de qual o dispositivo slave com que o master pretende comunicar. Isso é feito na forma dum endereçamento por software, i.e. cada slave terá um endereço de 7 bits a ser usado quando do estabelecimento da comunicação.

I2C

De notar que todas as ligações ao barramento são realizadas por saídas em dreno-aberto (e daí a necessidade das resistências de pull-up). Dessa forma nunca existirão curto-circuitos nas linhas mesmo que hajam acessos simultâneos.

No caso da placa Raspberry Pi, os pinos da interface necessários à implementação do protocolo I2C são os indicados a azul, são eles o I2C1_SDA e I2C1_SCL.

RPi-GPIO

Projeto Accel

Neste exemplo a placa Raspberry Pi é ligada a um acelerómetro ADXL345. Trata-se dum sensor que fornece as acelerações a que está sujeito segundo os seus eixos x, y e z.

Tal como o circuito da figura seguinte mostra, para além das alimentações (VCC e GND) são ainda ligados ao sensor as linhas I2C: SDA e SCL. Existem ainda duas outras ligações ao nível lógico 1: CS (que escolhe o protocolo I2C) e SDO (que escolhe o endereço de 7 bits 0x1D).

Accel_bb2

O código fonte (adxl345.py) que define a classe ADXL345, é o seguinte:

import pigpio
from collections import namedtuple

def dataConversion(high, low):
    value = low + (high<<8)
    if value > 2**15:
        value -= 2**16
    return value

class ADXL345():
    def __init__(self, pi, addr, parent=None):
        self.pi = pi
        self.addr = addr
        self.adxl = self.pi.i2c_open(1, self.addr)
        self.measure(True)

    def close(self):
        self.pi.i2c_close(self.adxl)
        
    def ID(self):
        ID = self.pi.i2c_read_byte_data(self.adxl, 0x00)
        return ID
        
    def getData(self):
        v = self.pi.i2c_read_i2c_block_data(self.adxl, 0x32, 6)[1]
        data = namedtuple("data", "x y z")
        data.x = dataConversion(v[1], v[0])
        data.y = dataConversion(v[3], v[2])
        data.z = dataConversion(v[5], v[4])
        return data
         
    def measure(self, state):
        POWER_CTL = self.pi.i2c_read_byte_data(self.adxl, 0x2D)
        POWER_CTL = POWER_CTL | 0x08 if state else POWER_CTL & ~0x08
        self.pi.i2c_write_byte_data(self.adxl, 0x2D, POWER_CTL)

Esta classe para além dos métodos de inicialização (__init__) e fecho (close), implementa ainda os seguintes outros métodos:

  • ID() – Devolve o byte identificador do sensor (ver pág. 24 do manual)
  • measure(state) – Liga (state = True) ou desliga a medição das acelerações (ver pág. 25)
  • getData() – Devolve os dados das acelerações (ver pág. 27)

Estas informações estão presentes em registos internos do sensor, tal como pode ser visto na página 23 da sua datasheet.

Para a implementação desta classe foram usadas algumas das seguintes funções da biblioteca pigpio:

import platform
import pigpio
import nokia5110
import adxl345
import time

if platform.uname()[4][0:3] == "arm":
    pi = pigpio.pi()
else:
    pi = pigpio.pi("10.0.0.100")

lcd = nokia5110.Nokia5110(pi)
adxl = adxl345.ADXL345(pi, 0x1D)
print("ADXL ID: 0x{:02X}".format(adxl.ID()))

lcd.printat(0, 3, "EDM 2015")
lcd.printat(1, 0, "Project: Accel")

while 1:
    data = adxl.getData()    
    lcd.printat(3, 4, "X {:4}".format(data.x))
    lcd.printat(4, 4, "Y {:4}".format(data.y))
    lcd.printat(5, 4, "Z {:4}".format(data.z))
    if data.z > 0: break
    time.sleep(.1)
    
adxl.close()
lcd.close()

pi.stop()

Na figura seguinte é possível ver a evolução temporal das linhas SDA e SCL quando da execução do método ID():

I2C_wf

Como se vê, começa-se por fazer uma escrita (endereço 0x3A*) do número do registo que se pretende ler (0x00), e seguidamente faz-se uma leitura (endereço 0x3B*) do conteúdo desse registo (0xE5 neste caso).

* Estes endereços de 8 bits resultam do de 7 bits (0x1D) acrescentado à direita de um 0 (no caso duma escrita) e de um 1 no caso duma leitura).


Projeto digipot

Pretende-se criar duas classes Python (MCP42010 e AD5241) capazes de controlar outros tantos potenciómetros digitais (MCP42010 e AD5241) que usam respetivamente os protocolos SPI e I2C.

MCP42010

Trata-se dum potenciómetro digital de 10kΩ (na realidade são dois) cujo ponto médio pode ser regulado entre 256 posições. A interface com este circuito faz-se através dum barramento SPI. De seguida são apresentadas algumas imagens importantes retiradas da sua datasheet:

MCP42010a

MCP42010b

MCP42010c

AD5241

Trata-se dum potenciómetro digital de 10kΩ (na realidade são dois) cujo ponto médio pode ser regulado entre 256 posições. A interface com este circuito faz-se através dum barramento I2C. De seguida são apresentadas algumas imagens importantes retiradas da sua datasheet:

AD5241a

AD5241b

AD5241c


Projeto e2prom

Pretende-se criar duas classes Python (M24lc01 e M25lc040) capazes de controlar os processos de escrita e leitura em duas memórias e2prom (24LC01 e 25LC040) que usam respetivamente os protocolos I2C e SPI.

mems

A memória 24LC01 tem uma capacidade de 128 bytes (organizada em blocos de 8 bytes), enquanto que a 25LC040 tem uma capacidade de 512 bytes (organizada em blocos de 16 bytes).

e2prom_bb

De notar que o circuito não inclui qualquer resistência de pull-up nas linhas I2C_SDA e I2C_SCL mas tal deve-se ao facto da placa Raspberry Pi B+ já as incluir, tal como se pode ver no respetivo esquemático.

Sugere-se um leitura atenta da datasheet da memória 24LC01 (em especial as páginas 7 a 12), e a datasheet da memória 25LC040 (em especial as páginas 7 a 10).

O código base para a construção dessas suas classes é o seguinte, que deve ser colocado num ficheiro e2prom.py:

from __future__ import print_function

def printMem(dat):
    size = len(dat)
    print("      x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF")
    for addr in range(0, size):
        if addr % 128 == 0:
            print("     ------------------------------------------------")
        if addr % 16 == 0:
            print("{:2X}x | ".format(addr//16), end="")
        print("{:02X} ".format(dat[addr]), end="")
        if addr % 16 == 15:
            print("")

def check(address, nBytes, size):
    if type(address) is not int:
        raise TypeError("wrong type address")
    if address < 0 or address >= size:
        raise NameError("address out of range (0..{})".format(size+1))
    if address + nBytes > size:
        raise NameError("too many bytes")
    
class M25lc040():
    def __init__(self, pi):
        self.size = 512
        pass

    def close(self):
        pass
                
    def write(self, address, data):
        check(address, len(data), self.size)
        pass
    
    def read(self, address, nBytes):
        check(address, nBytes, self.size)
        dat = [0]*nBytes
        return dat

    def show(self):
        data = self.read(0, self.size)
        printMem(data)
            
class M24lc01():
    def __init__(self, pi):
        self.size = 128
        pass

    def close(self):
        pass
    
    def write(self, address, data):
        check(address, len(data), self.size)
        pass
    
    def read(self, address, nBytes):
        check(address, nBytes, self.size)
        data = [0]*nBytes
        return data

    def show(self):
        data = self.read(0, self.size)
        printMem(data)

De notar a existência da função printMem, invocada pelo método show de cada classe e que apresenta os dados lidos da memória na forma duma tabela. Eis o exemplo para o caso dos 128 bytes presentes na memória 24LC01:

      x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF
     ------------------------------------------------
 0x | 00 00 00 00 00 00 01 02 03 04 00 00 00 00 00 00 
 1x | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
 2x | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
 3x | 00 00 00 00 00 00 00 00 00 00 00 00 00 01 02 03 
 4x | 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
 5x | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
 6x | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
 7x | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

Um programa de teste (mem.py) das classes pretendidas seria o seguinte:

import platform
import pigpio
import e2prom

if platform.uname()[4][0:3] == "arm":
    pi = pigpio.pi()
else:
    pi = pigpio.pi("192.168.102.123", 30X88) # altere a porta para o seu grupo

m24 = e2prom.M24lc01(pi)
m24.show()
dat = [0x01, 0x02, 0x03]
m24.write(0x00, dat)
m24.write(0x17, dat)
m24.show()
m24.close()

m25 = e2prom.M25lc040(pi)
m25.show()
dat = [0x01, 0x02, 0x03]
m25.write(0x000, dat)
m25.write(0x01E, dat)
m25.write(0x100, dat)
m25.show()
m25.close()

pi.stop()

 

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *