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 }
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"
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); } }
"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.
Anyone from Keil care to comment? I tested this with C51 V7.50a at OT(2) and OT(3) and the value of flt when scanf returns is incorrect. Everything works fine below OT(2) and above OT(3). I've reported the problem to engineering. Jon