/* * File: sdcc_driver_test.c * Author: Terry Newton (WTN) * Created on August 10, 2012 * Last Modified October 26, 2012 (mod Sept 7 2015) */ // Public domain, provided as-is and without warranty, have fun. // // This framework provides the following functionality for PIC18F2525 apps... // define workarounds to make it easier to use SDCC with MPLAB-X // code to set up typical PIC defaults - 8mhz clock, 9600baud, analog inputs // write characters to a 2x16 LCD using a 3-wire 74HC164-based interface // (LCD code supports some Parallax-style LCD cursor/screen control bytes) // write characters to the hardware serial port // specify stdout (printf etc) destination to be LCD or the serial port // read chars from the serial port with specified timeout (or no timeout) // enter strings from the serial port with backspace and buffer limit // read values from analog inputs (raw or averaged) // write and read bytes to and from the internal eeprom // millisecond delay routine for non-critical timing // Using a PIC18F2525 processor, if different check the eeprom_write // function to verify or edit the hardcoded register addresses. // // Sample app code... // A simple test for the drivers, for the pickit 28-pin demo board // with a 3-wire 74HC164-based (8-bit) LCD interface connected to // RB5(enable),RB6(clock),RB7(data/rs) and serial port interfaced // using resistors (see comments in LCD/serial sections for hardware). // Sample app flashes RB0-RB3 LED's on 1 second off 1 second to verify timing // Tests eeprom code - writes/reads locs 0,255,1023, lights RB0,RB1 if OK // Prints message on LCD via strcpy/printf_tiny, lights RB2 if string OK // Checks to see if someone on a terminal, if present then prompts for // serial string and char input. Main loop reads AN0 and lights RB3 if // greater than half scale, prints values to serial output. // to avoid MPLAB-X error flags, includes must specify full path to files //#include "/usr/local/share/sdcc/non-free/include/pic16/pic18f2525.h" #include "/usr/local/share/sdcc/include/pic16/pic18fregs.h" #include "/usr/local/share/sdcc/include/pic16/stdio.h" #include "/usr/local/share/sdcc/include/pic16/stdlib.h" #include "/usr/local/share/sdcc/include/pic16/math.h" #include "/usr/local/share/sdcc/include/pic16/string.h" // note: libc18f.lib libm18f.lib must be added to "Additional options" line // new style config line... (options listed in gpasm's p18f2525.inc file) #pragma config OSC=INTIO67,MCLRE=ON,WDT=OFF,PWRT=ON,BOREN=OFF,LVP=OFF,XINST=OFF //to avoid MPLAB-X error flags.. use xxx_REG/BIT instead #define INTCON_REG INTCON #define INTCON_BITS INTCONbits #define GIE_BIT INTCONbits.GIE #define EECON2_REG EECON2 #define WR_BIT EECON1bits.WR #define RD_BIT EECON1bits.RD #define WREN_BIT EECON1bits.WREN #define EEPGD_BIT EECON1bits.EEPGD #define CFGS_BIT EECON1bits.CFGS #define EEDATA_REG EEDATA #define EEADRL_REG EEADR #define EEADRH_REG EEADRH #define OSCCON_REG OSCCON #define ADCON0_REG ADCON0 #define ADCON1_REG ADCON1 #define ADRESL_REG ADRESL #define ADRESH_REG ADRESH #define ADFM_BIT ADCON2bits.ADFM #define GODONE_BIT ADCON0bits.GO #define CMCON_REG CMCON #define WDTCON_REG WDTCON #define PORTA_REG PORTA #define PORTB_REG PORTB #define PORTC_REG PORTC #define LATA_REG LATA #define LATB_REG LATB #define LATC_REG LATC #define TRISA_REG TRISA #define TRISB_REG TRISB #define TRISC_REG TRISC #define CCP1CON_REG CCP1CON #define CCP2CON_REG CCP2CON #define LATA_BITS LATAbits //access like LATA_BITS.LATA0 #define LATB_BITS LATBbits #define LATC_BITS LATCbits #define PORTA_BITS PORTAbits #define PORTB_BITS PORTBbits #define PORTC_BITS PORTCbits #define TRISA_BITS TRISAbits #define TRISB_BITS TRISBbits #define TRISC_BITS TRISCbits #define SPBRG_REG SPBRG #define SPBRGH_REG SPBRGH #define BRG16_BIT BAUDCONbits.BRG16 #define BRGH_BIT TXSTAbits.BRGH #define SYNC_BIT TXSTAbits.SYNC #define SPEN_BIT RCSTAbits.SPEN #define CREN_BIT RCSTAbits.CREN #define TXEN_BIT TXSTAbits.TXEN #define FERR_BIT RCSTAbits.FERR #define OERR_BIT RCSTAbits.OERR #define RXDTP_BIT BAUDCONbits.RXDTP #define TXCKP_BIT BAUDCONbits.TXCKP #define TXIF_BIT PIR1bits.TXIF #define RCIF_BIT PIR1bits.RCIF #define TXREG_REG TXREG #define RCREG_REG RCREG #define PLLEN_BIT OSCTUNEbits.PLLEN //set for 32mhz clock #define RXIN_BIT PORTCbits.RX //for detecting if serial connected //application default pin states/directions // RB5 = output LCD E default 0 // RB6 = output LCD CLK default 0 // RB7 = output LCD DAT/RS default 0 #define portadirs 0b11111111 #define portbdirs 0b00000000 //RB5-RB7=out for LCD #define portcdirs 0b10111111 //RC7=in RC6=out for serial #define portadefaults 0b00000000 #define portbdefaults 0b00000000 #define portcdefaults 0b00000000 #define LCDenable LATBbits.LATB5 #define LCDclock LATBbits.LATB6 #define LCDdata LATBbits.LATB7 //application declarations void setup(void); void loop(void); //driver declarations - delete whatever isn't used void default_setup(void); //set up PIC features and pins int analogRead(unsigned char analog_channel); //averaged analog read int readanalog(unsigned char analog_channel); //raw analog read void dodelay(unsigned int count); //micro delay void delayms(unsigned int ms); //millisecond delay #define mscalibrate 176 //number for dodelay to equal 1ms //note - set mscalibrate so that delayms(1000) delays for one second //void putchar(char arg) __wparam; //stdout link (leave commented) //char getchar(void); //stdin link (leave commented) unsigned char outdest; //stream out dest 0=LCD, not 0=serial void putch(unsigned char c); //write byte to LCD void initLCD(void); //initialize LCD void setLCDshift(unsigned char c); //write byte to LCD data lines void sendLCDcommand(unsigned char c); //send command byte to LCD void sendLCDdata(unsigned char c); //send data byte to LCD void eeprom_write (unsigned int address, unsigned char data); //write to eeprom unsigned char eeprom_read(unsigned int address); //read from eeprom void serialsetup(void); // initialize hardware serial port (9600/8mhz) void serialout(unsigned char c); // serial char out unsigned char serialin(unsigned int timeout); //serial char in void serialgetline(char * buffer, unsigned char maxchars); //serial line input //global app variables... unsigned int counter; //for demo char string[17]; //for demo void setup() { // add application setup code here // delete all this stuff for new app unsigned int i; unsigned char chartmp; // test delayms timing for (i=0;i<2;i++) { LATB_REG=0b00001111; delayms(1000); LATB_REG=0b00000000; delayms(1000); } // test eeprom read/write b0/b1 should light eeprom_write(0,100); eeprom_write(255,10); eeprom_write(1023,150); i=eeprom_read(0)+eeprom_read(255)+eeprom_read(1023); if (i==260) { LATB_BITS.LATB0 = 1; } eeprom_write(0,200); eeprom_write(255,20); eeprom_write(1023,100); i=eeprom_read(0)+eeprom_read(255)+eeprom_read(1023); if (i==320) { LATB_BITS.LATB1 = 1; } // test strcpy b2 should light and LCD should display message initLCD(); strcpy(string, "Hello World"); printf_tiny(string); if (string[1]=='e') { LATB_BITS.LATB2 = 1; } // test serial functions // (serial already initialized in default_setup) outdest=1; //specify serial stream output printf_tiny("\r\nHello Serial Port"); //to get single char with timeout... printf_tiny("\r\nPress a key if you're there\r\n"); chartmp=serialin(2500); //about 5 seconds to respond if (chartmp==0) { printf_tiny("Nobody home"); } else { printf_tiny("Someone's there..."); // string input... printf_tiny("\r\nEnter a string: "); serialgetline(string,16); //make sure number is less than array size printf_tiny("\r\nYou entered: "); printf_tiny(string); //to get single char with no timeout... printf_tiny("\r\nType a key...\r\n"); chartmp=serialin(0); // specify 0 for no timeout printf_tiny("Ascii code=%d",chartmp); } printf_tiny("\r\n"); outdest=0; //specify LCD stream output counter=0; //global counter for serial output messages } void loop() { // add application main loop code here // delete all this stuff for new app int value; //test analog input value=analogRead(0); if (value>511) { LATB_BITS.LATB3 = 1; } else { LATB_BITS.LATB3 = 0; } //print value on LCD putch(148); //position to line 2 printf_tiny("%d ",value); LCDdata = 0; //make it easier to see changes without LCD connected //occasionally print value to serial //(no error if serial not connected) counter++; if (counter > 300) { counter = 0; outdest=1; printf_tiny("%d\r\n",value); outdest=0; } delayms(7); } //=================== driver code ============================== //the following may be used for any purpose without restrictions //not subject to application copyright unsigned char LCDcurrentline; // global var for CR support void default_setup(void) { // startup code to set up typical PIC features... // 8mhz clock, analog AN0-4,AN8, rest digital I/O // port[a|b|c]defaults and port[a|b|c]dirs must be defined // requires _REG/_BIT defines for MPLAB compatibility INTCON_REG = 0; // no interrupts OSCCON_REG = 0b01110000; // set for 8mhz clock ADCON1_REG = 0b00000110; // use AN0-4,AN8, rest digital CMCON_REG = 7; // no comparators CCP1CON_REG = 0; // no capture CCP2CON_REG = 0; LATA_REG = portadefaults; // set default pin states LATB_REG = portbdefaults; LATC_REG = portcdefaults; TRISA_REG = portadirs; // set pin directions TRISB_REG = portbdirs; TRISC_REG = portcdirs; outdest = 0; //default LCD output - remove if no LCD code serialsetup(); //set up serial port - remove if no serial code } int analogRead(unsigned char analog_channel) { int analog_value,g,h; analog_value=readanalog(analog_channel); analog_value=0; for(g=0;g<10;g++) { analog_value+=readanalog(analog_channel); for(h=0;h<2;h++); } analog_value=analog_value/10; return analog_value; } int readanalog(unsigned char analog_channel) { // read analog input, channel = 0 for AN0 etc // does not set up port I/O, just reads int analog_value; ADCON0_REG = (int) analog_channel * 4 + 1; ADFM_BIT = 1; // right justified GODONE_BIT = 1; // start conversion while (GODONE_BIT); // wait to complete analog_value = ADRESL_REG; analog_value = analog_value + (ADRESH_REG * 256); return analog_value; } void dodelay(unsigned int count) { while(count--); } // define mscalibrate constant for number of loops for 1ms delay // note... not for accurate timing, just for gp message delays etc void delayms(unsigned int ms) { unsigned int i; for(i=0;i RS +5V >---*---0.1u--. RS,E,D0-D7 = LCD lines // | __ __ | _|_ RW,V- grounded, +5V to V+ // RB7 >--*------------*---| U |--* Connect VO line to a 5K // `---| |--|--> D7 trimmer w/10K from +5V to // RB5 >--*--> E D0 <----| |--|--> D6 supply 0-1.6V for contrast // | D1 <----| |--|--> D5 If backlight connect A to // 10K D2 <----| |--|--> D4 ~33 ohms to +5V, ground K // _|_ D3 <----| |--' // .--|_____|--. Typical 16x2 LCD Digikey # 67-1758 // _|_ 74HC164 | With assignments shown compatible // RB6 >----------------------------' with in-circuit programming // // Partial support for Parallax-like LCD commands... // 8 move cursor left // 9 move cursor right // 12 clear screen // 21 turn off display // 22 turn on display (cursor off, blink off) // 23 turn on display (cursor off, blink on) // 24 turn on display (cursor on, blink off) // 25 turn on display (cursor on, blink on) // 128 position to 1st line (129-143 for chars 2-15) // 148 position to 2nd line (149-163 for chars 2-15) // 13 position to "next" line (alternates lines, does not clear) // does not support line feed or other fancy stuff // (Parallax is a trademark of the Parallax Inc. company, // who makes a serial LCD that implements similar commands.) // // Usage... // initLCD(); // use printf to print strings // use putch to send command numbers // Uses dodelay(loops) and delayms(ms) subroutines void putch(unsigned char c) { unsigned char numberofshifts; if (c==13) { //cr code... if (LCDcurrentline == 0) { sendLCDcommand(192); LCDcurrentline = 1; } else { sendLCDcommand(128); LCDcurrentline = 0; } } else if (c==12) { //12 = clear display sendLCDcommand(1); LCDcurrentline = 0; } else if (c==8) sendLCDcommand(0b00010000); //8 = cursor left else if (c==9) sendLCDcommand(0b00010100); //9 = cursor right else if (c==21) sendLCDcommand(0b00001000); //21 = turn off display else if (c==22) sendLCDcommand(0b00001100); //22 = turn on display else if (c==23) sendLCDcommand(0b00001101); //23 = cursor off blink on else if (c==24) sendLCDcommand(0b00001110); //24 = cursor on blink off else if (c==25) sendLCDcommand(0b00001111); //25 = cursor on blink on else if (c>127) { if (c<148) { //positions on 1st line 128-143 sendLCDcommand(128); LCDcurrentline = 0; numberofshifts=c-128; while (numberofshifts) { sendLCDcommand(0b00010100); numberofshifts--; } } else { //positions on 2nd line 148-163 sendLCDcommand(192); LCDcurrentline = 1; numberofshifts=c-148; while (numberofshifts) { sendLCDcommand(0b00010100); numberofshifts--; } } } else sendLCDdata(c); } void initLCD(void) { setLCDshift(0); LCDenable=1; delayms(50); LCDenable=0; sendLCDcommand(0b00111000); delayms(10); sendLCDcommand(0b00111000); delayms(10); sendLCDcommand(0b00111000); delayms(10); sendLCDcommand(0b00111000); delayms(10); sendLCDcommand(0b00000100); sendLCDcommand(0b00001100); sendLCDcommand(0b00000001); LCDcurrentline = 0; } void setLCDshift(unsigned char c) { int i; LCDclock = 0; for (i=0;i<8;i++) { if (c & 0x80) LCDdata = 1; else LCDdata = 0; c = c << 1; LCDclock = 1; dodelay(1); LCDclock = 0; } } void sendLCDcommand(unsigned char c) { setLCDshift(c); LCDdata=0; LCDenable=1; dodelay(1); LCDenable=0; delayms(1); } void sendLCDdata(unsigned char c) { setLCDshift(c); LCDdata=1; LCDenable=1; dodelay(1); LCDenable=0; delayms(1); } // ===== EEPROM driver code ====== void eeprom_write (unsigned int address, unsigned char data) { EEADRL_REG = (unsigned char) (address & 255); EEADRH_REG = (unsigned char) (address >> 8); EEDATA_REG = data; EEPGD_BIT=0; CFGS_BIT=0; WREN_BIT=1; GIE_BIT=0; //required sequence.. addresses for PIC18F2525/2620/4525/4620 __asm__ ("MOVLW 0x55"); __asm__ ("MOVWF 0x0FA7"); //EECON2=0x55 __asm__ ("MOVLW 0x0AA"); __asm__ ("MOVWF 0x0FA7"); //EECON2=0x0AA __asm__ ("BSF 0x0FA6,1"); //WR_BIT=1 while(WR_BIT); WREN_BIT=0; GIE_BIT=1; } unsigned char eeprom_read(unsigned int address) { unsigned char data; EEADRL_REG = (unsigned char) (address & 255); EEADRH_REG = (unsigned char) (address >> 8); EEPGD_BIT=0; CFGS_BIT=0; RD_BIT=1; data=EEDATA_REG; return data; } // simple serial port setup // 9600 baud @ 8mhz clock direct connect using resistors like this... // // RC6 >---470---------> serial out db9 pin 2 // RC7 <-------*--22K--< serial in db9 pin 3 // VCC --100K--' // GND ----------------- serial gnd db9 pin 5 // void serialsetup(void) { SPBRG_REG=207; SPBRGH_REG=0; // for 32mhz the values are 64,3 BRG16_BIT=1; BRGH_BIT=1; SYNC_BIT=0; SPEN_BIT=1; CREN_BIT=1; TXEN_BIT=1; RXDTP_BIT=1; //1 for direct connect, 0 for TTL/MAX232/etc TXCKP_BIT=1; //1 for direct connect, 0 for TTL/MAX232/etc } //serial output - write a byte to hardware serial port void serialout(unsigned char c) { while (!TXIF_BIT); //wait for last byte to send TXREG_REG = c; } //serial input - read a byte from hardware serial port //call with 0 for no timeout, or specify timeout value //timout is roughly value * 2 milliseconds when serial connected //much faster if nothing connected unsigned char serialin(unsigned int timeout) { unsigned int counter1=0; unsigned char counter2=0; char chartmp; while (!RCIF_BIT) { __asm__("CLRWDT"); // no timeout if WDT enabled if (FERR_BIT||OERR_BIT) { // if error occurs (how???) chartmp=RCREG_REG; // read receive register (to null byte) and CREN_BIT=0; // clear CREN to clear error return 255; // return 255 to indicate error } if (timeout!=0) { // if a timeout value specified counter2++; if (counter2==0) { counter1++; if (counter1==timeout) { return 0; // return 0 to indicate timeout } } } } return RCREG_REG; } //serial input - accepts a line of input up to CR and copies to a string. //CR is not included in the string. Gets from serialin(0), echos to serialout //Supports ascii 8 and ascii 127 backspaces. //chars past maxchars are ignored except for BS or CR void serialgetline(char * buffer, unsigned char maxchars) { unsigned char count; char tmpchar; count=0; while(1) { //loop until it returns tmpchar=serialin(0); if (tmpchar==13) { buffer[count]=0; //terminate string return; } if (tmpchar==8 || tmpchar==127) { //backspace if (count > 0) { serialout(8); //backup serialout(32); //space serialout(8); //backup again count--; //remove char from buffer } //if at beginning of buffer just ignore } else { //add char to buffer if count