Jump to content
43oh

E-Ink display with bluetooth


Recommended Posts

Hello!

 

The target of my project is to build an E-Ink display controlled via bluetooth (picture uploading, changing displayed picture) using 4.4" E-Ink display http://www.densipaper.com/product/et044as013-mpkit-01

 

I am not new to programming, but when it comes to microcontrollers I am a complete beginner. So I've decided to try and develop something first with the TI Launchpad and 2.7" display http://www.densipaper.com/product/em027as012-tikit-01 and then port it to a bigger one.

 

So far, I have written my own android app using MIT app inventor 2 and I am able to send single bytes via bluetooth to my bluetooth module KAmodBTM222 http://www.kamami.pl/index.php?productID=137699 which is connected to the launchpad. The code in MSP430 is modified default EPD code https://github.com/wbicu/EPD-Extension-Board-v1.11

#include "Pervasive_Displays_small_EPD.h"
#include "image_data.h"

void init(){
    BCSCTL1 = CALBC1_16MHZ;
    DCOCTL = CALDCO_16MHZ;

    P1SEL = BIT1 | BIT2 ;
    P1SEL2 = BIT1 | BIT2;
    P1DIR |= 0x01;
    P1SEL = BIT1 | BIT2 ;
    P1SEL2 = BIT1 | BIT2;
    UCA0CTL1 = UCSWRST;
    UCA0CTL1 |= UCSSEL_2;                    
    UCA0BR0 = 131;                          
    UCA0BR1 = 6;                              
    										
    UCA0MCTL = UCBRS1 + UCBRS0;               
    UCA0CTL1 &= ~UCSWRST;
    IE2 |= UCA0RXIE;
    __bis_SR_register(GIE);
}

int main(void) {
	WDTCTL = WDTPW + WDTHOLD;
	init();

	for(; {
		EPD_display_from_pointer(EPD_270,(uint8_t *)&image_array_270_2,(uint8_t *)&image_array_270_1);
		init();
		delay_ms(9000);

		EPD_display_from_pointer(EPD_270,(uint8_t *)&image_array_270_1,(uint8_t *)&image_array_270_2);
		init();
		delay_ms(9000);
	}
}

#pragma vector=USCIAB0RX_VECTOR
__interrupt void USCI0RX_ISR(void){
		if (UCA0RXBUF == 'A'){
			P1OUT ^= 0x01;
		}
		else {
			P1OUT ^= 0x01;
		}

		IFG2 = IFG2 & 0x0A;
}

So that's what I was able to develop by myself - blink the LED via bluetooth while images are changing on the display. Now I've got a few questions to you guys! :) I wasn't able to find answers although I have searched thoroughly.

 

1. What is the proper way to handle RX input in the code? I want to be able to receive a whole picture 264 x 176 via bluetooth and display it on my E-Ink display. Right now I am able to handle single bytes and I've got a feeling I am doing this wrong...

2. Are there any similar projects for 4.4" or any EPD displays? If so, please post some links, maybe I can be inspired by them. :)

3. Will my MSP430G2553 be even able to handle both code and 400 x 300 pictures with its 16kb memory?

4. I have also thought about external memory. Would FLASH/FRAM/EEPROM be useful in this project?

5. How do you read/write to external memory? For example, how to write an array of chars and read it later?

 

I will be posting any progress and will keep this thread updated. I am excited to read your responses!

 

Greetings

Wbicu

post-38009-0-61380700-1410979734_thumb.jpg

Link to post
Share on other sites

Very interesting project Wbicu,

 

There are of course many ways to code, this is just some of my opinions.

 

1) A 264x176 image is 46464 bits, or 5,808 bytes. I'm not sure how the e-ink display is setup, it may be adequate to simply pass the data through.

 

You're going about RX input the right way with an interrupt, If you could you may be able to immediately place the data onto your SPI buss, this would involve re-writing the function of EPD_display_from_pointer to accept byte by byte data instead of a block of data.

 

If you require buffering the image, might have advantages. (if your display needs to be updated as a whole this would be required)

then you need somewhere to store 5,808kb of data. the G2553 does not have enough RAM for this, infact you have to get quite beefy before you're given that much RAM. Flash endurance is minimum of around 100,000 write cycles. this is might be sufficient for your application.

Have some numbers:

Update every second: lifetime 1.6 days

Update every minute: lifetime 70 days

Update every 10 minutes: 2 years...etc.

 

If these numbers are too small, then you'll need to find someway of adding extra RAM to your system, see 5.

 

2) I haven't seen any.

 

3) Let's crunch some numbers. 300x400 = 120,000 bits, or 15,000 bytes.

The MSP430G2553, Flash: 16,384 bytes.

 

So technically yes. I'm not sure how large those EPD_display_from_pointer functions are. at any rate you'll be having to squeeze your code in. Might be a fun challenge.

 

4) External memory is certainly an option, Assuming that you're project will be powered then SRAM would be the fastest. If you intend to power down most of your system to conserve power then maybe EEPROM/FLASH. FRAM is more expensive, but will provide you with the best of both worlds, low power, data preservation. This is really your choice.

 

5) External memory access will depend on it's interface.

Serial interfaces like SPI/I2C are likely what you'll need to use with the G2553, since it doesn't have enough pins for parallel. Writing/reading depends on the IC you select. typically you send a command+address byte (read/write) then you can send bytes at it to store. or if reading it will send bytes back to you.

 

 

Goodluck :)

Link to post
Share on other sites

If that is the same as one of the Pervasive Displays adapter boards, then it may have 8M bit serial flash on the board.

 

I have not had a lot of success at finding detailed specifications on how to drive these displays,

but I see that Pervasive Displays now has kits for a few more platforms, so may be some more example code 

and/or documentation out now.

 

Looking at the driver code it looks like one row of pixels is sent at a time, but what timing constraints there may be (i.e. can you send a row, then wait while another is transmitted or loaded from flash, then send that row, ...).

 

One of the Pervasive Displays demos gives an example of writing to flash. -   http://repaper.org/ github repository  flash_loader.ino

 

You might also consider what sort of images you want to display, e.g. can you get significant benefits from compression (enough to offset the extra code size and the speed penalty of decompressing).  Compression could be as simple as run length encoding, or storing a text image as characters and generating bitmaps on the fly, or more elaborate....

Link to post
Share on other sites

For EPD displays, Aimonen wrote some instructions on interfacing with the cheap ED060SC4 (http://www.essentialscrap.com/eink/software.html). He discusses different rendering mode and some workarounds for microcontrollers due to their lower clock speed and RAM space. Though keep in mind, he's using a STM32L, which has a lot more RAM space and pins than a G2553 in general.

Link to post
Share on other sites

Hi! I am here with some updates and more questions!

 

1. I have managed to write an android app with help of this video series: youtube.com/watch?v=OTQHZ16q0Ik and now I am able to send multiple bytes via bluetooth. It still needs some fine tuning, when it's done I will post the code.

 

2. Images in the original code https://github.com/wbicu/EPD-Extension-Board-v1.11 are const in a separate file (image_data.c), I wanted to move them to my main.c file and for some reason it doesn't work anymore! What I've done is:

-delete #includes

-move #defines to main.c

-copy-paste image data to main() function

-remove const modifier

 

So now code looks like this:

#define USE_EPD144 0 /**< 1.44 inch PDi EPD */
#define USE_EPD200 1 /**< 2 inch PDi EPD */
#define USE_EPD270 2 /**< 2.7 inch PDi EPD */

#define USE_EPD_Type USE_EPD270

#include "Pervasive_Displays_small_EPD.h"

void init(){
    BCSCTL1 = CALBC1_16MHZ;
    DCOCTL = CALDCO_16MHZ;

    P1SEL = BIT1 | BIT2 ;
    P1SEL2 = BIT1 | BIT2;
    P1DIR |= 0x41;
    P1SEL = BIT1 | BIT2 ;
    P1SEL2 = BIT1 | BIT2;
    UCA0CTL1 = UCSWRST;
    UCA0CTL1 |= UCSSEL_2;                     

    //UCA0BR0 = 131;                      
    //UCA0BR1 = 6;                        
    										  
    UCA0MCTL = UCBRS1 + UCBRS0;             
    UCA0CTL1 &= ~UCSWRST;
    IE2 |= UCA0RXIE;
    __bis_SR_register(GIE);
}

int main(void) {
	WDTCTL = WDTPW + WDTHOLD;
	init();
	unsigned char image_array_270_1[] = {<image_data>};
	unsigned char image_array_270_2[] = {<image_data>};
	for(; {
		EPD_display_from_pointer(EPD_270,(uint8_t *)&image_array_270_2,(uint8_t *)&image_array_270_1);
		init();
		delay_ms(9000);
		EPD_display_from_pointer(EPD_270,(uint8_t *)&image_array_270_1,(uint8_t *)&image_array_270_2);
		init();
		delay_ms(9000);
	}
}

#pragma vector=USCIAB0RX_VECTOR
__interrupt void USCI0RX_ISR(void){
        if (UCA0RXBUF == 'A'){
            P1OUT ^= 0x01;
        }
        else {
            P1OUT ^= 0x01;
        }

        IFG2 = IFG2 & 0x0A;
}

But it doesn't work anymore. I haven't done anything apart from those steps, compilation process is ok, but images don't change. What may be the reason?

 

Greetings

Wbicu

Link to post
Share on other sites

Hey Wbicu,

 

It is bad practice to define varibale data with initilisation like that in embedded systems. If you think about whats going on under the hood. The comipler needs to store a constant copy of your data array in FLASH. then when it gets to that line of code it needs to jump to a routine to copy it to RAM.

 

I understand that you're just trying out some code idea right now.

 

But there is a simple and maybe to obvious to notice. You're G2552 only has 512 BYTES! of RAM.

 

Since you are attempting to assign ~10kb worth of arrays with your main loop, the compiler will attempt to put these on the stack. So the compiler will not throw an error. however when it comes to run the system instantly runs out of RAM and your program halts. (most likely some for of memory write error) Or may continue running but just receive junk when it tries to read the invalid RAM addresses.

 

<tl;dr>

You cannot buffer the image in the G2553's RAM as it 10x bigger (5kb vs 512bytes).

Link to post
Share on other sites

Hey greeeg, thank you very much for your reply! It explained me a lot!

 

Right now I am trying to send some image data via bluetooth and display it. My idea is to send bytes in from android app in small time intervals, e.g. every 1/500 second, so that UART buffer in MSP430 can be read and emptied before receiving another byte. Would that work as intended?

 

And what would be the correct way to save input to the char array? My initial idea was to do something like this:

int main(void) {
    WDTCTL = WDTPW + WDTHOLD;
    init();
    int i = 0;
    bool ready = false;

    for(; {
	if (ready)){
	    //display images
	}
    }
}

#pragma vector=USCIAB0RX_VECTOR
__interrupt void USCI0RX_ISR(void){
	if (i < 5808) {
	    image_array_270_1[i] = UCA0RXBUF;
	    i++;
	} 
	if (i == 5808) {
	    ready = true;
	}
	IFG2 = IFG2 & 0x0A;
}

but I don't know how to do it, if I cannot initialize char array in the main loop, and array provided in image_data.c is const.

 

By the way, are there any good tutorials on MSP430 programming with condensed content? I have already found Davies - "MSP430 Microcontroller Basics" and pdf with user's guide slau144j.pdf, but they are all but condensed.

 

Thank you in advance!

 

Greetings

Wbicu

Link to post
Share on other sites
As greeg noted, you have to make your array much smaller.
(i < 5808) is not going to work if only have 512 bytes of RAM.
 
Look at handling the image 1 row at a time, that is only 33 bytes for the 2.7" display - and that the micro should be able to handle.
(Might wind up doing a couple of rows - read one into buffer while outputing the previous one, e.g.)
But start simple, with something like 1 row.
Can receive it a few rows at a time and stuff each row it into the flash on the EPD board if you need to.
 
Alternative is, if you want to do a whole image in memory at one time - look at parts with more RAM.
e.g., F5529 has 8 KB RAM (which could hold that 5808 element array, not enough for your eventual target of 16 KB image, but gives more room for experiment or for buffering before write image to flash)
FR5969 only has 2KB RAM, but has 64KB FRAM, (can be used similar to RAM, but slower and will not stand as many writes,
but nonvolatile like flash and doesn't take as much power as flash)

Or just move to an ARM processor where you have plenty of RAM (if you can find one that fits power, etc. requirements).

 
As far as synchronyzing with the android app - I haven't used bluetooth, but I would look for a way to send back a busy signal.
(either use x-on/x-off, or send back an acknowledgement or checksum every x bytes, or see if the bluetooth provides buffering (RTS/CTS), etc.)  Just blindly sending bytes is a recipe to lose bytes.
Also consider what the bluetooth packet size is.  (If bluetooth provides a guaranteed bytestream, then you can ignore this, but you also don't have to worry about sending one byte every so often.  Just send until out of bytes or the buffer is full.)
 

How often will you want to update the image?  

 

This might give some ideas toward question posed above (I wasn't quite clear what part was giving problems).

(Not tested, just writing on the fly.)

#define BUFFER_SIZE 33

unsigned char image_array_270_1[BUFFER_SIZE ];

volatile unsigned i = 0;

int main(void) {
   WDTCTL = WDTPW + WDTHOLD;
   init();

   for(; {
    if (i == BUFFER_SIZE)){
     //display images
    }
}
}

#pragma vector=USCIAB0RX_VECTOR
__interrupt void USCI0RX_ISR(void){
    if (i < BUFFER_SIZE ) {
     image_array_270_1[i++] = UCA0RXBUF;
    }
    IFG2 = IFG2 & 0x0A;
}
Link to post
Share on other sites

I just found this fork of the Pervasive Displays ePaper library which may be helpful.  It says it reduces screen memory need down to 1 line of display (33 bytes on the 2.7" display).  Evidently it has been tested on Arduino, but not on TI MSP430.  (I haven't looked at the code or tested it.).

 

https://github.com/brodykenrick/gratis

Link to post
Share on other sites
  • 1 month later...

Hello!

 

I have written line by line updating and done a little refactoring, code is here: https://github.com/wbicu/code-MSP430G2553

 

So right now I am able to send a picture line by line to the display. It's good but I was wondering how could I make it better - I would like to upload a whole picture at once. I have followed igor's advice and ordered a launchpad with more memory - FR5969.

 

I have expected that I would have to change output pins in code, but there were more errors that I don't know how to resolve.

#20 identifier "IFG2" is undefined
#20 identifier "P1SEL2" is undefined
#20 identifier "P1SEL" is undefined
#20 identifier "CALDCO_16MHZ" is undefined
#20 identifier "DCOCTL" is undefined
#20 identifier "UCA0RXIE" is undefined
#20 identifier "IE2" is undefined
#20 identifier "UCA0MCTL" is undefined
#20 identifier "IFG2" is undefined
#20 identifier "P1SEL2" is undefined
#20 identifier "BCSCTL1" is undefined
#20 identifier "UCB0TXIFG" is undefined	
#20 identifier "CALBC1_16MHZ" is undefined
#20 identifier "P1SEL2" is undefined
#20 identifier "P1SEL" is undefined
#20 identifier "P1SEL" is undefined
#20 identifier "P2SEL" is undefined
#20 identifier "P2SEL2" is undefined

How to resolve these problems (eg. undefined CALDCO_16MHZ or UCA0RXIE) so that the program works with new processor? What are general tips for rewriting a program to another processor?

Link to post
Share on other sites
I can't offer much help on porting between MSP430 processors, I would look at the source code to whatever driver library exists for the MSP430 - see how they handled the various processors.  That, or look at the source code to Energia, in the MSP430 section, to see how they did whatever function you are interested in.  (Probably plenty of other sources of examples - those are just a couple that are likely to have a wide variety of functions implemented.)

 

Can you post some kind of a diff or commentary pointing out what you changed in the driver library?

(Since your repository isn't a fork off of another repository, it is a bit difficult to be sure what changes you have made.)

 

I have been playing with the "gratis" (Arduino/Energia) driver for the ePaper displays quite a bit recently.  (Have it mostly working on the Tiva/Stellaris launchpad.)  In that driver there are functions that let you feed the display using a callback function to fill a single line buffer.  (That is how they display images stored in the flash.)

 

The regular drivers use a whole sequence of operations to change the display image (on V110 COGs at least).

They write an inverse of the current image, then make everything white, then write inverse of new, then write new.

It looks like your version doesn't do that (or maybe I am just missing it)?  What are the implications of not doing that?

(In terms of panel life, display clarity, etc.)  I have not found a lot about the details of how the drivers function, why they do what they do, or details on the display panels, so I am curious what specifications others have found about this.

(I assume there is probably a good reason that the examples do what they do, I just don't know what it is, and what the trade-offs are if you don't do that.)

 

In your application, how often will you be updating the display?  

If you don't rewrite it too often, you might be able to store several lines at a time in FRAM, then display them in a bunch.

(So takes fewer rewrites to update whole screen).
Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...