Display - cleanup and better "example"

* Moved test pattern to main program
* Use the touchscreen to affect display brightness
This commit is contained in:
Emmanuel BENOîT 2012-11-05 09:10:58 +01:00
parent f8a5204ded
commit 299adc5db9
6 changed files with 350 additions and 168 deletions

View file

@ -1,6 +1,7 @@
PROJECT = BitBangDisplay
PRJ_C_SRC = main.c \
ssd1963.c
ssd1963.c \
xpt2046.c
##############################################################################
# Build global options

View file

@ -2,6 +2,7 @@
#include "hal.h"
#include "ssd1963.h"
#include "xpt2046.h"
// This is called when the board boots up with the user button pressed. The
// idea is to enter this mode if the wrong SPI device has been used and flashing
@ -16,8 +17,88 @@ static void lockdown( void )
}
void display_off( void );
void display_test_pattern( u16 start_mask );
static void displayTestPattern( u16 start_mask )
{
_ssd1963_clear_cs;
ssd1963StartWriting( 0 , 0 );
int i;
for ( i = 0 ; i < SSD1963_SCR_HEIGHT ; i ++ ) {
u16 j , c = start_mask;
for ( j = 0 ; j < SSD1963_SCR_WIDTH ; j ++ ) {
if ( ( j & 7 ) == 7 ) {
c >>= 1;
if ( !c ) {
c = 0x8000;
}
}
ssd1963WriteData( c );
}
}
_ssd1963_set_cs;
}
static int tryReading;
static void onTouchscreenIRQ( EXTDriver * driver __attribute__((unused)) ,
expchannel_t channel __attribute__((unused)) )
{
tryReading = TRUE;
}
#define PORT(P) \
case (int) GPIO##P : \
port = EXT_MODE_GPIO##P; \
break;
static void initIRQs( void )
{
EXTConfig config;
u32 i;
for ( i = 0 ; i < EXT_MAX_CHANNELS ; i ++ ) {
if ( i == XPT2046_IRQ_PAD ) {
int port;
switch ( (int) XPT2046_IRQ_PORT ) {
default: PORT(A); PORT(B);
PORT(C); PORT(D); PORT(E);
PORT(F); PORT(G); PORT(H);
}
config.channels[ i ].mode = EXT_CH_MODE_FALLING_EDGE
| EXT_CH_MODE_AUTOSTART | port;
config.channels[ i ].cb = &onTouchscreenIRQ;
} else {
config.channels[ i ].mode = EXT_CH_MODE_DISABLED;
config.channels[ i ].cb = NULL;
}
}
extStart( &EXTD1 , &config );
}
static void checkTouchScreen( void )
{
int x , y;
if ( ! xpt2046GetAverageCoordinates( &x , &y , 3 ) ) {
return;
}
x >>= 8;
_ssd1963_clear_cs;
ssd1963WriteCommand( SSD1963_CMD_SET_POST_PROC );
ssd1963WriteData( 0x40 );
ssd1963WriteData( x );
ssd1963WriteData( 0x40 );
if ( y > 2048 ) {
ssd1963WriteData( 0 );
} else {
ssd1963WriteData( 1 );
}
_ssd1963_set_cs;
}
int main( void )
{
@ -27,14 +108,18 @@ int main( void )
lockdown( );
}
xpt2046Init( );
initIRQs();
ssd1963Init( );
int i = 0;
while (TRUE) {
display_test_pattern( 1 << i );
displayTestPattern( 1 << i );
chThdSleep(100);
i = ( i + 1 ) % 16;
if ( palReadPad( GPIOA , GPIOA_BUTTON ) ) {
display_off( );
if ( tryReading ) {
checkTouchScreen( );
tryReading = FALSE;
}
}
}

View file

@ -4,66 +4,43 @@
#include "ssd1963.h"
#define _command 0x40
#define _delay 0x80
#define _ndata(x) ( (u8)( (x) & 0x3f) )
static const u8 _ssd1963_reset_sequence[ ] = {
/*
* PLL configuration
* -----------------
*
* PLL = input * ( P1 + 1 ) / ( P2 + 1 )
* (P1 : 8 bits ; P2 : 4 bits; P3 : junk)
*
* 1/ The ITDB02-4.3 board has a 12MHz crystal (which contradicts the
* SSD1963 datasheet - wtf?).
* 2/ The SSD1963 must be clocked at *at most* 110MHz.
* 3/ Also, 250MHz < input * ( P1 + 1 ) < 800MHz
* 4/ The STM32F4 can communicate at 50MHz.
* 5/ The SSD1963 accepts PLL/2 accesses per second.
*
* 100MHz = 12MHz * ( P1 + 1 ) / ( P2 + 1 )
* => P1 = 49, P2 = 5
* 100MHz = 10MHz * ( P1 + 1 ) / ( P2 + 1 )
* => P1 = 19, P2 = 1
*/
_command | _ndata(3) ,
// PLL configuration
SSD1963_COMMAND | SSD1963_DATA(3) ,
SSD1963_CMD_SET_PLL_MN ,
0x31 , 0x05 , 0x04 ,
// 0x1e , 0x02 , 0x04 ,
0x1e , 0x02 , 0x04 ,
// Enable PLL and wait until it's stable
_command | _delay | _ndata(1) ,
SSD1963_COMMAND | SSD1963_DELAY | SSD1963_DATA(1) ,
SSD1963_CMD_SET_PLL , 1 ,
1 ,
// Fully enable PLL
_command | _ndata(1) ,
SSD1963_COMMAND | SSD1963_DATA(1) ,
SSD1963_CMD_SET_PLL , 3 ,
};
static const u8 _ssd1963_init_sequence[ ] = {
// Software reset
_command | _delay ,
SSD1963_COMMAND | SSD1963_DELAY ,
SSD1963_CMD_SOFT_RESET ,
5 ,
/* Pixel clock: screw finesse, crank that up to the max */
_command | _ndata( 3 ) | _delay ,
// Pixel clock: screw finesse, crank that up to the max
SSD1963_COMMAND | SSD1963_DATA( 3 ) | SSD1963_DELAY ,
SSD1963_CMD_SET_LSHIFT_FREQ ,
0x07 , 0xff , 0xff ,
15 ,
// Use 565 RGB format
_command | _delay | _ndata(1) ,
SSD1963_COMMAND | SSD1963_DELAY | SSD1963_DATA(1) ,
SSD1963_CMD_SET_PIXEL_FORMAT ,
0x03 ,
5 ,
// Setup LCD panel
_command | _ndata( 7 ) ,
SSD1963_COMMAND | SSD1963_DATA( 7 ) ,
SSD1963_CMD_SET_LCD_MODE ,
0x20 , // Data width 24-bit, FRC and dithering disabled
// Data latch on falling edge
@ -78,7 +55,7 @@ static const u8 _ssd1963_init_sequence[ ] = {
( ( SSD1963_SCR_HEIGHT - 1 ) & 0xff ) ,
0x00 , // Ignored (serial interface)
_command | _ndata( 8 ) ,
SSD1963_COMMAND | SSD1963_DATA( 8 ) ,
SSD1963_CMD_SET_HORI_PERIOD ,
0x02 , 0x13 , // Total period (PCLK)
0x00 , 0x08 , // Non-display period
@ -86,86 +63,35 @@ static const u8 _ssd1963_init_sequence[ ] = {
0x00 , 0x02 , // Start location (PCLK)
0x00 , // Ignored (serial interfaces)
_command | _ndata( 7 ) ,
SSD1963_COMMAND | SSD1963_DATA( 7 ) ,
SSD1963_CMD_SET_VERT_PERIOD ,
0x01 , 0x20 , // Vertical total period
0x00 , 0x04 , // Non-display period
0x0c , // Sync pulse width
0x00 , 0x02 , // Start location
_command | _ndata(1) ,
0x36 ,
0x00 ,
SSD1963_COMMAND | SSD1963_DATA(1) ,
SSD1963_CMD_SET_ADDRESS_MODE ,
0x00 , // Top to bottom, left to right, no reversing,
// RGB framebuffer, LCD l-to-r refresh, no
// flipping
_command ,
SSD1963_CMD_SET_DISPLAY_ON ,
_command | _ndata(6) ,
SSD1963_CMD_SET_PWM_CONF ,
0x06 , 0xf0 , 0x01 , 0xf0 , 0 , 0 ,
_command | _ndata(1) ,
0xd0 ,
0x0d ,
_command | _ndata(1) , SSD1963_CMD_SET_GAMMA_CURVE, 1 ,
//_command | SSD1963_CMD_ENTER_NORMAL_MODE ,
//_command | SSD1963_CMD_EXIT_INVERT_MODE ,
_command , SSD1963_CMD_EXIT_IDLE_MODE ,
/*
// TE signal is not connected on the ITDB02-4.3
SSD1963_COMMAND ,
SSD1963_CMD_SET_TEAR_OFF ,
*/
// Set standard gamma curve
SSD1963_COMMAND | SSD1963_DATA(1) ,
SSD1963_CMD_SET_GAMMA_CURVE ,
1 ,
// Enable display
SSD1963_COMMAND ,
SSD1963_CMD_SET_DISPLAY_ON ,
};
#define _ssd1963_wcmd(command) \
do { \
_ssd1963_clear_rs; \
_ssd1963_write( command ); \
_ssd1963_clear_wr; \
_ssd1963_set_wr; \
} while ( FALSE )
#define _ssd1963_wdata(data) \
do { \
_ssd1963_set_rs; \
_ssd1963_write( data ); \
_ssd1963_clear_wr; \
_ssd1963_set_wr; \
} while ( FALSE )
void _ssd1963_run_sequence( const u8 * sequence , u32 size )
{
_ssd1963_clear_cs;
u32 addr = 0;
u8 has_command = 0 , has_data = 0 , has_delay = 0;
while ( addr < size ) {
if ( has_command ) {
u16 command = sequence[ addr ++ ];
_ssd1963_wcmd( command );
has_command = 0;
} else if ( has_data ) {
u16 data = sequence[ addr ++ ];
_ssd1963_wdata( data );
has_data --;
} else if ( has_delay ) {
u8 delay = sequence[ addr ++ ];
chThdSleep( delay );
has_delay = 0;
} else {
u8 value = sequence[ addr ++ ];
has_command = ( ( value & _command ) == _command );
has_delay = ( ( value & _delay ) == _delay );
has_data = value & ~( _command | _delay );
}
}
_ssd1963_set_cs;
}
void _ssd1963_reset_chip( void )
static void _ssd1963_reset_chip( void )
{
// Select and reset the chip
_ssd1963_set_reset;
@ -178,29 +104,12 @@ void _ssd1963_reset_chip( void )
chThdSleep( 100 );
// Run the PLL init sequence
_ssd1963_run_sequence( _ssd1963_reset_sequence ,
ssd1963RunSequence( _ssd1963_reset_sequence ,
sizeof( _ssd1963_reset_sequence ) );
}
void _ssd1963_set_pos( u32 x , u32 y )
{
_ssd1963_wcmd( 0x2a );
_ssd1963_wdata( x >> 8 );
_ssd1963_wdata( x & 0xff );
_ssd1963_wdata( ( ( SSD1963_SCR_WIDTH - 1 ) >> 8 ) & 0xff );
_ssd1963_wdata( ( SSD1963_SCR_WIDTH - 1 ) & 0xff );
_ssd1963_wcmd( 0x2b );
_ssd1963_wdata( y >> 8 );
_ssd1963_wdata( y & 0xff );
_ssd1963_wdata( ( ( SSD1963_SCR_HEIGHT - 1 ) >> 8 ) & 0xff );
_ssd1963_wdata( ( SSD1963_SCR_HEIGHT - 1 ) & 0xff );
_ssd1963_wcmd( 0x2c );
}
void ssd1963Init( void )
{
_ssd1963_init_gpio( PAL_STM32_OSPEED_LOWEST );
@ -209,58 +118,63 @@ void ssd1963Init( void )
chThdSleep( 10 );
_ssd1963_init_gpio( PAL_STM32_OSPEED_HIGHEST );
_ssd1963_run_sequence( _ssd1963_init_sequence ,
ssd1963RunSequence( _ssd1963_init_sequence ,
sizeof( _ssd1963_init_sequence ) );
_ssd1963_clear_cs;
_ssd1963_set_pos( 0 , 0 );
ssd1963StartWriting( 0 , 0 );
int i;
for ( i = 0 ; i < SSD1963_SCR_HEIGHT ; i ++ ) {
u16 j , c = 0x8000;
for ( j = 0 ; j < SSD1963_SCR_WIDTH ; j ++ ) {
if ( ( j & 7 ) == 7 ) {
c >>= 1;
if ( !c ) {
c = 0x8000;
}
}
_ssd1963_wdata( c );
}
for ( i = 0 ; i < SSD1963_SCR_HEIGHT * SSD1963_SCR_WIDTH ; i ++ ) {
ssd1963WriteData( 0 );
}
_ssd1963_set_cs;
}
void display_test_pattern( u16 start_mask )
void ssd1963RunSequence( const u8 * sequence , u32 size )
{
_ssd1963_clear_cs;
_ssd1963_set_pos( 0 , 0 );
int i;
for ( i = 0 ; i < SSD1963_SCR_HEIGHT ; i ++ ) {
u16 j , c = start_mask;
for ( j = 0 ; j < SSD1963_SCR_WIDTH ; j ++ ) {
if ( ( j & 7 ) == 7 ) {
c >>= 1;
if ( !c ) {
c = 0x8000;
}
}
_ssd1963_wdata( c );
}
}
_ssd1963_set_cs;
}
void display_off( void )
{
static int display = 1;
display = !display;
_ssd1963_clear_cs;
if ( display ) {
_ssd1963_wcmd( SSD1963_CMD_SET_DISPLAY_ON );
u32 addr = 0;
u8 has_command = 0 , has_data = 0 , has_delay = 0;
while ( addr < size ) {
if ( has_command ) {
u16 command = sequence[ addr ++ ];
ssd1963WriteCommand( command );
has_command = 0;
} else if ( has_data ) {
u16 data = sequence[ addr ++ ];
ssd1963WriteData( data );
has_data --;
} else if ( has_delay ) {
u8 delay = sequence[ addr ++ ];
chThdSleep( delay );
has_delay = 0;
} else {
_ssd1963_wcmd( SSD1963_CMD_SET_DISPLAY_OFF );
u8 value = sequence[ addr ++ ];
has_command = ( ( value & SSD1963_COMMAND )
== SSD1963_COMMAND );
has_delay = ( ( value & SSD1963_DELAY )
== SSD1963_DELAY );
has_data = value & ~( SSD1963_COMMAND | SSD1963_DELAY );
}
}
_ssd1963_set_cs;
}
void ssd1963StartWriting( u32 x , u32 y )
{
ssd1963WriteCommand( 0x2a );
ssd1963WriteData( x >> 8 );
ssd1963WriteData( x & 0xff );
ssd1963WriteData( ( ( SSD1963_SCR_WIDTH - 1 ) >> 8 ) & 0xff );
ssd1963WriteData( ( SSD1963_SCR_WIDTH - 1 ) & 0xff );
ssd1963WriteCommand( 0x2b );
ssd1963WriteData( y >> 8 );
ssd1963WriteData( y & 0xff );
ssd1963WriteData( ( ( SSD1963_SCR_HEIGHT - 1 ) >> 8 ) & 0xff );
ssd1963WriteData( ( SSD1963_SCR_HEIGHT - 1 ) & 0xff );
ssd1963WriteCommand( 0x2c );
}

View file

@ -21,6 +21,7 @@
#define SSD1963_CMD_SET_DISPLAY_OFF 0x28
#define SSD1963_CMD_SET_DISPLAY_ON 0x29
#define SSD1963_CMD_SET_TEAR_OFF 0x34
#define SSD1963_CMD_SET_ADDRESS_MODE 0x36
#define SSD1963_CMD_EXIT_IDLE_MODE 0x38
#define SSD1963_CMD_SET_LCD_MODE 0xb0
#define SSD1963_CMD_SET_HORI_PERIOD 0xb4
@ -34,15 +35,40 @@
#define SSD1963_CMD_SET_PIXEL_FORMAT 0xf0
// Control and data ports
#define ssd1963Ctrl (*((__IO u16 *) 0x60000000))
#define ssd1963Data (*((__IO u16 *) 0x60020000))
// Command sequences
#define SSD1963_DATA(x) ( (u8)( (x) & 0x3f) )
#define SSD1963_COMMAND 0x40
#define SSD1963_DELAY 0x80
// Initialise the display controller
void ssd1963Init( void );
// Execute a sequence of commands from the specified address
void ssd1963RunSequence( const u8 * sequence , u32 size );
// Write a command to the SSD1963
#define ssd1963WriteCommand(command) \
do { \
_ssd1963_clear_rs; \
_ssd1963_write( command ); \
_ssd1963_clear_wr; \
_ssd1963_set_wr; \
} while ( FALSE )
// Write data to the SSD1963
#define ssd1963WriteData(data) \
do { \
_ssd1963_set_rs; \
_ssd1963_write( data ); \
_ssd1963_clear_wr; \
_ssd1963_set_wr; \
} while ( FALSE )
// Start writing data at the specified location
void ssd1963StartWriting( u32 x , u32 y );
#endif // _H_SSD1963_STM32F4

125
07-BitBangDisplay/xpt2046.c Normal file
View file

@ -0,0 +1,125 @@
#include "ch.h"
#include "hal.h"
#include "xpt2046.h"
static const SPIConfig _xpt2046_spi_config = {
NULL ,
XPT2046_NSS_PORT ,
XPT2046_NSS_PAD ,
SPI_CR1_BR_2 | SPI_CR1_BR_1 | SPI_CR1_BR_0
};
u16 _xpt2046_get_reading( u8 control )
{
u8 tData[3] = { control , 0 , 0 };
u8 rData[3] = { 0 , 0 , 0 };
#if SPI_USE_MUTUAL_EXCLUSION
spiAcquireBus( &( XPT2046_SPI_DRIVER ) );
#endif
palClearPad( XPT2046_NSS_PORT , XPT2046_NSS_PAD );
spiExchange( &( XPT2046_SPI_DRIVER ) , 3 , tData , rData );
palSetPad( XPT2046_NSS_PORT , XPT2046_NSS_PAD );
#if SPI_USE_MUTUAL_EXCLUSION
spiReleaseBus( &( XPT2046_SPI_DRIVER ) );
#endif
if ( ( control & 0x08 ) == 0 ) {
return ( rData[1] << 5 ) | ( rData[2] >> 3 );
}
return ( rData[1] << 4 ) | ( rData[2] >> 4 );
}
void xpt2046Init( void )
{
// Initialise SPI
spiStart( &( XPT2046_SPI_DRIVER ) , &_xpt2046_spi_config );
// NSS signal
palSetPadMode( XPT2046_NSS_PORT , XPT2046_NSS_PAD ,
PAL_MODE_OUTPUT_PUSHPULL );
palSetPad( XPT2046_NSS_PORT , XPT2046_NSS_PAD );
// Main SPI signals
palSetPadMode( XPT2046_CLK_PORT , XPT2046_CLK_PAD ,
PAL_MODE_ALTERNATE( 5 ) );
palSetPadMode( XPT2046_DIN_PORT , XPT2046_DIN_PAD ,
PAL_MODE_ALTERNATE( 5 ) );
palSetPadMode( XPT2046_DOUT_PORT , XPT2046_DOUT_PAD ,
PAL_MODE_ALTERNATE( 5 ) );
// PENIRQ signal
palSetPadMode( XPT2046_IRQ_PORT , XPT2046_IRQ_PAD , PAL_MODE_INPUT );
// Read a sample, leaving PENIRQ active
_xpt2046_get_reading( 0x90 );
}
int xpt2046GetCoordinates( int * pX , int * pY )
{
int i;
int allX[ 7 ] , allY[ 7 ];
_xpt2046_get_reading( 0xd1 );
_xpt2046_get_reading( 0x91 );
for ( i = 0 ; i < 7 ; i ++ ) {
allX[ i ] = _xpt2046_get_reading( 0xd1 );
allY[ i ] = _xpt2046_get_reading( 0x91 );
}
int j;
for ( i = 0 ; i < 4 ; i ++ ) {
for ( j = i ; j < 7 ; j ++ ) {
int temp = allX[ i ];
if ( temp > allX[ j ] ) {
allX[ i ] = allX[ j ];
allX[ j ] = temp;
}
temp = allY[ i ];
if ( temp > allY[ j ] ) {
allY[ i ] = allY[ j ];
allY[ j ] = temp;
}
}
}
_xpt2046_get_reading( 0x90 );
if ( palReadPad( XPT2046_IRQ_PORT , XPT2046_IRQ_PAD ) ) {
return 0;
}
*pX = allX[ 3 ];
*pY = allY[ 3 ];
return 1;
}
int xpt2046GetAverageCoordinates( int * pX , int * pY , int nSamples )
{
int nRead = 0;
int xAcc = 0 , yAcc = 0;
int x , y;
while ( nRead < nSamples ) {
if ( !xpt2046GetCoordinates( &x , &y ) ) {
break;
}
xAcc += x;
yAcc += y;
nRead ++;
}
if ( nRead == 0 ) {
return 0;
}
*pX = xAcc / nRead;
*pY = yAcc / nRead;
return 1;
}

View file

@ -0,0 +1,31 @@
#ifndef _H_XPT2046_STM32F4
#define _H_XPT2046_STM32F4
// Ports and controllers for the various touchscreen pins
// IRQ (any port will do)
#define XPT2046_IRQ_PORT GPIOC
#define XPT2046_IRQ_PAD 4
// NSS (any port will do, this is done manually)
#define XPT2046_NSS_PORT GPIOA
#define XPT2046_NSS_PAD 4
// CLK (must be a SPI clock port)
#define XPT2046_CLK_PORT GPIOA
#define XPT2046_CLK_PAD 5
// DIN (must be a SPI MOSI port)
#define XPT2046_DIN_PORT GPIOA
#define XPT2046_DIN_PAD 7
// DOUT (must be a SPI MISO port)
#define XPT2046_DOUT_PORT GPIOA
#define XPT2046_DOUT_PAD 6
// SPI driver to use
#define XPT2046_SPI_DRIVER SPID1
void xpt2046Init( void );
int xpt2046GetCoordinates( int * pX , int * pY );
int xpt2046GetAverageCoordinates( int * pX , int * pY , int nSamples );
#endif //_H_XPT2046_STM32F4