PiSupply / PaPiRus

Resources for PaPiRus ePaper eInk displays
https://www.pi-supply.com/product/papirus-epaper-eink-screen-hat-for-raspberry-pi/
Other
345 stars 88 forks source link

PaPiRus HAT Pinout / Arduino connection #187

Open Donderda opened 6 years ago

Donderda commented 6 years ago

Hi everyone, I found this webpage: https://pinout.xyz/pinout/papirus_hat where I was able to determine which pins are used for what. Can anyone tell me what CS0 and CS1 are used for? Which one is for FLASH_CS and which one for EPD_CS?

Thank you for your help :)

tvoverbeek commented 6 years ago

SPI CE0 (BCM GPIO 8, pin 24) is used as the chip select for the SPI interface of the EPD COG(Chip-On-Glass). SPI CE1 (BCM GPIO 7, pin 26) is not used by PaPiRus-HAT, only wired through to the 40 pin FPC connector for extending the GPIO. See also the schematic on https://github.com/PiSupply/PaPiRus/blob/master/hardware/PaPiRus%20HAT/Latest%20Version%20-%20v1.9/2014-035-01-Pi-ePaper-circuit_v1_9.pdf

Donderda commented 6 years ago

Thank you for your reply! Currently, I'm trying to connect the Display to an Arduino UNO using the Repaper/gratis repo mentioned in the Project description.

According to this https://github.com/repaper/gratis/blob/master/doc/extension_board.md#pin-assignment, the already mentionet pinout (https://pinout.xyz/pinout/papirus_hat) and your hint I came up with this pin-connections:

Description     Arduino (UNO) PIN           Display PIN
3.3V                3.3V                1
SPI CLK             Digital-13          23
BUSY                Digital-7           22
PWM             Digital-5           12
RESET               Digital-6           18  (optional for wake on signal)
PANEL_ON            Digital-2           16
DISCHARGE           Digital-4           10
BORDER_CONTROL              Digital-3           8
SPI_MISO            Digital-12          21 / 19 # not sure about this
SPI_MOSI            Digital-11          19 / 21 # see pin above
FLASH_CS            Digital-9           24 / 26    # not used
EPD_CS              Digital-8           24 / 24
GND             GND             6       

SW1                             36
SW2                             37
SW3                             38
SW4                             40

Do you know whether this will work? If so, do you know which Demo-Code I could use from the RePaper-Repo?

Thank you very much :)

tvoverbeek commented 6 years ago

Agree with most of it. The PWM is not used by the displays with the Papirus HAT, no need to connect it SPI-MISO (Master In - Slave Out) should be 21 SPI-MOSI (Mater Out - Salve In) should be 19 Arduino or Raspberry Pi is the SPI Master. FLASH_CS is not needed. It is intended for the Pervasive Displays development board which as on-board flash memory which you can use to load images. Not available on the PaPiRus HAT.

As you probably know the Arduino UNO uses 5V logic. The display only tolerated 3.3V logic. The HAT has the apropiate voltage converters on-board (see the schematic) so should be no problem to connect it to a UNO.

Regarding the sketches. Suggest to start with demo in Sketches/demo. You will need to modify it since the temperature measurement will not work. The HAT has a different temperature sensor as the development board. There are also other things to check/change like the LED etc. Good luck with your project.

Donderda commented 6 years ago

Thank you for your reply. I was able to clear the screen and reading the temperature and clock👍!

But displaying stuff seems to be a Problem :(. I'm using an 2.7" Display - and it seems that the UNO does not have enough SRAM for generating a buffer of this size.

Next step: Connecting it to an ESP8266 using the Arduino Libraries and disable the WiFi-functionality for low power consumption :).

Donderda commented 5 years ago

Okay, I was able to connect it with an ESP32. But out of the blue - the Display claims that it's broken. Do you have an idea what this could cause? On an RPi the displays works perfectly...

tvoverbeek commented 5 years ago

@Donderda No, not directly. The SPI transactions in the begin() function (where the breakage detection is located) for Arduino and Raspberry Pi look identical. Do not know if the SPI library works the same on ESP32 as on RPi, since any SPI write also returns data. Maybe you have to process write and read in a single call on ESP32.

Donderda commented 5 years ago

Thank you for your reply. What makes me wonder is the fact that COG detection above those lines seem to work correctly.

The last time it worked I had to change the SPI_on, SPI_put and SPI_read command to this:

static void SPI_on() {
    SPI.end();
    pinMode(SPI_cs, OUTPUT);
    SPI.begin(SPI_clk, SPI_miso, SPI_mosi, SPI_cs);
    SPI_put(SPI_cs, 0x00);
    SPI_put(SPI_cs, 0x00);
    Delay_us(10);
}
static void SPI_put(uint8_t cs_pin, uint8_t c) {
    ESP_LOGI("SPI_out", "%x", c);
    SPI.beginTransaction(SPISettings(spiClk, MSBFIRST, SPI_MODE0)); // added
    digitalWrite(cs_pin, LOW);
    SPI.transfer(c);
    digitalWrite(cs_pin, HIGH);
    SPI.endTransaction(); // added
}

My SPI_read-function currently looks like this:

static uint8_t SPI_read(uint8_t cs_pin, const uint8_t *buffer,
        uint16_t length) {
    // CS low
    digitalWrite(cs_pin, LOW);

    //uint8_t rbuffer[4];
    uint8_t result = 0;

    // send all data
    for (uint16_t i = 0; i < length; ++i) {
        SPI.beginTransaction(SPISettings(spiClk, MSBFIRST, SPI_MODE0));
        result = SPI.transfer(*buffer++);
        ESP_LOGI("SPI_read", "%x", result);
        /* Commented out. It isn't use anyway..

         if (i < 4) {
            rbuffer[i] = result;
        }
        */
        SPI.endTransaction();
    }

    // CS high
    digitalWrite(cs_pin, HIGH);
    return result;
}

Here are some more debug infomartion I'm printing:

[I][EPaperPervasive.cpp:214] init(): PRE SPI-READ COG
[I][EPaperPervasive.cpp:863] SPI_read(): ff
[I][EPaperPervasive.cpp:863] SPI_read(): 12
[I][EPaperPervasive.cpp:863] SPI_read(): 0
[I][EPaperPervasive.cpp:863] SPI_read(): 12
[I][EPaperPervasive.cpp:225] init(): POST SPI-READ COG
[I][EPaperPervasive.cpp:830] SPI_put(): 70
[I][EPaperPervasive.cpp:830] SPI_put(): 2
[I][EPaperPervasive.cpp:830] SPI_put(): 72
[I][EPaperPervasive.cpp:830] SPI_put(): 40
[I][EPaperPervasive.cpp:231] init(): PRE SPI-READ breakage
[I][EPaperPervasive.cpp:830] SPI_put(): 70
[I][EPaperPervasive.cpp:830] SPI_put(): f
[I][EPaperPervasive.cpp:863] SPI_read(): 0
[I][EPaperPervasive.cpp:863] SPI_read(): 0
[I][EPaperPervasive.cpp:234] init(): POST SPI-READ breakage

And here is the corresponding snippet from my init-function:

    // wait for COG to become ready
    while (HIGH == digitalRead(this->EPD_Pin_BUSY)) {
        Delay_us(10);
    }
    // read the COG ID
    ESP_LOGI("SPI_read", "PRE SPI-READ COG");

    int cog_id = SPI_read(this->EPD_Pin_EPD_CS, CU8(0x71, 0x00), 2);
    cog_id = SPI_read(this->EPD_Pin_EPD_CS, CU8(0x71, 0x00), 2);

    if (0x02 != (0x0f & cog_id)) {
        this->status = EPD_UNSUPPORTED_COG;
        this->power_off();
        ESP_LOGE("EPaperPervasiv", "UNSUPPORTED COG");
        return;
    }
    ESP_LOGI("SPI_read", "POST SPI-READ COG");

    // Disable OE
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x02), 2);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x40), 2);
    // check breakage
    ESP_LOGI("SPI_read", "PRE SPI-READ breakage");
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x0f), 2);
    int broken_panel = SPI_read(this->EPD_Pin_EPD_CS, CU8(0x73, 0x00), 2);
    ESP_LOGI("SPI_read", "POST SPI-READ breakage");

    if (0x00 == (0x80 & broken_panel)) {
        this->status = EPD_PANEL_BROKEN;
        this->power_off();
        ESP_LOGE("EPaperPervasiv", "EPD_PANEL_BROKEN: %x", broken_panel);
        return;
    }
    Serial.println("AFTER EPD_PANEL_BROKEN CHECK");

Maybe @mrwastl could help? He worked on some ESP32 stuff work the repaper/gratis repository my work is based on...?

tvoverbeek commented 5 years ago

@Donderda I assume your SPI_send calls SPI_put twice in code like SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x0f), 2);. When sending the 0x70,0x0f (command header, command index) the CS goes high then low again between the 0x70 and the 0x0f. The CS should go high first after the 0x0f. When reading the COG-ID, the 0x71,0x00 is sent without the CS going high between the 0x71 and 0x00. Make sure the CS does not go high when sending the 2-byte command header, command index. For the nitty gritty see the datasheet on Pervasive Displays: http://www.pervasivedisplays.com/_literature_198794/COG_Driver_Interface_Timing_for_small_size_G2_V230

Donderda commented 5 years ago

@tvoverbeek: THANK YOU! I read the docs the whole day, dived really deep into the docs. But I overread this small detail. You saved me. 💋

Soon I will link an ESP32 example here (as soon as my source code is cleaned up 😇 )

SkyWriter commented 3 years ago

Hey @Donderda, I was wondering if you ever got to clean that code up? Or if you mind posting it as is. I'm trying to hook up the HAT to ESP32, and having some code examples would be awesome! Vielen Dank!

Donderda commented 3 years ago

Hi Ivan, sure. I will have a look at it later and link you to a repo :-)

Written with two thumbs on my mobile device...

Am 09.09.2020 um 11:10 schrieb Ivan Kasatenko notifications@github.com:

 Hey @Donderda, I was wondering if you ever got to clean that code up? Or if you mind posting it as is. I'm trying to hook up the HAT to ESP32, and having some code examples would be awesome! Thanks!

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or unsubscribe.

mlthlschr commented 3 years ago

Hey @Donderda, would be great if you could provide it here as well :-D

rlaltrello commented 1 year ago

Hello @Donderda... I''m trying to use papirus zero with ESP32... Is your repo public? Thanks!

Donderda commented 1 year ago

If I remember correctly, I changed the functions EPC_CLASS::power_off(), EPD_Class::end() and EPD_Class::init() to the following using higher delays when sending the SPI commands:


void EPD_Class::init() {
    pinMode(this->EPD_Pin_RESET, OUTPUT);
    pinMode(this->EPD_Pin_PANEL_ON, OUTPUT);
    pinMode(this->EPD_Pin_DISCHARGE, OUTPUT);
    pinMode(this->EPD_Pin_BORDER, OUTPUT);
    pinMode(this->EPD_Pin_EPD_CS, OUTPUT);
    pinMode(this->EPD_Pin_BUSY, INPUT);

    digitalWrite(this->EPD_Pin_RESET, LOW);
    digitalWrite(this->EPD_Pin_PANEL_ON, LOW);
    digitalWrite(this->EPD_Pin_DISCHARGE, LOW);
    digitalWrite(this->EPD_Pin_BORDER, LOW);
    digitalWrite(this->EPD_Pin_EPD_CS, LOW);

    // assume ok
    this->status = EPD_OK;

    // power up sequence
    digitalWrite(this->EPD_Pin_RESET, LOW);
    digitalWrite(this->EPD_Pin_PANEL_ON, LOW);
    digitalWrite(this->EPD_Pin_DISCHARGE, LOW);
    digitalWrite(this->EPD_Pin_BORDER, LOW);
    digitalWrite(this->EPD_Pin_EPD_CS, LOW);
    SPI_on();

    Delay_ms(5);
    digitalWrite(this->EPD_Pin_PANEL_ON, HIGH);
    Delay_ms(10);

    digitalWrite(this->EPD_Pin_RESET, HIGH);
    digitalWrite(this->EPD_Pin_BORDER, HIGH);
    digitalWrite(this->EPD_Pin_EPD_CS, HIGH);
    Delay_ms(5);

    digitalWrite(this->EPD_Pin_RESET, LOW);
    Delay_ms(5);

    digitalWrite(this->EPD_Pin_RESET, HIGH);
    Delay_ms(5);

    // wait for COG to become ready
    while (HIGH == digitalRead(this->EPD_Pin_BUSY)) {
        Delay_us(10);
    }
    // read the COG ID

    int cog_id = SPI_read(this->EPD_Pin_EPD_CS, CU8(0x71, 0x00), 2);
    cog_id = SPI_read(this->EPD_Pin_EPD_CS, CU8(0x71, 0x00), 2);

    if (0x02 != (0x0f & cog_id)) {
        this->status = EPD_UNSUPPORTED_COG;
        this->power_off();
        ESP_LOGE("EPaperPervasive", "UNSUPPORTED COG");
        return;
    }

    // Disable OE
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x02), 2);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x40), 2);
    // check breakage
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x0f), 2);
    int broken_panel = SPI_read(this->EPD_Pin_EPD_CS, CU8(0x73, 0x00), 2);

    if (0x00 == (0x80 & broken_panel)) {
        this->status = EPD_PANEL_BROKEN;
        this->power_off();
        ESP_LOGE("EPaperPervasive", "EPD_PANEL_BROKEN. Result: %x", broken_panel);
        return;
    }

    // power saving mode
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x0b), 2);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x02), 2);

    // channel select
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x01), 2);
    SPI_send(this->EPD_Pin_EPD_CS, this->channel_select,
            this->channel_select_length);

    // high power mode osc
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x07), 2);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0xd1), 2);

    // power setting
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x08), 2);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x02), 2);

    // Vcom level
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x09), 2);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0xc2), 2);

    // power setting
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x04), 2);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x03), 2);

    // driver latch on
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x03), 2);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x01), 2);

    // driver latch off
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x03), 2);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x00), 2);
    //Serial.println("Booted");

    Delay_ms(5);

    bool dc_ok = false;

    for (int i = 0; i < 4; ++i) {
        // charge pump positive voltage on - VGH/VDL on
        SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x05), 2);
        SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x01), 2);

        Delay_ms(240);

        // charge pump negative voltage on - VGL/VDL on
        SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x05), 2);
        SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x03), 2);

        Delay_ms(40);

        // charge pump Vcom on - Vcom driver on
        SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x05), 2);
        SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x0f), 2);

        Delay_ms(40);
        // check DC/DC
        SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x0f), 2);
        int dc_state = SPI_read(this->EPD_Pin_EPD_CS, CU8(0x73, 0x00), 2);
        if (0x40 == (0x40 & dc_state)) {
            dc_ok = true;

            break;
        }
    }
    if (!dc_ok) {
        // output enable to disable
        SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x02), 2);
        SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x40), 2);

        this->status = EPD_DC_FAILED;
        this->power_off();
        return;
    }

    SPI_off();
}

void EPD_Class::end() {

    this->nothing_frame();

    if (EPD_1_44 == this->size || EPD_2_0 == this->size) {
        this->border_dummy_line();
    }

    this->dummy_line();

    if (EPD_2_7 == this->size) {
        // only pulse border pin for 2.70" EPD
        digitalWrite(this->EPD_Pin_BORDER, LOW);
        Delay_ms(200);
        digitalWrite(this->EPD_Pin_BORDER, HIGH);
    }

    SPI_on();

    // check DC/DC
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x0f), 2);
    int dc_state = SPI_read(this->EPD_Pin_EPD_CS, CU8(0x73, 0x00), 2);
    if (0x40 != (0x40 & dc_state)) {
        this->status = EPD_DC_FAILED;
        this->power_off();
        return;
    }

    // latch reset turn on
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x03), 2);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x01), 2);

    // output enable off
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x02), 2);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x05), 2);

    // power off charge pump Vcom
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x05), 2);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x03), 2);

    // power off charge pump neg voltage
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x05), 2);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x01), 2);

    Delay_ms(240);

    // power off all charge pumps
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x05), 2);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x00), 2);

    // turn of osc
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x07), 2);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x01), 2);

    // discharge internal on
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x04), 2);
    SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x83), 2);

    Delay_ms(30);

    // discharge internal off
    //SPI_send(this->EPD_Pin_EPD_CS, CU8(0x70, 0x04), 2);
    //SPI_send(this->EPD_Pin_EPD_CS, CU8(0x72, 0x00), 2);

    power_off();
}

void EPD_Class::power_off() {

    // turn of power and all signals
    digitalWrite(this->EPD_Pin_RESET, LOW);
    digitalWrite(this->EPD_Pin_PANEL_ON, LOW);
    digitalWrite(this->EPD_Pin_BORDER, LOW);

    // ensure SPI MOSI and CLOCK are Low before CS Low
    SPI_off();
    digitalWrite(this->EPD_Pin_EPD_CS, LOW);

    // pulse discharge pin
    digitalWrite(this->EPD_Pin_DISCHARGE, HIGH);
    Delay_ms(150);
    digitalWrite(this->EPD_Pin_DISCHARGE, LOW);
}

It's been a long time. Please let me know if this changes fix them.

Donderda commented 1 year ago

Here's a repo of the project I used the display for: https://github.com/Donderda/doorsign/

Feel free to check the code. It's not well documented, but maybe it helps @rlaltrello