![](templates/subSilver/images/CCSLogo.jpg) |
![CCS C Software and Maintenance Offers](templates/subSilver/images/forumAd6.jpg) |
View previous topic :: View next topic |
Author |
Message |
Shaheers
Joined: 23 Sep 2013 Posts: 14
|
Unable to write character '0' (zero) on LCD using flex_LCD |
Posted: Mon Sep 23, 2013 1:50 pm |
|
|
I am using ccs compiler PCM 4.114 and using a PIC 16F887 uC.
I am unable to write zero on lcd as i am using flex_lcd.c
I found ccs's keypad and lcd routines useless for me so i use flex_lcd for LCD and write my own code for 4x3 keypad.
All keys are working fine but i am unable to write zero.
I am testing it on Proteus.
All the code i use are as follows:
Main program file:
Code: |
void main()
{
char key;
lcd_init();
lcd_putc("Starting ...\n");
while(TRUE)
{
key=get_key();
if(key!='H'){
lcd_putc(key);
delay_ms(50);
}
}
}
|
Config file:
Code: |
#include <16F887.h>
#FUSES NOWDT //No Watch Dog Timer
#FUSES INTRC_IO //Internal RC Osc, no CLKOUT
#FUSES NOPUT //No Power Up Timer
#FUSES MCLR //Master Clear pin enabled
#FUSES NOPROTECT //Code not protected from reading
#FUSES NOCPD //No EE protection
#FUSES BROWNOUT //Reset when brownout detected
#FUSES IESO //Internal External Switch Over mode enabled
#FUSES FCMEN //Fail-safe clock monitor enabled
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NODEBUG //No Debug mode for ICD
#FUSES BORV40 //Brownout reset at 4.0V
#FUSES NOWRT //Program memory not write protected
#use delay(int=8000000)
|
LCD file:
Code: |
// flex_lcd.c
// These pins are for the Microchip PicDem2-Plus board,
// which is what I used to test the driver. Change these
// pins to fit your own board.
#define LCD_DB4 PIN_D4
#define LCD_DB5 PIN_D5
#define LCD_DB6 PIN_D6
#define LCD_DB7 PIN_D7
#define LCD_E PIN_D1
#define LCD_RS PIN_D0
#define LCD_RW PIN_D2
// If you only want a 6-pin interface to your LCD, then
// connect the R/W pin on the LCD to ground, and comment
// out the following line.
//#define USE_LCD_RW 1
//========================================
#define lcd_type 2 // 0=5x7, 1=5x10, 2=2 lines
#define lcd_line_two 0x40 // LCD RAM address for the 2nd line
BYTE const LCD_INIT_STRING[4] =
{
0x20 | (lcd_type << 2), // Func set: 4-bit, 2 lines, 5x8 dots
0xc, // Display on
1, // Clear display
6 // Increment cursor
};
//-------------------------------------
void lcd_send_nibble(BYTE nibble)
{
// Note: !! converts an integer expression
// to a boolean (1 or 0).
output_bit(LCD_DB4, !!(nibble & 1));
output_bit(LCD_DB5, !!(nibble & 2));
output_bit(LCD_DB6, !!(nibble & 4));
output_bit(LCD_DB7, !!(nibble & 8));
delay_cycles(1);
output_high(LCD_E);
delay_us(2);
output_low(LCD_E);
}
//-----------------------------------
// This sub-routine is only called by lcd_read_byte().
// It's not a stand-alone routine. For example, the
// R/W signal is set high by lcd_read_byte() before
// this routine is called.
#ifdef USE_LCD_RW
BYTE lcd_read_nibble(void)
{
BYTE retval;
// Create bit variables so that we can easily set
// individual bits in the retval variable.
#bit retval_0 = retval.0
#bit retval_1 = retval.1
#bit retval_2 = retval.2
#bit retval_3 = retval.3
retval = 0;
output_high(LCD_E);
delay_cycles(1);
retval_0 = input(LCD_DB4);
retval_1 = input(LCD_DB5);
retval_2 = input(LCD_DB6);
retval_3 = input(LCD_DB7);
output_low(LCD_E);
return(retval);
}
#endif
//---------------------------------------
// Read a byte from the LCD and return it.
#ifdef USE_LCD_RW
BYTE lcd_read_byte(void)
{
BYTE low;
BYTE high;
//output_high(LCD_RW);
delay_cycles(1);
high = lcd_read_nibble();
low = lcd_read_nibble();
return( (high<<4) | low);
}
#endif
//----------------------------------------
// Send a byte to the LCD.
void lcd_send_byte(BYTE address, BYTE n)
{
output_low(LCD_RS);
#ifdef USE_LCD_RW
while(bit_test(lcd_read_byte(),7)) ;
#else
delay_us(60);
#endif
if(address)
output_high(LCD_RS);
else
output_low(LCD_RS);
delay_cycles(1);
#ifdef USE_LCD_RW
output_low(LCD_RW);
delay_cycles(1);
#endif
output_low(LCD_E);
lcd_send_nibble(n >> 4);
lcd_send_nibble(n & 0xf);
}
//----------------------------
void lcd_init(void)
{
BYTE i;
output_low(LCD_RS);
#ifdef USE_LCD_RW
output_low(LCD_RW);
#endif
output_low(LCD_E);
delay_ms(15);
for(i=0 ;i < 3; i++)
{
lcd_send_nibble(0x03);
delay_ms(5);
}
lcd_send_nibble(0x02);
for(i=0; i < sizeof(LCD_INIT_STRING); i++)
{
lcd_send_byte(0, LCD_INIT_STRING[i]);
// If the R/W signal is not used, then
// the busy bit can't be polled. One of
// the init commands takes longer than
// the hard-coded delay of 60 us, so in
// that case, lets just do a 5 ms delay
// after all four of them.
#ifndef USE_LCD_RW
delay_ms(5);
#endif
}
}
//----------------------------
void lcd_gotoxy(BYTE x, BYTE y)
{
BYTE address;
if(y != 1)
address = lcd_line_two;
else
address=0;
address += x-1;
lcd_send_byte(0, 0x80 | address);
}
//-----------------------------
void lcd_putc(char c)
{
switch(c)
{
case '\f':
lcd_send_byte(0,1);
delay_ms(2);
break;
case '\n':
lcd_gotoxy(1,2);
break;
case '\b':
lcd_send_byte(0,0x10);
break;
default:
lcd_send_byte(1,c);
break;
}
}
//------------------------------
#ifdef USE_LCD_RW
char lcd_getc(BYTE x, BYTE y)
{
char value;
lcd_gotoxy(x,y);
// Wait until busy flag is low.
while(bit_test(lcd_read_byte(),7));
output_high(LCD_RS);
value = lcd_read_byte();
output_low(lcd_RS);
return(value);
}
#endif
|
and keypad code:
Code: |
#define col0 PIN_B4 // columns are output
#define col1 PIN_B5
#define col2 PIN_B6
#define row0 PIN_B0 // rows are as input
#define row1 PIN_B1
#define row2 PIN_B2
#define row3 PIN_B3
// Keypad layout:
char get_key() {
char k;
delay_ms(50);
k='H'; // a dummy value ,if no key is pressed
output_high(col0);
output_high(col1);
output_high(col2);
delay_ms(50);
output_low(col0);
delay_ms(10);
port_b_pullups(true);
if(input(row0)==0)
{delay_ms(50);if(input(row0)==0) {k= '1'; return (k);}}
else if(input(row1)==0)
{delay_ms(50);if(input(row1)==0) {k= '4';return (k);}}
else if(input(row2)==0)
{delay_ms(50);if(input(row2)==0) {k= '7';return (k);}}
else if(input(row3)==0)
{delay_ms(50);if(input(row3)==0) {k= '*';return (k);}}
output_high(col0);
output_low(col1);
delay_ms(10);
if(input(row0)==0)
{delay_ms(50);if(input(row0)==0) {k= '2';return (k);}}
else if(input(row1)==0)
{delay_ms(50);if(input(row1)==0) {k= '5';return (k);}}
else if(input(row2)==0)
{delay_ms(50);if(input(row2)==0) {k= '8';return (k);}}
else if(input(row3)==0)
{delay_ms(50);if(input(row3)==0) {k= '0';return (k);}}
output_high(col1);
output_low(col2);
delay_ms(10);
if(input(row0)==0)
{delay_ms(50);if(input(row0)==0) {k= '3';return (k);}}
else if(input(row1)==0)
{delay_ms(50);if(input(row1)==0) {k= '6';return (k);}}
else if(input(row2)==0)
{delay_ms(50);if(input(row2)==0) {k= '9';return (k);}}
else if(input(row3)==0)
{delay_ms(50);if(input(row3)==0) {k= '#';return (k);}}
else {delay_ms(10); return('H');}}
|
|
|
![](templates/subSilver/images/spacer.gif) |
temtronic
Joined: 01 Jul 2010 Posts: 9174 Location: Greensville,Ontario
|
|
Posted: Mon Sep 23, 2013 6:34 pm |
|
|
Easy answer..
there's a 'bug' in Proteus !
Really, if all 11 of the other keypad presses are OK, and only the '0' is bad, then it MUST be another Proteus bug.Simple basic logic.
Since you're not dealing with a real PIC, LCD module,and keypad and the ONLY hardware is really software( Proteus), the conclusion is that Proteus is once again at fault.Please read PIC101 sticky.
Also...
both the keypad and LCD driver's that CCS supply work 100% with real PIC,keypad and LCD modules.They have for years.That they 'don't work' using Proteus again shows several flaws in Proteus.
hth
Jay |
|
![](templates/subSilver/images/spacer.gif) |
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Mon Sep 23, 2013 6:47 pm |
|
|
Quote: | I am using ccs compiler PCM 4.114 and using a PIC 16F887 uC.
char get_key() {
char k;
delay_ms(50);
k='H'; // a dummy value ,if no key is pressed
output_high(col0);
output_high(col1);
output_high(col2);
delay_ms(50);
output_low(col0);
delay_ms(10);
port_b_pullups(true);
if(input(row0)==0)
|
I can't help you with Proteus but using 'true' for port_b_pullups() is wrong.
The 16F887 data sheet says:
Quote: |
3.4.2 WEAK PULL-UPS
Each of the PORTB pins has an individually configurable
internal weak pull-up. Control bits WPUB<7:0> enable or
disable each pull-up (see Register 3-7). |
This means you don't use 'True' or 'False' as a parameter for
port_b_pullups() with this PIC.
The CCS manuals says:
Quote: | port_x_pullups ( )
Syntax:
port_b_pullups (value)
Parameters: value is TRUE or FALSE on most parts, some parts that allow
pullups to be specified on individual pins permit an 8 bit int here,
one bit for each port pin.
|
This means if you want to turn on all pullups on PortB, you need to use
a parameter of 0xFF instead of 'True'.
Also, the PortB pullups are weak and do not pull up instantly. It takes
a little time, depending on the amount of pin capacitance and any other
capacitance in the trace or the device that it's connected to. Therefore,
add a small delay after enabling the pull-ups. 10 usec should be enough.
Quote: |
port_b_pullups(0xFF); // Turn on all pull-ups
delay_us(10); // Allow time for them to pull up
if(input(row0)==0) |
|
|
![](templates/subSilver/images/spacer.gif) |
Shaheers
Joined: 23 Sep 2013 Posts: 14
|
|
Posted: Tue Sep 24, 2013 7:56 pm |
|
|
Thanks for the reply, there is a minor confusion that can we call functions in a functions freely or is there any limit eg.
Code: |
main()
{
function1();
}
function1(){
instructions;
function2();
}
function3(){
instructions;
function1();
}
|
|
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19369
|
|
Posted: Wed Sep 25, 2013 2:03 am |
|
|
Start with this:
Code: |
void main(void)
{
//One huge unbroken lump of code
}
|
This immediately has 'limits', since the chip ROM is 'paged', and even if you have lots of spare space in the chip, the 'unbroken lump', can't be split up to go onto separate pages...
Then:
Code: |
void one_function(void)
{
}
void another_function(void)
{
}
void yetanother_function(void)
{
}
void main(void)
{
one_function();
another_function();
yetanother_function();
....
//yet more functions
}
|
This allows the code to be broken up, and is effectively unlimited, since no function calls another, and the compiler is 'smart enough' to actually combine multiple functions to be a single code block. It puts the bits together to try to minimise the overheads involved.
Then:
Code: |
void one_function(void)
{
//Possibly more functions
}
void another_function(void)
{
one_function();
}
void yetanother_function(void)
{
another_function();
}
void main(void)
{
yetanother_function();
....
//yet more functions
}
|
Now here each function is called inside another. Potentially this uses 'stack' space, to hold the return address. The PIC has a limited amount of stack storage (depends on the chip you are using). Again though the compiler is 'smart', and if single functions call functions in only one place, will actually build them to be compiled 'inline' to save stack levels. Generally unless you have lots of functions called from lots of locations, each calling lots more functions inside themselves, this is unlikely to cause problems, _but it can_. At the top of the .lst file generated when you compile a program, there is a little line, telling you the 'worst case' stack usage, and the maximum size, so you can check this is not a problem.
Then:
Code: |
void one_function(void)
{
yetanother_function();
}
void another_function(void)
{
}
void yetanother_function(void)
{
one_function();
}
void main(void)
{
yetanother_function();
....
//yet more functions
}
|
This is not allowed.
Here when you call 'yetanother_function', it calls 'one_function', which then calls 'yetanother_function'. So it is tryiing to run 'yetanother_function', inside itself. This is called 'recursion'. Now, more complex processors, have 'data stacks', (as well as the simple 'call stack' on the PIC). With these you can save multiple copies of the variables used inside a function on this stack, and recursion can be supported. On the PIC (except on the larger DsPIC's), there is no hardware support for this, so 'recursion' is not permitted. Even on these other processors, there are problems though, since recursion can result in the stack required growing exponentially.....
So, there are limits everywhere. Generally 'logic' is your friend. Since the compiler will attempt to put small bits of code, even if declared as a separate function 'inline' (put them where they are called), being logical by having small functions to make it easy to understand a simple operation as an 'entity', helps code clarity, and costs nothing.
There is another limitation. Any function called inside an interrupt routine, can (effectively) be called anywhere from the main code. If you use the same function in the external code, this could potentially cause recursion. Hence the compiler, will automatically _disable interrupts_ inside the function, if it is called in the main code.
Best Wishes |
|
![](templates/subSilver/images/spacer.gif) |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|