Jump to content
43oh

PCF8566 & MSP430G


Recommended Posts

Does anyone on this board have any experience in using the PCF8566 to drive glass LCD displays? It uses I2C for communications with a Master. I'm getting acknowledgments back from the PCF8566 after selecting its address and also from sending a command to it, however, subsequent data bytes display incorrectly. Any help would be greatly appreciated.

 

Thanks,

Roger430

Link to post
Share on other sites

:oops: :oops: My bad! Turns out my Raw LCD display segment map was totally different than what I expected. I got the 4 backplane pins right but the segment pins were way off. Everything is working just fine now. I modified the I2c code in TI's example code "msp430g2x21_usi_12.c" to communicate with the PCF8566.

Link to post
Share on other sites
Awesome.. could we see what your working on? :)

Sure, it's just an experiment with a raw LCD display that I salvaged from a digital device. I did this to get familiar with I2C and the PFC8566 was pretty cheap. I'll get a picture and clean up my code a bit and post it here shortly.

Thanks for asking!

 

Roger

Link to post
Share on other sites

Well here it is in all its glory! If only I could find a use for it. . . :D

The toughest part was trying to figure out the configuration of the Raw LCD display. This one happened to have 4 back planes and used only 2 pins for each character for a total of 20 pins. It has a few graphics and a low battery indicator. It also has a small 7 segment character in the upper right hand corner as seen in the photo. The PCF8566 plays nicely with the MSP420G2231 and works very well with 3.3v.

 

post-3266-135135523784_thumb.jpg

 

post-3266-135135524238_thumb.jpg

 

and here is the code:

 

//******************************************************************************
//  MSP430G2x21/G2x31 Demo - I2C Master Transmitter / Reciever, multiple bytes
//
//  Description: I2C Master communicates with I2C Slave using
//  the USI. Master data should increment from 0x55 with each transmitted byte
//  and Master determines the number of bytes recieved, set by 
//  the Number_of_Bytes value. LED off for address or data Ack; 
//  LED on for address or data NAck.
//  ACLK = n/a, MCLK = SMCLK = Calibrated 1MHz
//
//
//  ***THIS IS THE MASTER CODE***
//
//                  Slave                      Master
//     (msp430g2x21_usi_15.c)
//             MSP430G2x21/G2x31          MSP430G2x21/G2x31
//             -----------------          -----------------
//         /|\|              XIN|-    /|\|              XIN|-
//          | |                 |      | |                 |
//          --|RST          XOUT|-     --|RST          XOUT|-
//            |                 |        |                 |
//      LED <-|P1.0             |        |                 |
//            |                 |        |             P1.0|-> LED
//            |         SDA/P1.7|------->|P1.6/SDA         |
//            |         SCL/P1.6|<-------|P1.7/SCL         |
//
//  Note: internal pull-ups are used in this example for SDA & SCL
//
//  D. Dang
//  Texas Instruments Inc.
//  October 2010
//  Built with CCS Version 4.2.0 and IAR Embedded Workbench Version: 5.10
//******************************************************************************

#include 

#define number_of_bytes 10                   // How many bytes?

void Master_Transmit(void);
//void Master_Recieve(void);

void Setup_USI_Master_TX(void);
//void Setup_USI_Master_RX(void);

// First two bytes are PCF8566 commands
static char MST_Data[10] = {0xC8, 0x00, 0xBE, 0x06, 0x7C, 0x5E, 0xC6, 0xDA, 0xBE, 0x00};  //character segment map

char SLV_Addr = 0x7C;                       
int I2C_State = 0;
int Bytecount = 0; 
int Transmit = 0;     // State variable
void Data_TX (void);
void main(void)
{
 volatile unsigned int i;                  // Use volatile to prevent removal

 WDTCTL = WDTPW + WDTHOLD;                 // Stop watchdog
 if (CALBC1_1MHZ ==0xFF || CALDCO_1MHZ == 0xFF)                                     
 {  
   while(1);                               // If calibration constants erased
                                           // do not load, trap CPU!!
 } 

 BCSCTL1 = CALBC1_1MHZ;                    // Set DCO
 DCOCTL = CALDCO_1MHZ;

 P1OUT = 0xC0;                             // P1.6 & P1.7 Pullups, others to 0
 P1REN |= 0xC0;                            // P1.6 & P1.7 Pullups
 P1DIR = 0xFF;                             // Unused pins as outputs
 P2OUT = 0;
 P2DIR = 0xFF;



 while(1)
 {
   Master_Transmit(); 
   //_NOP();                                 // Used for IAR
 }
}

/******************************************************
// USI interrupt service routine
// Data Transmit : state 0 -> 2 -> 4 -> 10 -> 12 -> 14
******************************************************/
#pragma vector = USI_VECTOR
__interrupt void USI_TXRX (void)
{
 switch(__even_in_range(I2C_State,14))
   {
     case 0: // Generate Start Condition & send address to slave
             P1OUT |= 0x01;                // LED on: sequence start
             Bytecount = 0;
             USISRL = 0x00;                // Generate Start Condition...
             USICTL0 |= USIGE+USIOE;
             USICTL0 &= ~USIGE;
             if (Transmit == 1){
               USISRL = 0x7C;              // Address is 0x48 << 1 bit + 0 (rw)
             }
              if (Transmit == 0){
              USISRL = 0x91;               // 0x91 Address is 0x48 << 1 bit 
                                           // + 1 for Read
              }
             USICNT = (USICNT & 0xE0) + 0x08; // Bit counter = 8, TX Address
             I2C_State = 2;                // next state: rcv address (N)Ack
             break;

     case 2: // Receive Address Ack/Nack bit
             USICTL0 &= ~USIOE;            // SDA = input
             USICNT |= 0x01;               // Bit counter=1, receive (N)Ack bit
             I2C_State = 4;                // Go to next state: check (N)Ack
             break;

     case 4: // Process Address Ack/Nack & handle data TX

             USICTL0 |= USIOE;             // SDA = output
             if (USISRL & 0x01)            // If Nack received...
             { // Send stop...
               USISRL = 0x00;
               USICNT |=  0x01;            // Bit counter=1, SCL high, SDA low
               I2C_State = 14;             // Go to next state: generate Stop
               P1OUT |= 0x01;              // Turn on LED: error
             }
             else
             { // Ack received, TX data to slave... 
             USISRL = MST_Data[bytecount];            // Load data byte
             USICNT |=  0x08;              // Bit counter = 8, start TX
             I2C_State = 10;               // next state: receive data (N)Ack
             //Bytecount++;
             P1OUT &= ~0x01;               // Turn off LED
             break;
             }

     case 10: // Receive Data Ack/Nack bit
             USICTL0 &= ~USIOE;            // SDA = input
             USICNT |= 0x01;               // Bit counter = 1, receive (N)Ack bit
             I2C_State = 12;               // Go to next state: check (N)Ack
             break;

     case 12: // Process Data Ack/Nack & send Stop
             USICTL0 |= USIOE;
             if (Bytecount == number_of_bytes){// If last byte
             USISRL = 0x00;

             I2C_State = 14;               // Go to next state: generate Stop
             P1OUT |= 0x01;
             USICNT |=  0x01;     }        // set count=1 to trigger next state
             else{
               P1OUT &= ~0x01;             // Turn off LED
               Data_TX();                  // TX byte
             }
             break;

     case 14:// Generate Stop Condition
             USISRL = 0x0FF;               // USISRL = 1 to release SDA
             USICTL0 |= USIGE;             // Transparent latch enabled
             USICTL0 &= ~(USIGE+USIOE);    // Latch/SDA output disabled
             I2C_State = 0;                // Reset state machine for next xmt
             LPM0_EXIT;                    // Exit active for next transfer
             break;
   }

 USICTL1 &= ~USIIFG;                       // Clear pending flag
}

void Data_TX (void){

             USISRL = MST_Data[bytecount];          // Load data byte
             USICNT |=  0x08;              // Bit counter = 8, start TX
             I2C_State = 10;               // next state: receive data (N)Ack
             Bytecount++;
}

void Setup_USI_Master_TX (void)
{ 
 _DINT();
 Bytecount = 0;
 Transmit = 1;
 USICTL0 = USIPE6+USIPE7+USIMST+USISWRST;  // Port & USI mode setup
 USICTL1 = USII2C+USIIE;                   // Enable I2C mode & USI interrupt
 USICKCTL = USIDIV_7+USISSEL_2+USICKPL;    // USI clk: SCL = SMCLK/128
 USICNT |= USIIFGCC;                       // Disable automatic clear control
 USICTL0 &= ~USISWRST;                     // Enable USI
 USICTL1 &= ~USIIFG;                       // Clear pending flag
 _EINT();
}

void Master_Transmit(void){
Setup_USI_Master_TX();
   USICTL1 |= USIIFG;                      // Set flag and start communication
   LPM0;                                   // CPU off, await USI interrupt
   __delay_cycles(10000);                  // Delay between comm cycles
}

Link to post
Share on other sites

That's nice. Good job!

 

There's plenty of applications for something like this:

- Outside Thermometer

- Tachometer

- Clock

- Voltmeter

- CPU load monitor

 

 

Personally, I would build an outside thermometer with some 1-Wire temp sensors because it's flipping cold up here today. It's -20'C or 0'F. Brrr!!!

Link to post
Share on other sites

I managed to cobble together a workable temperature display of the internal MSP430 temperature sensor. I used the small character in the upper right hand corner of the display as a degree symbol (actually the character 0). I used a table lookup for the characters 0 - 9. Right now it only displays the temp in two digits. I need to display 3 digits here in Texas - temps last summer were mostly in the 100's. Here's a picture followed by the code if anyone is interested (the schematic has been updated above):

 

post-3266-13513552425_thumb.jpg

 

//******************************************************************************
//  This project is a simple display of the internal temperature sensor
//  of the MSP430G2231 utilizing a raw (no logic) LCD panel and a PCF8566
//  display driver.  The MSP430 communicates with the PCF8566 using 2 wires
//  via I2C.  Temperatures up to 199F will be displayed, however, no provision
//  has been made for negative temperatures. (Rarely gets that cold if at all
//  in this part of Texas.)  I2c code came directly from the example code TI
//  has provided.
//  Roger430 - December, 2011
//
//  MSP430G2x21/G2x31 Demo - I2C Master Transmitter / Receiver, multiple bytes
//
//  Description: I2C Master communicates with I2C Slave using
//  the USI. Master data should increment from 0x55 with each transmitted byte
//  and Master determines the number of bytes received, set by 
//  the Number_of_Bytes value. LED off for address or data Ack; 
//  LED on for address or data NAck.
//  ACLK = n/a, MCLK = SMCLK = Calibrated 1MHz
//
//
//  ***THIS IS THE MASTER CODE***
//
//                  Slave                      Master
//                 PCF8566                   MSP430G2x31
//             -----------------          -----------------
//            |                 |     /|\|              XIN|-
//            | Address:        |      | |                 |
//            | 0x7C            |      --|RST          XOUT|-
//            | 0111 1100       |        |                 |
//            |                 |        |                 |
//            |                 |        |             P1.0|-> LED
//            |      Pin 2  SDA |<-------|P1.6/SDA         |
//            |      Pin 1  SCL |<-------|P1.7/SCL         |
//
//  Note: internal pull-ups are used in this example for SDA & SCL
//
//  D. Dang
//  Texas Instruments Inc.
//  October 2010
//  Built with CCS Version 4.2.0 and IAR Embedded Workbench Version: 5.10
//
//******************************************************************************

#include 

void Master_Transmit(void);

void Setup_USI_Master_TX(void);

// Initialize data:
static char MST_DataInit[10] = {0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0x00};
// LCD segment array:
static const char Digits[10] = {0xBE, 0x06, 0x7C, 0x5E, 0xC6, 0xDA, 0xFA, 0x0E, 0xFE, 0xCE};
// Transmit array:
static char MST_DataT[7] = {0xC8, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00};

int number_of_bytes = 0;
int digit1 = 0;
int digit2 = 0;
long temp;
long IntDegF;
long IntDegC;
char *MST_Data;
char SLV_Addr = 0x7C;                      
int I2C_State = 0;
int Bytecount = 0; 
int Transmit = 0;     // State variable
int FCflag = 1;		//Fahrenheit/Celsius flag
void Data_TX (void);
void GetDigits(int);
void main(void)
{
 volatile unsigned int i;                  // Use volatile to prevent removal

 WDTCTL = WDTPW + WDTHOLD;                 // Stop watchdog
 if (CALBC1_1MHZ ==0xFF || CALDCO_1MHZ == 0xFF)                                     
 {  
   while(1);                               // If calibration constants erased
                                           // do not load, trap CPU!!
 }
 ADC10CTL1 = INCH_10 + ADC10DIV_3;         // Temp Sensor ADC10CLK/4
 ADC10CTL0 = SREF_1 + ADC10SHT_3 + REFON + ADC10ON + ADC10IE;
 __enable_interrupt();                     // Enable interrupts.
 TACCR0 = 30;                              // Delay to allow Ref to settle
 TACCTL0 |= CCIE;                          // Compare-mode interrupt.
 TACTL = TASSEL_2 | MC_1;                  // TACLK = SMCLK, Up mode.
 LPM0;                                     // Wait for delay.
 TACCTL0 &= ~CCIE;                         // Disable timer Interrupt
 __disable_interrupt(); 

 BCSCTL1 = CALBC1_1MHZ;                    // Set DCO
 DCOCTL = CALDCO_1MHZ;

 P1OUT = 0xC0;                             // P1.6 & P1.7 Pullups, others to 0
 P1REN |= 0xC0;                            // P1.6 & P1.7 Pullups
 P1DIR = 0xFF;                             // Unused pins as outputs
 P2OUT = 0;
 P2DIR = 0xFF;
 _delay_cycles(100000);
 MST_Data = MST_DataInit;   // Load initialize data
 number_of_bytes = 10;
 Master_Transmit();   // Initialize PCF8566

 while(1)
 {
 	ADC10CTL0 |= ENC + ADC10SC;             // Sampling and conversion start
   __bis_SR_register(CPUOFF + GIE);        // LPM0 with interrupts enabled
__disable_interrupt();
   temp = ADC10MEM;
   // IntDegF = ((temp - 630) * 761) / 1024;  // Calculate Fahrenheit
   IntDegF = ((temp - 623) * 761) / 1024;  // Calculate Fahrenheit
   //IntDegC = ((temp - 673) * 423) / 1024;  // Calculate Celsius
   IntDegC = ((temp - 666) * 423) / 1024;  // Calculate Celsius
   if (FCflag == 1) {
GetDigits(IntDegF);
   MST_DataT[6] = 0xE8;	// set small character to "F"
   FCflag = 0;		// celsius on the next go-around
   }
   else
   {
   GetDigits(IntDegC);
   MST_DataT[6] = 0xB8;	// set small character to "C"
   FCflag = 1;		// fahrenheit on the next go-around
   }
MST_DataT[3] = Digits[digit1];	// put 10's digit in array
MST_DataT[4] = Digits[digit2];  // put units digit in array  
   MST_Data = MST_DataT;  			// get data to xmit
   number_of_bytes = 7;
   Master_Transmit();				// transmit data to PCF8566
   __enable_interrupt();
 }
}

/******************************************************
// USI interrupt service routine
// Data Transmit : state 0 -> 2 -> 4 -> 10 -> 12 -> 14
// Data Recieve  : state 0 -> 2 -> 4 -> 6 -> 8 -> 14
******************************************************/
#pragma vector = USI_VECTOR
__interrupt void USI_TXRX (void)
{
 switch(__even_in_range(I2C_State,14))
   {
     case 0: // Generate Start Condition & send address to slave
             P1OUT |= 0x01;                // LED on: sequence start
             Bytecount = 0;
             USISRL = 0x00;                // Generate Start Condition...
             USICTL0 |= USIGE+USIOE;
             USICTL0 &= ~USIGE;
             if (Transmit == 1){
               USISRL = 0x7C;              // Address is 0x48 << 1 bit + 0 (rw)
             }
              if (Transmit == 0){
              USISRL = 0x91;               // 0x91 Address is 0x48 << 1 bit 
                                           // + 1 for Read
              }
             USICNT = (USICNT & 0xE0) + 0x08; // Bit counter = 8, TX Address
             I2C_State = 2;                // next state: rcv address (N)Ack
             break;

     case 2: // Receive Address Ack/Nack bit
             USICTL0 &= ~USIOE;            // SDA = input
             USICNT |= 0x01;               // Bit counter=1, receive (N)Ack bit
             I2C_State = 4;                // Go to next state: check (N)Ack
             break;

     case 4: // Process Address Ack/Nack & handle data TX

             USICTL0 |= USIOE;             // SDA = output
             if (USISRL & 0x01)            // If Nack received...
             { // Send stop...
               USISRL = 0x00;
               USICNT |=  0x01;            // Bit counter=1, SCL high, SDA low
               I2C_State = 14;             // Go to next state: generate Stop
               P1OUT |= 0x01;              // Turn on LED: error
             }
             else
             { // Ack received, TX data to slave... 
             USISRL = MST_Data[bytecount];            // Load data byte
             USICNT |=  0x08;              // Bit counter = 8, start TX
             I2C_State = 10;               // next state: receive data (N)Ack
             //Bytecount++;
             P1OUT &= ~0x01;               // Turn off LED
             break;
             }         

     case 10: // Receive Data Ack/Nack bit
             USICTL0 &= ~USIOE;            // SDA = input
             USICNT |= 0x01;               // Bit counter = 1, receive (N)Ack bit
             I2C_State = 12;               // Go to next state: check (N)Ack
             break;

     case 12: // Process Data Ack/Nack & send Stop
             USICTL0 |= USIOE;
             if (Bytecount == number_of_bytes){// If last byte
             USISRL = 0x00;

             I2C_State = 14;               // Go to next state: generate Stop
             P1OUT |= 0x01;
             USICNT |=  0x01;     }        // set count=1 to trigger next state
             else{
               P1OUT &= ~0x01;             // Turn off LED
               Data_TX();                  // TX byte
             }
             break;

     case 14:// Generate Stop Condition
             USISRL = 0x0FF;               // USISRL = 1 to release SDA
             USICTL0 |= USIGE;             // Transparent latch enabled
             USICTL0 &= ~(USIGE+USIOE);    // Latch/SDA output disabled
             I2C_State = 0;                // Reset state machine for next xmt
             LPM0_EXIT;                    // Exit active for next transfer
             break;
   }

 USICTL1 &= ~USIIFG;                       // Clear pending flag
}

void Data_TX (void){

             USISRL = MST_Data[bytecount];          // Load data byte
             USICNT |=  0x08;              // Bit counter = 8, start TX
             I2C_State = 10;               // next state: receive data (N)Ack
             Bytecount++;
}

void Setup_USI_Master_TX (void)
{ 
 _DINT();
 Bytecount = 0;
 Transmit = 1;
 USICTL0 = USIPE6+USIPE7+USIMST+USISWRST;  // Port & USI mode setup
 USICTL1 = USII2C+USIIE;                   // Enable I2C mode & USI interrupt
 USICKCTL = USIDIV_7+USISSEL_2+USICKPL;    // USI clk: SCL = SMCLK/128
 USICNT |= USIIFGCC;                       // Disable automatic clear control
 USICTL0 &= ~USISWRST;                     // Enable USI
 USICTL1 &= ~USIIFG;                       // Clear pending flag
 _EINT();
}

void Master_Transmit(void){
Setup_USI_Master_TX();
   USICTL1 |= USIIFG;                      // Set flag and start communication
   LPM0;                                   // CPU off, await USI interrupt
   __delay_cycles(2000000);                // Delay between comm cycles
}

// ADC10 interrupt service routine
#pragma vector=ADC10_VECTOR
__interrupt void ADC10_ISR (void)
{
 __bic_SR_register_on_exit(CPUOFF);        // Clear CPUOFF bit from 0(SR)
}

#pragma vector=TIMERA0_VECTOR
__interrupt void ta0_isr(void)
{
 TACTL = 0;
 LPM0_EXIT;                                // Exit LPM0 on return
}

void GetDigits(tempVal)
{
if (tempVal >= 100 )
{
	tempVal -= 100;
	MST_DataT[2] = 0x06;	// set 100's digit to "1" (no provision for > 199)
}
else
{
	MST_DataT[2] = 0x00;	// or not
}
digit1 = tempVal/10;		// separate 10's digit
digit2 = tempVal%10;		// separate units digit
}

** Added code to alternately display the temperature in Fahrenheit and Celsius. The symbol in the upper right hand corner will change from F to C accordingly.

** Added display of 100's digit - Dec. 2011

Link to post
Share on other sites
Love the degree symbol. Good use. POTM submission please.

Thanks for the invitation to submit this to POTM, however, I don't feel that this experiment would qualify as a candidate for POTM. I believe this project is too specific in nature as it contains an LCD display that most likely cannot be purchased anywhere. It was salvaged from a scrapped digital blood pressure device. The code could possibly be used with other raw LCD displays - don't know for sure.

 

Thanks,

Roger

Link to post
Share on other sites
  • 4 weeks later...

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...