Konwersja kodu z Pythona na Java - sprawdzenie

Odpowiedz Nowy wątek
2017-09-13 12:51
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?


Pozostało 580 znaków

2017-09-13 13:11
0

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

Pozostało 580 znaków

2017-09-13 13:26
0

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


Pozostało 580 znaków

2017-09-13 13:42
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.

Pozostało 580 znaków

2017-09-13 14:35
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 :/


Pozostało 580 znaków

2017-09-13 16:07
0

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


Pozostało 580 znaków

2017-09-13 16:10
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.

edytowany 3x, ostatnio: vpiotr, 2017-09-13 16:12

Pozostało 580 znaków

2017-09-13 16:12
1

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

Pozostało 580 znaków

2017-09-13 16:23
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.


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
edytowany 1x, ostatnio: Wibowit, 2017-09-13 16:26
W sumie to chyba takie testy nie sa łatwe do napisania (gdzie jest komunikacja, operacje IO a nie jakas logika biznesowa) - scibi92 2017-09-13 17:36
Wręcz przeciwnie. Wystarczy, że na czas testu jednostkowego podmienię dev.write na listaIntów.add, a na końcu testu porównam wynikową listę z predefiniowaną i już mam podstawowy test. Nieco trudniej by było gdybyśmy sprawdzali też jednocześnie odstępy czasowe, ale tylko nieco. Wtedy można by zrobić listę przechowującą jakiś typ podstawowy np Wrapped mający dwa podtypy WrappedData i WrappedDelay, a potem można porównywać nagrane wartości z predefiniowanymi tak samo jak w wariancie podstawowym. Banalne byłoby też przeniesienie takich testów z Pythona do Javy. - Wibowit 2017-09-13 18:10
@Wibowit: Brzmi fajnie, ale z moimi umiejętnościami niemożliwe do stworzenia. Mam sprawdzać czy coś się wyświetla? Mogę to zrobić tylko ręcznie. Wystarczy, że na czas testu jednostkowego podmienię dev.write na listaIntów.add, a na końcu testu porównam wynikową listę z predefiniowaną i już mam podstawowy test. w tym wypadku nie mam czegoś takiego jak lista predefiniowana, brodzę po ciemku w tym momencie, w każdym razie dzięki za pomysł ;) - Burdzi0 2017-09-13 18:19
Skoro w Pythonie działa dobrze, to nagraj listy do testów w Pythonie, przenieś testy do Javy i odpal je. - Wibowit 2017-09-13 18:21
@Burdzi0: To nie byłoby takie trudne. Jakbyś zrobił taką samą rejestrację (tracing) w obu językach to w Pythonie zapisujesz ten trace do pliku, w Javie go wczytujesz i porównujesz z tym co Ci wyszło w trakcie testu w Javie. Jak zrobisz plik CSV to będzie to w miarę proste (liczba-porządkowa, adres, wartość). Tam masz trochę tych przeliczanek tych bajtów. Poza tym na początku testuj na prostszych danych - np. jeden znak. - vpiotr 2017-09-13 20:13

Pozostało 580 znaków

2017-09-13 18:54
Biały Rycerz
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.

Pozostało 580 znaków

2017-09-13 18:54
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


edytowany 1x, ostatnio: Burdzi0, 2017-09-13 18:55
Pokaż pozostałe 2 komentarze
A tak ogólnie to zazdraszczam projektu, bo nie dość że Raspberry Pi to jeszcze kawałek elektroniki. - vpiotr 2017-09-13 20:15
@vpiotr: W planach mam napisanie mini gry 2d i zrobienie kontrolerów na bazie GPIO ;) - Burdzi0 2017-09-13 21:25
@vpiotr: Swoją drogą to nie aż tak drogie, zwłaszcza, że nie trzeba a kupować wszystkiego naraz - Burdzi0 2017-09-13 21:25
@Burdzi0: no tak, ale raczej mi brakuje czasu niż pieniędzy na to. - vpiotr 2017-09-13 21:26
@vpiotr: Rozumiem, mam ciągle wakacje i nie mogę się przestawić na to, że inni ni, wybacz :| - Burdzi0 2017-09-13 21:31

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

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