This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

scanf behaviour

I am new to C programming. I wrote calculator program to check my ability in c. I am using ADUC831 demo board, 5x5 matrix keypad, 16x2 LCD in 4 bit mode. following is the code listing. program when run, executes printf statement to show "num1" (checked this by inserting delay) but do not executes scanf to receive float number fl1 and proceeds to display "operator>" and receives variable "operator". on further execution it displays num2> on LCD but skips execution of scanf to receive float no fl2 and directly shows result float no fr. Pl. indicate where am I wrong? Is there a better way to scan keyboard then the one I adopted?
Thanks

listing getkey.c
char _getkey (void)
{
while (!kbhit); /* wait for a character */
kbhit = 0 ; /* clear receive interrupt bit */
return(scancode) ;
}
listing putchar.c
#include <ADI\ADUC831.h>
#include "kbd.h"
#include "lcd.h"

#define	MAX_COL_NUM	0x0f
#define	MAX_ROW_NUM	0x02

#define	CR	0x0d
#define	LF	0x0a
#define	FORM_FEED	0x0c
#define	BACKSPACE	0x08
unsigned char g_byRow = 0 ;
unsigned char g_byCol = 0 ;

unsigned char putchar (unsigned char c)
{
	if (c == CR)	{
		g_byCol = 0 ;
		if (g_byRow < MAX_ROW_NUM)
			g_byRow ++ ;
	}

	else if (c == FORM_FEED)
	{
		lcd_cmd(0x80) ;
		for(c = 0 ; c < 16 ; c ++)
			lcd_dat(' ') ;
		lcd_cmd(0xc0) ;
		for(c = 0 ; c < 16 ; c ++)
			lcd_dat(' ') ;
		g_byRow = 0 ;
		g_byCol = 0 ;
		c = FORM_FEED ;
	}
	else if (c == BACKSPACE)
	{
		if (g_byCol)
		{
			g_byCol -- ;
			if (g_byRow == 0)
				lcd_cmd(0x80 + g_byCol) ;
			else
				lcd_cmd(0xc0 + g_byCol) ;
			lcd_dat(' ') ;
		}
	}
	else
	{
		if (g_byRow == 0)
			lcd_cmd(0x80 + g_byCol) ;
		else
			lcd_cmd(0xc0 + g_byCol) ;
		lcd_dat(c) ;
		g_byCol ++ ;
		if (g_byCol > MAX_COL_NUM)
		{
			g_byCol = 0 ;
			if (g_byRow < MAX_ROW_NUM)
				g_byRow ++ ;
		}
	}
	return(c) ;
}


 Listing kbd.h
extern bit kbhit ;
extern char scancode ;
 listing lcd.h
void lcd_cmd1 (unsigned char cmd) ;
void lcd_dat1 (unsigned char dat) ;
void lcd_cmd (unsigned char cmd) ;
void lcd_dat (unsigned char dat) ;
void lcd_init () ;
void delay (unsigned int ms) ;
Listing kbd 5x5.c
#include <ADI\ADUC831.h>
#include <stdio.h>

unsigned char scancode ;
bit kbhit = 0 ;

unsigned char g_byScanCount = 0 ;
unsigned char g_byTheScanCount ;
unsigned char g_byRetCode ;
bit g_bKeyIsDown = 0 ;

// upper 3 bits of P0 and upper 2 bits of P2 are scan lines
sbit	SL0	= P0 ^ 5 ;
sbit	SL1	= P0 ^ 6 ;
sbit	SL2	= P0 ^ 7 ;
sbit	SL3	= P2 ^ 7 ;
sbit	SL4	= P2 ^ 6 ;
// lower 5 bits of P0 are return lines

const unsigned char KeyCodes[25] =
{
	0x0d,	// CR
	'F',
	'G',
	'H',
	8,		// backspace
	'D',
	'E',
	'-',
	'*',
	'/',
	'C',
	'+',
	'3',
	'6',
	'9',
	'B',
	'.',
	'2',
	'5',
	'8',
	'A',
	'0',
	'1',
	'4',
	'7'
} ;

void timer0 (void) interrupt 1 using 1
{
// this interrupt will occur every 10 ms (assuming ADuC831 @ 11.0592 MHz)
	TH0 = 0xDC ;
	if (g_byScanCount == 0)
		SL0 = 0 ;
	else if (g_byScanCount == 1)
		SL1 = 0 ;
	else if (g_byScanCount == 2)
		SL2 = 0 ;
	else if (g_byScanCount == 3)
		SL3 = 0 ;
	else
		SL4 = 0 ;
	#pragma asm
		nop ;
		nop ;
		nop ;
	#pragma endasm	// small delay to allow the scan lines to settle

	if (g_bKeyIsDown)
	{
		if (g_byTheScanCount == g_byScanCount)
		{
			if ((P0 & 0x1f) == 0x1f)
				g_bKeyIsDown = 0 ;
		}
	}
	else
	{
		g_byRetCode = P0 & 0x1f ;
		if (g_byRetCode == 0x1e)
			g_byRetCode = 0 ;
		else if (g_byRetCode == 0x1d)
			g_byRetCode = 1 ;
		else if (g_byRetCode == 0x1b)
			g_byRetCode = 2 ;
		else if (g_byRetCode == 0x17)
			g_byRetCode = 3 ;
		else if (g_byRetCode == 0x0f)
			g_byRetCode = 4 ;
		else
			g_byRetCode = 5 ;	// illegal return code
		if (g_byRetCode != 5)
		{
			scancode = KeyCodes[(g_byRetCode * 5) + g_byScanCount] ;
			g_bKeyIsDown = 1 ;
			g_byTheScanCount = g_byScanCount ;
			kbhit = 1 ;
		}
	}
	g_byScanCount ++ ;
	if (g_byScanCount > 4)
		g_byScanCount = 0 ;
	SL0 = 1 ;
	SL1 = 1 ;
	SL2 = 1 ;
	SL3 = 1 ;
	SL4 = 1 ;
}
 Listing 16x2lcd.c
#include <ADI\ADUC831.h>
#include "lcd.h"
#include "kbd.h"
unsigned char p2data = 0xff ;

void delay (unsigned int ms) {
unsigned int i = 0	;
for (i=0; i < ms ; i++) {
}
}

void lcd_cmd1 (unsigned char cmd)
{
// sends the 4 LSBits of cmd as a (half) command to the LCD
	p2data = (p2data & 0xc0) | (cmd & 0x0f) ;
	P2 = p2data ;
	p2data |= 0x10 ;	// EN high
	P2 = p2data ;
	p2data &= 0xef ;	// EN low
	P2 = p2data ;
}
void lcd_dat1 (unsigned char dat)
{
// sends the 4 LSBits of dat as a (half) data to the LCD
	p2data = (p2data & 0xc0) | (dat & 0x0f) | 0x20 ;	// A0 = high
	P2 = p2data ;
	p2data |= 0x10 ;	// EN high
	P2 = p2data ;
	p2data &= 0xef ;	// EN low
	P2 = p2data ;
}
void lcd_cmd (unsigned char cmd)
{
// sends cmd as a command to LCD
	lcd_cmd1(cmd >> 4) ;
	lcd_cmd1(cmd & 0x0f) ;
	delay(125) ;
}
void lcd_dat (unsigned char dat)
{
// sends dat as data to LCD
	lcd_dat1(dat >> 4) ;
	lcd_dat1(dat & 0x0f) ;
	delay(125) ;
}
void lcd_init ()
{
	lcd_cmd1(0x03) ;
	delay(2750) ;
	lcd_cmd1(0x03) ;
	delay(125) ;
	lcd_cmd1(0x03) ;
	delay(125) ;
	lcd_cmd1(0x02) ;
	delay(125) ;

	lcd_cmd(0x28) ;
	lcd_cmd(0x08) ;
	lcd_cmd(0x0c) ;
	lcd_cmd(0x06) ;
}
 Listing Calci.c
#include <ADI\ADUC831.h>
#include "kbd.h"
#include "lcd.h"
#include <stdio.h>
/* global variables */
#define	CR	0x0d
#define	LF	0x0a
#define	FORM_FEED	0x0c
#define	BACKSPACE	0x08

unsigned char operator ;
float fl1, fl2, fr;

void main ()
{
	TH0 = 0xDC ;	// for interrupt every 10 ms (assuming ADuC831 @ 11.0592 MHz)
	TL0 = 0x00 ;
	TMOD = 0x21 ;	// t0 = 16 bit timer, t1 = 8 bit auto-reload timer
	TCON = 0x55 ;
	IE = 0x82 ;		// enable timer0 interrupt
	lcd_init() ;
	while(1) 			// infinite loop
	{
		putchar(FORM_FEED) ;
		printf("Num1> ");
		scanf("%f",&fl1) ;			// get the first number from serial port
		putchar(FORM_FEED) ;
		printf("Operator> ") ;
		scanf("%c",&operator);	// get the operator from the serial port
		putchar(FORM_FEED) ;
		printf("Num2> ") ;
		scanf("%f",&fl2) ;			// get the second number from serial port
		putchar(FORM_FEED) ;
		switch(operator)			// according to the operator do the calculations
		{
			case	'+'	:	fr = fl1 + fl2 ;
						break ;
			case	'-'	:	fr = fl1 - fl2 ;
						break ;
			case	'*'	:	fr = fl1 * fl2 ;
						break ;
			case	'/'	:	fr = fl1 / fl2 ;
						break ;
			default	:	printf("Illegal operator") ;
			// if none of the above operators are present then the
			// entered operator is an illegal operator
		}	// end of switch

			// send the answer to the serial port
			// show 6 places after decimal point
		printf("\r= %.6f",fr) ;
		getchar() ;
	}		// end of while
}

  • I don't know for sure what your problem is, but do remember that if scanf() encounters a character that doesn't match its format string it terminates leaving that character unread in the input buffer. The next scanf() call will see that character and terminate if it doesn't match its format string, and so on.

    If I were you I would try reading input with _getkey() to make sure that you are receiving what you think you should be receiving. If you can't guarantee the format of data it might be better to read incoming data into a buffer and 'preprocess' it into a format you are sure is suitable before passing it to sscanf().

  • Thanks for reply. I tried to debug code by changing statement scanf("%f",&fl1) ; with scanf("%d",&fl1) ; and run and accepted decimal input but has skipped the scanf("%c",&operator); statement while running. This shows that, if scanf is working for character input then it skips float input and if it works for decimal input then it stops working for character input. Why it is behaving like this? I was not able to trace error. Pl. Help. Thanks.

  • version used is
    uvision2 : 2.30
    C51.exe : 7.00
    A51.exe : 7.00a
    BL51.exe : 5.00
    LIB51.exe : 4.23
    OH51.exe : 2.6

  • Dear Stefan Duncanson,
    I am reading character & passing it to "scancode" variable (code listing kbd 5x5.c) and th' _getkey passing it to scanf for format processing. It seems I have made mistake in declaring _getkey function but I am unble to trace that. It would be of great help if you / anyone can indicate what shall I do to make my program working.

  • Your _getkey() function looks ok to me, why do you think there's something wrong with it?

    However, I don't see how you are able to get the program to compile and link successfully as your getkey.c file doesn't see an extern declaration of 'scancode'.

    Can you:
    1) Verify that the source you have posted is the exact same code you are using. If not, post the real code.
    2) Post any warnings/errors you are getting.
    3) Try my suggestion above of replacing the scanf() calls with a loop that reads the keyboard with _getkey() and outputs each character through the serial port to verify that you are getting the right data from the keyboard.
    4) Test the return value of scanf() to see whether it thinks it is performing successful conversions.

  • Another thing, that inline assembler is probably making life difficult. You can use the 'intrinsic' _nop_() function instead, refer to the manual for details.

  • Dear Stefan Duncanson,
    Sorry for the inconvenience, I missed one line in while cut & paste getkey.c code that is

    #include "kbd.h"
    . Rest of the code is the same as what I am compiling.
    2) Post any warnings/errors you are getting.
    I am getting following message
    Program Size: data=91.4 xdata=0 code=3761
    creating hex file from "CALCI"...
    "CALCI" - 0 Error(s), 0 Warning(s).
    Regarding sug. 3 -- when I format scanf to accept decimal number instead of float, keypad keys pressed are correctly reflected on LCD. but then it stops accepting %c format to accept +, -, /, * signs assigned to keys and skips scanf containing %c formats. Also I can not enter float numbers. This led me to think that may I have declared wrong variable format in _getkey.
    Pl. suggest how to impliment your sug.4 i.e. test the return value of scanf().

  • Dear Stefan Duncanson,
    I replaced nop instructions with _nop_() of INTRINS.H but there is no change in behaviour of code.

  • I can explain why the scanf("%c",&operator) call isn't waiting: the whitespace character that terminated the previous scanf() call is being read instead. If you add a space before the %c it will work properly.

    I can't explain why the scanf("%f",&fl1) isn't working, though, other than a bug in the compiler. I can replicate your problem with the following code on C51 V7.01 if I compile spefically at optimisation level 2:

    #include <reg52.h>
    #include <stdio.h>
    
    char _getkey(void)
    {
       char c;
       while (!RI);
       c = SBUF;
       RI = 0;
       return(c);
    }
    
    void main(void)
    {
       float flt;
    
       Setup(); //Initialise serial port
       while(1)
       {
          scanf("%f",&flt);
          printf("\n%.6f",flt);
       }
    }
    

    In a more complex program I also see the problem at optimisation level 9.

  • "I replaced nop instructions with _nop_() of INTRINS.H but there is no change in behaviour of code."

    I didn't mean to imply that you would see any difference, it just means you don't have to go through that business of generating a .src file and assembling, or whatever it is you have to do.

  • I didn't mean to imply that you would see any difference, it just means you don't have to go through that business of generating a .src file and assembling, or whatever it is you have to do. & routine to test compiler scanf() behaviour --->
    Thanks, Being my first project on keil & 'C' for microcontroller, It really helped me to understand how keil compiler behaves at diff. optimising levels. Ultimately I found that C51FPS.lib was the culprit. It was got corrupted. Thanks once again

  • "Ultimately I found that C51FPS.lib was the culprit. It was got corrupted."

    That certainly isn't the problem I'm seeing.

    What seems to be going wrong for me is that data space used by both atof() and _getkey() is being incorrectly overlaid. I've tested C51 v7.20 and the problem exists there also. Optimisation level needs to be set to 2, if 9 is used (in the code I posted above) the local variable in _getkey() is optimised into a register making the problem go away.

    Anyone from Keil care to comment?

  • You are right. Problem is still there. I can use level 1 max. On selecting level 2 float number is getting multiply by 10^12 i.e. if float number is 1.234 then output will be 1234000000000.000000 (my lib file was indeed got correpted due to which I was not able to use %f in scanf even at level 0). As being my first project with keil I thought error may be at my end.
    Anyone from Keil care to comment?
    I also agree with you for comment from keil.

  • "On selecting level 2 float number is getting multiply by 10^12 i.e. if float number is 1.234 then output will be 1234000000000.000000"

    Yes, that is exactly what I'm seeing.