Konwersja kodu z Pythona na Java - sprawdzenie

0

Witajcie!
Mam ostatnio (mówiąc ostatnio mam na myśli 2 tygodnie) bardzo duży problem z moim wyświetlaczem LCD do Raspberry Pi 3.
Żaden znaleziony w necie kod nie działa, wszelkie próby napisania go własnoręcznie legły w gruzach. Pojawił się jednak promyk nadziei - mam kod w Pythonie, który działa. Spróbowałem go przepisać na Javę i spróbować go wykonać.

Hardware

Software
Działający kod w Pythonie

#!/usr/bin/python

import smbus
import time

# Define some device parameters
I2C_ADDR = 0x3f  # I2C device address
LCD_WIDTH = 16  # Maximum characters per line

# Define some device constants
LCD_CHR = 1  # Mode - Sending data
LCD_CMD = 0  # Mode - Sending command

LCD_LINE_1 = 0x80  # LCD RAM address for the 1st line
LCD_LINE_2 = 0xC0  # LCD RAM address for the 2nd line
LCD_LINE_3 = 0x94  # LCD RAM address for the 3rd line
LCD_LINE_4 = 0xD4  # LCD RAM address for the 4th line

LCD_BACKLIGHT = 0x08  # On
# LCD_BACKLIGHT = 0x00  # Off

ENABLE = 0b00000100  # Enable bit

# Timing constants
E_PULSE = 0.0005
E_DELAY = 0.0005

# Open I2C interface
# bus = smbus.SMBus(0)  # Rev 1 Pi uses 0
bus = smbus.SMBus(1)  # Rev 2 Pi uses 1


def lcd_init():
    # Initialise display
    lcd_byte(0x33, LCD_CMD)  # 110011 Initialise
    lcd_byte(0x32, LCD_CMD)  # 110010 Initialise
    lcd_byte(0x06, LCD_CMD)  # 000110 Cursor move direction
    lcd_byte(0x0C, LCD_CMD)  # 001100 Display On,Cursor Off, Blink Off
    lcd_byte(0x28, LCD_CMD)  # 101000 Data length, number of lines, font size
    lcd_byte(0x01, LCD_CMD)  # 000001 Clear display
    time.sleep(E_DELAY)


def lcd_byte(bits, mode):
    # Send byte to data pins
    # bits = the data
    # mode = 1 for data
    #        0 for command

    bits_high = mode | (bits & 0xF0) | LCD_BACKLIGHT
    bits_low = mode | ((bits << 4) & 0xF0) | LCD_BACKLIGHT

    # High bits
    bus.write_byte(I2C_ADDR, bits_high)
    lcd_toggle_enable(bits_high)

    # Low bits
    bus.write_byte(I2C_ADDR, bits_low)
    lcd_toggle_enable(bits_low)


def lcd_toggle_enable(bits):
    # Toggle enable
    time.sleep(E_DELAY)
    bus.write_byte(I2C_ADDR, (bits | ENABLE))
    time.sleep(E_PULSE)
    bus.write_byte(I2C_ADDR, (bits & ~ENABLE))
    time.sleep(E_DELAY)


def lcd_string(message, line):
    # Send string to display

    message = message.ljust(LCD_WIDTH, " ")

    lcd_byte(line, LCD_CMD)

    for i in range(LCD_WIDTH):
        lcd_byte(ord(message[i]), LCD_CHR)


def main():
    # Main program block

    # Initialise display
    lcd_init()

    while True:
        # Send some test
        lcd_string("RPiSpy         <", LCD_LINE_1)
        lcd_string("I2C LCD        <", LCD_LINE_2)

        time.sleep(3)

        # Send some more text
        lcd_string(">         RPiSpy", LCD_LINE_1)
        lcd_string(">        I2C LCD", LCD_LINE_2)

        time.sleep(3)


if __name__ == '__main__':

    try:
        main()
    except KeyboardInterrupt:
        pass
    finally:
        lcd_byte(0x01, LCD_CMD)

Prawie działający kod w Javie:

package com.burdzi0;

import com.pi4j.io.i2c.I2CBus;
import com.pi4j.io.i2c.I2CDevice;
import com.pi4j.io.i2c.I2CFactory;
import com.pi4j.io.i2c.I2CFactoryProviderRaspberry;
import com.sun.org.apache.bcel.internal.generic.I2C;

import java.io.IOException;

public class LCDController {

    int I2C_ADDR = 0x3f;
    int LCD_WIDTH = 16;

    int LCD_CHR = 1;
    int LCD_CMD = 0;

    int LCD_LINE_1 = 0x80;
    int LCD_LINE_2 = 0xC0;

    int LCD_BACKLIGHT_ON = 0x08;
    int LCD_BACKLIGHT_OFF = 0x00;

    int ENABLE = 0b00000100;

    int E_PULSE = 5;
    int E_DELAY = 5;

    I2CBus bus;
    I2CDevice dev;

    public LCDController() throws IOException, InterruptedException {
        bus = I2CFactory.getInstance(I2CBus.BUS_1);
        dev = bus.getDevice(I2C_ADDR);
        init();
    }

    void init() throws InterruptedException, IOException {
        lcd_byte(0x33, LCD_CMD);
        lcd_byte(0x32, LCD_CMD);
        lcd_byte(0x06, LCD_CMD);
        lcd_byte(0x0C, LCD_CMD);
        lcd_byte(0x28, LCD_CMD);
        lcd_byte(0x01, LCD_CMD);
        Thread.sleep(E_DELAY);
    }

    void lcd_byte(int bits, int mode) throws IOException, InterruptedException {
        int bits_high = mode | (bits & 0xF0) | LCD_BACKLIGHT_ON;
        int bits_low = mode | ((bits << 4) & 0xF0) | LCD_BACKLIGHT_ON;

        dev.write(I2C_ADDR, (byte) bits_high);
        lcd_toggle_enable((byte) bits_high);

        dev.write(I2C_ADDR, (byte) bits_low);
        lcd_toggle_enable((byte) bits_low);
    }

    void lcd_toggle_enable(byte bits) throws InterruptedException, IOException {
        Thread.sleep(E_DELAY);
        dev.write(I2C_ADDR, (byte) (bits | ENABLE));
        Thread.sleep(E_PULSE);
        dev.write(I2C_ADDR, (byte) (bits & ~ENABLE));
        Thread.sleep(E_DELAY);
    }

    void lcd_string(String message, int line) throws IOException, InterruptedException {
        lcd_byte(line, LCD_CMD);
        for (int i = 0; i < LCD_WIDTH; i++) {
            lcd_byte(message.charAt(i), LCD_CHR);
        }
    }

    public static void main(String[] args) {
        LCDController lcd = null;
        try {
            lcd = new LCDController();
            lcd.init();
            while (true) {
                lcd.lcd_string("RPiSpy         <", lcd.LCD_LINE_1);
                lcd.lcd_string("I2C LCD        <", lcd.LCD_LINE_2);
                Thread.sleep(3000);
                lcd.lcd_string(">         RPiSpy", lcd.LCD_LINE_1);
                lcd.lcd_string(">        I2C LCD", lcd.LCD_LINE_2);
                Thread.sleep(3000);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            try {
                lcd.lcd_byte(0x01, lcd.LCD_CMD);
            } catch (IOException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

Wiem, że kod nie jest ładny, ale ma być przede wszystkim funkcjonalny. Efektem tego kodu jest:
IMG_20170913_123509.jpgIMG_20170913_123512.jpgIMG_20170913_123514.jpg
Gdzie popełniłem błąd?

0

Bierzesz pod uwagę ze int javie jest signed? Może druga strona oczekuje że jednak unsigned?

0

@Shalom: Co w takim wypadku byś zaproponował? long nie mogę użyć, bo metoda oczekuje int. Jak to obejść?

0
Burdzi0 napisał(a):

@Shalom: Co w takim wypadku byś zaproponował? long nie mogę użyć, bo metoda oczekuje int. Jak to obejść?

Nie jestem Shalom, ale mała podpowiedź: "char" jest nieznakowanym 16-bitowym integerem w Javie.

0

Próbowałem zmienić i nic. Wróciłem do kodu i nic. Ewidentnie coś innego wywoływało poprzedni stan. Teraz wygląda na to, że mój kod nie działa wcale :/

0

Jakieś pomysły? Cokolwiek? Jestem w kropce, a żaden z przykładów w necie nie pomaga...

1

Zrób to metodą "dupa debuging" (po angielsku "tracing").
Dla każdego bajtu który fizycznie wychodzi na urządzenie zapisz czas względny od momentu startu apki (w ms lub ns) i wartość bajtu.
Zrób to po stronie Pythona i Javy.
Porównaj wyniki.
Popraw Jave.

Czyli tutaj - zastąp "dev.write" swoją metodą "lcd_write" i tam umieść "dev.write" oraz tracing.

1

Dumpy porób z obu programów i porównaj. Np. wszystkie wywołania write_byte.

1

Zamiast patrzeć się w kod w nieskończoność zrób testy jednostkowe.

Kiedyś pracowałem nad komunikacją (przez Ethernet) z czytnikiem kart płatniczych i dzięki testom jednostkowym wykryłem parę błędów w kodzie zanim w ogóle dostałem sprzęt do ręki.

0

Jak nie poradzisz sobie z tym, to zrób niskopoziomową obsługę w c/c++ na pewno będzie więcej przykładów, opakuj to w jni native, a logikę napisz w javie.

2

Matkomoja działa :D
2 tygodnie chrzanienia się:

  • ze sposobem połączenia
  • ze sposobem debugowania
  • z różnymi znaleźnymi kodami
    i wreszcie działa :D

Problem polegał na zmianie:

dev.write(I2C_ADDR, (byte) bits_high);

na:

dev.write(LCD_LINE_1, (byte) bits_high);

Cały przykładowy kod wygląda teraz tak:

import com.pi4j.io.i2c.I2CBus;
import com.pi4j.io.i2c.I2CDevice;
import com.pi4j.io.i2c.I2CFactory;
import org.apache.log4j.Logger;

import java.io.IOException;
 
public class LCDController {
 
    int I2C_ADDR = 0x3f;
    int LCD_WIDTH = 16;
 
    int LCD_CHR = 1;
    int LCD_CMD = 0;
 
    int LCD_LINE_1 = 0x80;
    int LCD_LINE_2 = 0xC0;
 
    int LCD_BACKLIGHT_ON = 0x08;
    int LCD_BACKLIGHT_OFF = 0x00;
 
    int ENABLE = 0b00000100;
 
    int E_PULSE = 5;
    int E_DELAY = 5;
 
    I2CBus bus;
    I2CDevice dev;

    public LCDController() throws IOException, InterruptedException, I2CFactory.UnsupportedBusNumberException {
        bus = I2CFactory.getInstance(I2CBus.BUS_1);
        dev = bus.getDevice(I2C_ADDR);
        init();
    }
 
    void init() throws InterruptedException, IOException {
        lcd_byte(0x33, LCD_CMD);
        lcd_byte(0x32, LCD_CMD);
        lcd_byte(0x06, LCD_CMD);
        lcd_byte(0x0C, LCD_CMD);
        lcd_byte(0x28, LCD_CMD);
        lcd_byte(0x01, LCD_CMD);
        Thread.sleep(E_DELAY);
    }
 
    void lcd_byte(int bits, int mode) throws IOException, InterruptedException {
        int bits_high = mode | (bits & 0xF0) | LCD_BACKLIGHT_ON;
        int bits_low = mode | ((bits << 4) & 0xF0) | LCD_BACKLIGHT_ON;

        dev.write(LCD_LINE_1, (byte) bits_high);
        lcd_toggle_enable((byte) bits_high);
 
        dev.write(LCD_LINE_1, (byte) bits_low);
        lcd_toggle_enable((byte) bits_low);
    }
 
    void lcd_toggle_enable(byte bits) throws InterruptedException, IOException {
        dev.write(LCD_LINE_1, (byte) (bits | ENABLE));
        dev.write(LCD_LINE_1, (byte) (bits & ~ENABLE));
    }
 
    void lcd_string(String message, int line) throws IOException, InterruptedException {
        lcd_byte(line, LCD_CMD);
        for (int i = 0; i < LCD_WIDTH; i++) {
            lcd_byte(message.charAt(i), LCD_CHR);
        }
    }
 
    public static void main(String[] args) {
        LCDController lcd = null;
        try {
            lcd = new LCDController();
            lcd.init();
            lcd.lcd_string("4programmers.net", lcd.LCD_LINE_1);
            lcd.lcd_string("     Burdzi0    ", lcd.LCD_LINE_2);
            Thread.sleep(2000);
            lcd.lcd_string("Bardzo  dziekuje", lcd.LCD_LINE_1);
            lcd.lcd_string("  za  pomoc  :D ", lcd.LCD_LINE_2);
            Thread.sleep(2000);
            lcd.lcd_string("     Shalom     ", lcd.LCD_LINE_1);
            lcd.lcd_string("     vpiotr     ", lcd.LCD_LINE_2);
            Thread.sleep(2000);
            lcd.lcd_string("    jarekczek   ", lcd.LCD_LINE_1);
            lcd.lcd_string("     Wibowit    ", lcd.LCD_LINE_2);
            Thread.sleep(2000);

        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (I2CFactory.UnsupportedBusNumberException e) {
            e.printStackTrace();
        } finally {
            try {
                lcd.lcd_byte(0x01, lcd.LCD_CMD);
            } catch (IOException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
 
}

Przykład podziękowań w zipie ;)
VID_20170913_184414.mp4.zip

0
    void lcd_byte(int bits, int mode) throws IOException, InterruptedException {
        int bits_high = mode | (bits & 0xF0) | LCD_BACKLIGHT_ON;
        int bits_low = mode | ((bits << 4) & 0xF0) | LCD_BACKLIGHT_ON;

        dev.write((byte) bits_high);
        lcd_toggle_enable((byte) bits_high);
 
        dev.write((byte) bits_low);
        lcd_toggle_enable((byte) bits_low);
    }
 
    void lcd_toggle_enable(byte bits) throws InterruptedException, IOException {
        dev.write((byte) (bits | ENABLE));
        dev.write((byte) (bits & ~ENABLE));
    }

Taka zmiana kodu pozwala na przyspieszenie zmiany tekstu - nie ma opóźnienia

0

To można fajnie rozpracować bez Pi. Na początku zamiast import wpisać:

class smbus:
  def __init__(self):
    pass
  @staticmethod
  def SMBus(i):
    return smbus()
  def write_byte(self, a, b):
    print a, b

class time:
  def __init__(self):
    pass
  @staticmethod
  def sleep(i):
    pass

i jeszcze jeden break w while True

A Pythona też trzeba umieć bo używa go Oracle w Oracle'u. :)
Edit: Nie, nie w Oracle'u. W WebLogicu. Web Logic Scripting Tool.

1 użytkowników online, w tym zalogowanych: 0, gości: 1