nuetron 64 Posted August 11, 2011 Share Posted August 11, 2011 Hi guys! I've been working on a program that will read a byte from an AT computer keyboard, and print it on a 16x2 LCD display. I'm also working on a function that will read the visible portion of the display, and save it in an I2C EEPROM. Using RobG's I2C code, I was able to successfully interface an HD44780 display to my '2231, via a PCF8574 port expander. Since I now have a bidirectional 8-bit parallel bus, I will connect a W83C43 keyboard controller to it, thus enabling a very large input range to my micro. The schematic: RobG, zeke, bluehash and 1 other 4 Quote Link to post Share on other sites
zeke 693 Posted August 11, 2011 Share Posted August 11, 2011 I made a VT100 terminal back in 1996 with an AT keyboard but I used a Motorola 68000 and a 640x480 LCD panel. I think it's a great project to do. :thumbup: By the way, don't you have to change the A0/A1/A2 connections so that the Flash memory and the port expander don't conflict? nuetron 1 Quote Link to post Share on other sites
nuetron 64 Posted August 11, 2011 Author Share Posted August 11, 2011 By the way, don't you have to change the A0/A1/A2 connections so that the Flash memory and the port expander don't conflict? Nope, the address for the EEPROM starts with 0xA (1010), while the address for the expander starts with 0x4 (0100). The next three bits are a sub address (this is where the A0/A1/A2 come in for the expander, the pins on the EEPROM are unused), and the last bit defines whether the operation is a read or write. Quote Link to post Share on other sites
nuetron 64 Posted August 13, 2011 Author Share Posted August 13, 2011 If I wanted an external interrupt to wake my micro, perform an action, and fall asleep again, would I do this? #pragma vector=PORT1_VECTOR __interrupt void PORT1_ISR(void) { _BIC_SR_IRQ(LPM3_bits); // Clear LPM3 bits from 0(SR) readKeybd(); sendData(charData); P1IFG = 0; // clear interrupt _BIS_SR(LPM3_bits + GIE); // Enter LPM3 } Quote Link to post Share on other sites
zeke 693 Posted August 13, 2011 Share Posted August 13, 2011 Did you setup the interrupt on change feature of P1? What polarity did you select? Quote Link to post Share on other sites
nuetron 64 Posted August 13, 2011 Author Share Posted August 13, 2011 Does this answer your question? P1DIR &= ~KINT; P1OUT |= KINT; P1REN |= KINT; P1IES |= KINT; P1IFG &= ~KINT; P1IE |= KINT; set as input, output positive, enable pull-up, negative edge, interrupt enable. Does "P1IFG &= ~KINT;" mean "KINT" sets the P1 interrupt flag? Quote Link to post Share on other sites
zeke 693 Posted August 13, 2011 Share Posted August 13, 2011 Does "P1IFG &= ~KINT;" mean "KINT" sets the P1 interrupt flag? Nope. It means clear the P1.0 Interrupt Flag. P1DIR &= ~KINT; // set P1.0 as input P1OUT |= KINT; // set P1.0 HIGH P1REN |= KINT; // enable P1.0 internal pullup resistor P1IE |= KINT; // enable P1.0 interrupt P1IES |= KINT; // set P1.0 interrupt detection to Hi/Lo edge transition P1IFG &= ~KINT; // clear P1.0 interrupt flag And then in your ISR #pragma vector=PORT1_VECTOR __interrupt void PORT1_ISR(void) { readKeybd(); sendData(charData); P1IFG &= ~KINT; // clear P1.0 interrupt IMHO, I think that you should avoid low power modes until you've tested out the ISR. LPModes are trixy little buggers and need to be treated carefully. Take a look at this sample usage from MSP430 Microcontroller Basics, page 201: Listing 6.6: Part of program timintC2.c to toggle LEDs using interrupts from Timer_A. The device enters low-power mode 0 between interrupts. // ---------------------------------------------------------------- __enable _interrupt (); // Enable interrupts (intrinsic) for (; { // Loop forever doing nothing __low_power_mode_0 (); // Enter low power mode LPM0 } // Interrupts do the work} // ----------------------------------------------------------------------// Interrupt service routine for Timer A channel 0// Processor returns to LPM0 automatically after ISR// ----------------------------------------------------------------------#pragma vector = TIMERA0_VECTOR__interrupt void TA0_ISR (void){ P2OUT nuetron 1 Quote Link to post Share on other sites
zeke 693 Posted August 13, 2011 Share Posted August 13, 2011 BTW, I found an example in MSP430 Microcontroller Basics that may be just what you're looking for. It's on page 217,218. Listing 7.1: Program butled4.c in C to light LED1 when button B1 is pressed usinginterrupts and low-power mode 4.// butled4.c - press button B1 to light LED1// Responds to interrupts on input pin , LPM4 between interrupts// Olimex 1121 STK board , LED1 active low on P2.3,// button B1 active low on P2.1// J H Davies , 2006 -11 -18; IAR Kickstart version 3.41A// ----------------------------------------------------------------------#include // Specific device#include // Intrinsic functions// ----------------------------------------------------------------------void main (void){ WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer P2OUT_bit.P2OUT_3 = 1; // Preload LED1 off (active low!) P2DIR_bit.P2DIR_3 = 1; // Set pin with LED1 to output P2IE_bit.P2IE_1 = 1; // Enable interrupts on edge P2IES_bit.P2IES_1 = 1; // Sensitive to negative edge (H->L) do { P2IFG = 0; // Clear any pending interrupts ... } while (P2IFG != 0); // ... until none remain for (; { // Loop forever (should not need) __low_power_mode_4 (); // LPM4 with int'pts , all clocks off } // (RAM retention mode)} // ----------------------------------------------------------------------// Interrupt service routine for port 2 inputs// Only one bit is active so no need to check which// Toggle LED , toggle edge sensitivity , clear any pending interrupts// Device returns to low power mode automatically after ISR// ----------------------------------------------------------------------#pragma vector = PORT2_VECTOR__interrupt void PORT2_ISR (void){ P2OUT_bit.P2OUT_3 nuetron 1 Quote Link to post Share on other sites
nuetron 64 Posted September 12, 2011 Author Share Posted September 12, 2011 Ok, I've left off interrupts for now, working on other aspects of my program. I keep running into trouble, though. Partway through the code, the 'g2231 I'm working with will reset. The problem is elusive, recurring, and doesn't always happen in the same spot, and only changes location when I change the code, no matter how slight. Sometimes it will reset very close to the beginning of the code, other times it will happen halfway through. The code: #include "msp430g2231_mod.h" // #include "msp430g2211.h" #define SDA BIT7 #define SCL BIT0 #define EN BIT5 // display ENABLE #define RS BIT4 // display RS pin connected here #define RW BIT3 // display R/W pin connected here #define A2 BIT2 #define CS BIT1 #define KINT BIT6 #define MEMRD 0xA1 // EEPROM #define MEMWR 0xA0 // EEPROM #define P1RD 0x41 // port expander 1 read // display databus connected here #define P1WR 0x40 // port expander 1 write // display databus connected here #define FAILURE -1 #define SUCCESS 0 #define error "MEMREAD ERROR! " // for error reporting void sendByte(void); void receiveByte(void); void sendAck(void); void receiveAck(void); void start(void); void stop(void); unsigned char txData = 0; unsigned char rxData = 0; unsigned int address = 0; // 12 bit address, upper 4 bits should be 0s. unsigned char charData = 0; int bitCounter = 0; // Nuetron's copied and modified functions void sendStr(void); void sendStr(char string[], char info, int size); int readStr(void); int readPort(void); int writePort(char data); int writeCharLCD(char data, bool rs); int readStrLCD(void); void sendData(char data){ writeCharLCD(data, 1); } void sendInstruction(char data){ writeCharLCD(data, 0); } void clearDisplay(void){ sendInstruction(0x01); __delay_cycles(2000); } void initDisplay(void){ sendInstruction(0x38); sendInstruction(0x0E); clearDisplay(); sendInstruction(0x06); } bool ackFlag; bool kintFlag; char dispBuffer[32]; // Display buffer void main(void) { WDTCTL = WDTPW + WDTHOLD; BCSCTL1 = CALBC1_1MHZ; DCOCTL = CALDCO_1MHZ; P1OUT |= EN|RS|RW|A2|CS; P1DIR |= EN|RS|RW|A2|CS; P1OUT |= SCL; P1DIR |= SCL; // Set up KINT P1DIR &= ~KINT; // set P1.0 as input P1OUT |= KINT; // set P1.0 HIGH P1REN |= KINT; // enable P1.0 internal pullup resistor initDisplay(); initDisplay(); // My display seems to reqire two initializations... address = 0; for(int t=0; t < 5; t++){ readStr(); sendStr(); __delay_cycles(2500000); } readStrLCD(); } void sendStr(void) { sendInstruction(0x80); for(int charIndex = 0; charIndex < 32; charIndex++) { if(dispBuffer[charIndex] < 0x20){ sendStr(error, '<', 15); break; } if(dispBuffer[charIndex] > 0x7F){ sendStr(error, '>', 15); break; } if(charIndex == 16) sendInstruction(0xC0); sendData(dispBuffer[charIndex]); } } void sendStr(char string[], char info, int size) { sendInstruction(0x80); for(int charIndex = 0; charIndex < size; charIndex++) { sendData(string[charIndex]); } sendData(info); } int readStr(void) { start(); txData = MEMWR; sendByte(); receiveAck(); if(!ackFlag) return FAILURE; txData = address; sendByte(); receiveAck(); start(); txData = MEMRD; sendByte(); receiveAck(); if(!ackFlag) return FAILURE; for(int c=0; c < 32; c++){ receiveByte(); sendAck(); dispBuffer[c] = rxData; address++; } stop(); return SUCCESS; } // */ int writeStr(void) { int c[2]; for(c[0]=0; c[0] < 2; c[0]++) { start(); txData = MEMWR; sendByte(); receiveAck(); if(!ackFlag) return FAILURE; txData = address; sendByte(); receiveAck(); if(!ackFlag) return FAILURE; if(!c[0]) sendInstruction(0x80); if(c[0]) sendInstruction(0xC0); while(c[1] < 32){ if(c[1] == 16) break; txData = dispBuffer[c[1]]; sendByte(); receiveAck(); if(!ackFlag) return FAILURE; address++; c[1]++; } stop(); } return SUCCESS; } int writeCharLCD(char data, bool rs) { start(); txData = P1WR; sendByte(); receiveAck(); if(!ackFlag) return FAILURE; txData = data; sendByte(); receiveAck(); if(!ackFlag) return FAILURE; stop(); rs ? (P1OUT |= RS) : (P1OUT &= ~RS); P1OUT &= ~RW; P1OUT &= ~EN; __delay_cycles(50); P1OUT |= EN; return SUCCESS; } int readStrLCD(void) { sendInstruction(0x80); writePort(0xFF); P1OUT |= RW|RS; P1OUT &= ~EN; __delay_cycles(25); readPort(); P1OUT |= EN; __delay_cycles(100); return SUCCESS; } int writePort(char data) { start(); txData = P1WR; sendByte(); receiveAck(); if(!ackFlag) return FAILURE; txData = data; sendByte(); receiveAck(); if(!ackFlag) return FAILURE; stop(); return SUCCESS; } int readPort(void) { start(); txData = P1RD; sendByte(); receiveAck(); if(!ackFlag) return FAILURE; receiveByte(); sendAck(); stop(); charData = rxData; return SUCCESS; } // required // send byte to slave void sendByte(void) { P1DIR |= SDA; bitCounter = 0; while(bitCounter < 8) { (txData & BIT7) ? (P1OUT |= SDA) : (P1OUT &= ~SDA); P1OUT |= SCL; txData <<= 1; P1OUT &= ~SCL; bitCounter++; } P1OUT |= SDA; P1DIR &= ~SDA; } // required // receive byte from slave void receiveByte(void) { bitCounter = 0; while(bitCounter < 8) { P1OUT |= SCL; rxData <<= 1; if(P1IN & SDA) { rxData |= BIT0; } P1OUT &= ~SCL; bitCounter++; } } // required // send master's ACK void sendAck(void) { P1DIR |= SDA; P1OUT &= ~SDA; P1OUT |= SCL; P1OUT &= ~SCL; P1OUT |= SDA; P1DIR &= ~SDA; } // required // receive slave's ACK void receiveAck(void) { P1OUT |= SCL; (P1IN & SDA) ? (ackFlag = false) : (ackFlag = true); P1OUT &= ~SCL; } // required // start condition void start(void) { P1OUT |= SCL; P1DIR |= SDA; P1OUT &= ~SDA; P1OUT &= ~SCL; P1OUT |= SDA; P1DIR &= ~SDA; } // required // stop condition void stop(void) { P1DIR |= SDA; P1OUT &= ~SDA; P1OUT |= SCL; P1OUT |= SDA; P1DIR &= ~SDA; } As you can see here, I have the optimizations turned all the way up. And here, you can see how much data the program occupies. Am I just using too much data memory? (In other words, would I need a higher memory capacity uC to run this code?) P.S: If it will help, I am using IAR Kickstart. Quote Link to post Share on other sites
RobG 1,892 Posted September 12, 2011 Share Posted September 12, 2011 Keep in mind that every function call eats up your RAM, so if you have several nested calls, you can quickly run out of memory. nuetron 1 Quote Link to post Share on other sites
nuetron 64 Posted September 13, 2011 Author Share Posted September 13, 2011 Thanks. As it turns out, it needed a lot more RAM for what I was trying to do. Here are the results from the build for a 'G2252, which has twice the RAM that a 'G2231 has: So, now that I know the 'G2252 will handle it, shall I order some samples from TI? Or would using USI be simpler and easier? Quote Link to post Share on other sites
nuetron 64 Posted September 19, 2011 Author Share Posted September 19, 2011 The USI is definitely simpler: A lot of this code was taken from a TI example, and heavily modified to reflect my previous compilation: #include "msp430g2231_mod.h" #define LED1 BIT7 #define LED2 BIT6 #define EN BIT5 // display ENABLE #define RS BIT4 // display RS pin connected here #define RW BIT3 // display R/W pin connected here #define A2 BIT2 #define CS BIT1 #define KINT BIT0 #define MEMRD 0xA1 // EEPROM #define MEMWR 0xA0 // EEPROM #define P1RD 0x41 // port expander 1 read // display databus connected here #define P1WR 0x40 // port expander 1 write // display databus connected here #define fail 0 #define success 1 #define hello_world "Hello! This works!" char buffer = 0; void init_disp(void); void wrLCD(char data, bool rs); void sendStr(char string[]); int tx_Data(char data); int rx_Data(void); /* * ======== main ======== */ void main(void) { BCSCTL1 = CALBC1_1MHZ; DCOCTL = CALDCO_1MHZ; P1OUT = 0xFF; P1REN |= 0xC1; P1DIR = 0xFE; P2OUT = 0xC0; P2DIR = 0xC0; USICTL0 = USIPE7 | USIPE6 | USIMST | USIOE | USISWRST; USICKCTL = USIDIV_4 + USISSEL_2 + USICKPL; USICTL1 = USII2C; USICTL0 &= ~USISWRST; init_disp(); sendStr(hello_world); } void init_disp(void){ P1OUT &= ~RW + ~RS; for(int t=0; t<2; t++){ while(tx_Data == fail){ tx_Data(0x38); P1OUT &= ~EN; P1OUT |= EN; tx_Data(0x0E); P1OUT &= ~EN; P1OUT |= EN; tx_Data(0x01); P1OUT &= ~EN; P1OUT |= EN; __delay_cycles(2000); tx_Data(0x06); P1OUT &= ~EN; P1OUT |= EN; __delay_cycles(200000); } } } void wrLCD(char data, bool rs){ tx_Data(data); rs ? (P1OUT |= RS) : (P1OUT &= ~RS); P1OUT &= RW; P1OUT &= ~EN; P1OUT |= EN; } void sendStr(char string[]) { wrLCD(0x80, 0); for(int str = 0; string[str]; str++){ if(str == 16){ wrLCD(0xC0, 0); } if(str == 32){ break; } wrLCD(string[str], 1); } } int tx_Data(char data) { P2OUT |= LED1; // Turn led on, cycle starting... USISRL = 0x00; // Generate Start Condition... USICTL0 |= USIGE+USIOE; USICTL0 &= ~USIGE; USISRL = P1WR; // Address is 0x40 USICNT = (USICNT & 0xE0) + 0x08; // Bit counter = 8, TX Address USICTL0 &= ~USIOE; // SDA = input USICNT |= 0x01; // Bit counter=1, receive (N)Ack bit USICTL0 |= USIOE; // SDA = output if (USISRL & 0x01) // If Nack received... { // Send stop... USISRL = 0x00; USICNT |= 0x01; // Bit counter=1, SCL high, SDA low USISRL = 0xFF; // USISRL = 1 to release SDA USICTL0 |= USIGE; // Transparent latch enabled USICTL0 &= ~(USIGE+USIOE); // Latch/SDA output disabled return fail; } else { // Ack received, TX data to slave... USISRL = data; // Load data byte USICNT |= 0x08; // Bit counter = 8, start TX } USICNT |= 0x01; // Bit counter=1, SCL high, SDA low USISRL = 0xFF; // USISRL = 1 to release SDA USICTL0 |= USIGE; // Transparent latch enabled USICTL0 &= ~(USIGE+USIOE); // Latch/SDA output disabled P2OUT &= ~LED1; // Turn led off, cycle finished return success; } int rx_Data(void){ buffer = 0; P2OUT |= LED2; // Turn led on, cycle starting... USISRL = 0x00; // Generate Start Condition... USICTL0 |= USIGE+USIOE; USICTL0 &= ~USIGE; USISRL = P1RD; // Address is 0x41 USICNT = (USICNT & 0xE0) + 0x08; // Bit counter = 8, TX Address USICTL0 &= ~USIOE; // SDA = input USICNT |= 0x01; // Bit counter=1, receive (N)Ack bit USICTL0 |= USIOE; // SDA = output if (USISRL & 0x01) // If Nack received... { // Send stop... USISRL = 0x00; USICNT |= 0x01; // Bit counter=1, SCL high, SDA low USISRL = 0xFF; // USISRL = 1 to release SDA USICTL0 |= USIGE; // Transparent latch enabled USICTL0 &= ~(USIGE+USIOE); // Latch/SDA output disabled return fail; } else { // Ack received, RX data from slave... USICTL0 &= ~USIOE; // SDA = input --> redundant USICNT |= 0x08; // Bit counter = 8, RX data buffer = USISRL; // The data in USISRL stored in buffer for later use } USICNT |= 0x01; // Bit counter=1, SCL high, SDA low USISRL = 0xFF; // USISRL = 1 to release SDA USICTL0 |= USIGE; // Transparent latch enabled USICTL0 &= ~(USIGE+USIOE); // Latch/SDA output disabled P2OUT &= ~LED2; // Turn led off, cycle finished return success; } Does anyone see any errors my compiler wouldn't catch? Quote Link to post Share on other sites
nuetron 64 Posted October 8, 2011 Author Share Posted October 8, 2011 Ok, I've got to settle on one thing and stop changing my mind! :problem: I think I have finally decided what kind of bus system I will use; an 8bit parallel bus, fashioned after the 8051 (Intel) timing. It will have the standard RD and WR control lines, as well as the address and data buses. The schematic: Keyboard&LCD.sch Quote Link to post Share on other sites
bluehash 1,581 Posted October 9, 2011 Share Posted October 9, 2011 Looks like its coming through really well. One helpful critic - try to rearrange your components neatly. Use net names if you are going from one side of the page to the other. If you are unable to understand this let me know, I'll explain. nuetron 1 Quote Link to post Share on other sites
nuetron 64 Posted October 9, 2011 Author Share Posted October 9, 2011 Do these make more sense? Something I forgot to mention earlier: to build this parallel bus, I had to switch to an MSP with more I/O. And here is the current under-construction program: #include "msp430g2252.h" #define RD BIT7 #define WR BIT6 #define CK BIT2 #define G BIT1 #define DIR BIT0 #define hello_world "Malfunction!*Need Input!" void init_disp(void); void wrLCD(char data, bool rs); void sendStr(char string[]); void setAddr(char addr); bool line = 0; void main(void){ // Stop watchdog timer to prevent time out reset WDTCTL = WDTPW + WDTHOLD; P1OUT = 0xFF; P1DIR = 0xFF; P2OUT = 0xFF; P2DIR = 0xFF; init_disp(); sendStr(hello_world); } void wrLCD(char data, bool rs){ rs ? (setAddr(0x80)) : (setAddr(0x00)); P1OUT = data; P2OUT &= ~WR; P2OUT |= WR; } void init_disp(void){ setAddr(0x00); for(int t=0; t<2; t++){ P1OUT = 0x38; P2OUT &= ~WR; P2OUT |= WR; P1OUT = 0x0E; P2OUT &= ~WR; P2OUT |= WR; P1OUT = 0x01; P2OUT &= ~WR; P2OUT |= WR; __delay_cycles(2000); P1OUT = 0x06; P2OUT &= ~WR; P2OUT |= WR; __delay_cycles(200000); } } void sendStr(char string[]) { wrLCD(0x80, 0); line=0; for(int str=0; (str < 32) && string[str]; str++){ if(!line && ((string[str] == '*') || (str == 16))){ // wrLCD(0xC0, 0); line=1; str++; } wrLCD(string[str], 1); } } void setAddr(char addr){ P1OUT = addr; P2OUT &= ~CK; P2OUT |= CK; } And here's the build so far: Quote Link to post Share on other sites
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.