Sida 1 av 1

Hjälp med buggar, XC8 PIC16F690

Postat: 2 maj 2014, 16:46:16
av 90kar08
Håller på med ett projekt tillsammans med en kompis. Det ska bli en intervalltimer till systemkamera men för tillfället är det ett menysystem på 16x2 LCD, en rotationsenkoder, en LED och en tryckknapp.



I slutet av videon ser man hur menyn är tänkt att fungera men som synes finns det lite buggar... :roll:

Videon är från några veckor sedan, koden nedan generar samma buggar men inte riktigt lika ofta. Vi har försökt kommentera bort eller ändra på tveksamma kodstycken i tur och ordning men vi får ingen vidare förbättring och börjar få slut på idéer... Är det kanske så att vi gör något man kanske inte bör göra i C? Vi är båda mest vana vid Java och assembler.

Kod: Markera allt

/****************************************************
*													*
*	Filename:	IMSv1.2.c							*
*	Date:		2014-03-20							*
*													*
*	Author:		Kalle Jonsson						*
*													*
*****************************************************
*													*
*	Processor:	PIC16F690							*
*	Compiler:	MPLAB XC8							*
*													*
****************************************************/

#include 	<xc.h>
#include 	<string.h>
#define 	_XTAL_FREQ 	4000000
#define		ENTMOD		0x06
#define 	FNCSET		0x38
#define		DSPOFF		0x08
#define		DSPON		0x0C
#define		LCD_RS		PORTCbits.RC0
#define		LCD_E		PORTCbits.RC1
#define		LCD_BL		PORTCbits.RC5
#define		LED			PORTAbits.RA4
#define		BTN			PORTAbits.RA1
#define		SHT			PORTCbits.RC7


__CONFIG(BOREN_OFF & CPD_OFF & CP_OFF & MCLRE_OFF & WDTE_OFF & PWRTE_OFF & FOSC_INTRCIO);

volatile bit A, B, BTN_pressed, BTN_released;
volatile signed char rotcnt = 0;
volatile unsigned char AB, BA, PORTCs;
volatile signed int cnt = 0;
volatile unsigned int BTN_Time = 0;
volatile unsigned long currentTime = 0, lastChange;

//Initialize IO pins
void IOInit()
{
	BTN_released = 1;
	TRISA = 0b00000111;
	TRISB = 0b00000000;
	TRISC = 0b01001100;
	ANSEL = 0;				//No analog pins
	ANSELH = 0;				//--------------
	//ADCON0 = 0b00011000;
	//		   0------- Left justified
	//		   -0------ VDD voltage ref
	//		   --0110-- AN6
	//         ------0- GO/DONE
	//		   -------1 ADON
	//ADCON1 = 0x10;		//AD Conversion Clock Fosc/8
}

//Initialize all interrupts
void interruptInit()
{
	//ADIF = 0;				//A/D Converter Interrupt Flag bit clear
	//ADIE = 1;				//A/D Converter Interrupt Enable bit set
	IOCA = 0b000101;		//Interrupt-on-change PORTA
	RABIF = 0;				//IOC Flag bit clear
	RABIE = 1;				//IOC Enabled 
	OPTION_REG =0b11010001;	//TIMER 0: internal clock, prescaler 1:4
	T0IF = 0;				//TIMER 0 Interrupt Flag bit clear
	T0IE = 1;				//TIMER 0 Overflow Interrupt Enabled
	//PEIE = 1;				//Peripheral Interrupt Enable bit set
}

//Send an instruction to the LCD display
void LCD_cmd(char B)		
{
	LCD_RS = 0;
	PORTB = B & 0xF0;
	NOP();
	LCD_E = 1;
	__delay_us(50);
	LCD_E = 0;
	__delay_us(50);
	PORTB = ((B & 0xF)<<4);
	NOP();
	LCD_E = 1;
	__delay_us(50);
	LCD_E = 0;
	__delay_us(50);
}

//Clear and home the LCD display
void LCD_clr() 				
{
	LCD_cmd(1);
	__delay_ms(2);
	LCD_cmd(2);
	__delay_ms(2);
}

//Output a character onto the LCD display
void LCD_out(char c)		
{
	LCD_RS = 1;
	PORTB = (c & 0xF0);
	NOP();
	LCD_E = 1;
	__delay_us(50);
	LCD_E = 0;
	__delay_us(50);
	PORTB = ((c & 0x0F)<<4);
	NOP();
	LCD_E = 1;
	__delay_us(50);
	LCD_E = 0;
	__delay_us(50);
}

//Move the cursor on the LCD to the specified adress
void LCD_adr(char adr) 
{
	LCD_cmd(0x80 + adr);
}	

//Output an integer with a specified number of digits
void LCD_int_places(unsigned int nbr, int places)
{
	int i, j, out, temp;

	for(i = places; i > 0; i--) {
		out = nbr;
		temp = 1;
		for(j = i-1; j > 0; j--) {
			out /= 10;
			temp *= 10;
		}
		LCD_out('0' + out);
		nbr = nbr-out*temp;
	}	
}

//Output a string on the LCD at the specified adress
void LCD_String(const char *ch, char adr)
{
	LCD_adr(adr);
	while(*ch!='\0')
	{
		LCD_out(*ch++);
	}
}

//Initialize the LCD display
void LCD_Init()				//Initialize LCD diplay
{
	__delay_ms(100);
	LCD_RS = 0;
	LCD_E = 0;
	PORTB = 0x20;			//4-bit Mode
	LCD_E = 1;
	__delay_us(50);
	LCD_E = 0;
	__delay_ms(2);
	LCD_cmd(0b00101000);	//4-bit, two line, 5x8 font
	__delay_ms(1);
	LCD_cmd(0b00001100);	//Display on, Cursor off, Blink off
	__delay_ms(1);
	LCD_cmd(0x01);			//Clear display
	__delay_ms(2);
	LCD_cmd(0b0110);		//Increment mode, shift off
	
	PORTB = 0;
	__delay_ms(100);
}	

//Return 1 if the button has been pressed, else 0
char getBTN() {
	if (BTN_pressed) {
		BTN_pressed = 0;
		return 1;
	} else {
		return 0;
	}
}

//Return 1 or -1 if the rotary encoder has been moved, else 0
signed char getROT() {
	if (rotcnt < 0) {
		rotcnt++;
		return -1;
	} else if (rotcnt > 0) {
		rotcnt--;
		return 1;
	} else {
		return 0;
	}
}

//Add or remove one from var and return
unsigned int useROT(unsigned int var, unsigned int max) {
	signed char rotTemp = 0;
	if (rotcnt < 0) {
		rotcnt++;
		rotTemp = -1;
	} else if (rotcnt > 0) {
		rotcnt--;
		rotTemp = 1;
	}
	signed long temp = var + rotTemp;
	if (temp <= max && temp >= 0) {
		return temp;
	} else {
		return var;
	}
}

void interrupt ISR()
{
	GIE = 0;
	if (T0IF && T0IE) {						//Timer 0 overflow interrupt, once every ms
		TMR0 += 6;
		currentTime++;						//Elapsed time in ms since reset
		if (!BTN && BTN_released) {
			BTN_pressed = 1;
			BTN_released = 0;
		} else if (BTN) {
			BTN_released = 1;
		}	
		T0IF = 0;
	}

	if (RABIF && RABIE) {					//Interrupt-on-change
		unsigned char PORTAcopy = PORTA;
//		if ((currentTime - lastChange) > 2) {
//			lastChange = currentTime;
			
			A = PORTAcopy & 0b001;
			B = (PORTAcopy & 0b100) >> 2;
	
			AB = ((A << 1) + B);
			char test = (BA ^ AB) & 0b11;
	
			BA = ((B << 1) + A);
	
			if (test == 0x01) {
				cnt++;
				if (cnt%4 == 0 && (rotcnt < 127)) {	
					rotcnt++;
					cnt = 0;
				}
				
			} else if (test == 0x02) {				
				cnt++;
				if (cnt%4 == 0 && rotcnt > -127) {
					rotcnt--;
					cnt = 0;
				}
			}
		//}
		RABIF = 0;
	}
	GIE = 1;
}

void main() 
{
	unsigned char shutterUnit='s', intervalUnit='s', sensorMode=2,
		BLMode=3, trigger=1, battery = 0, BL = 100, sensor = 45, state = 0, btn, 
 		arrowRow = 2, arrowPos = 0x40, nextUnit='s', openUnit='s';
	unsigned int shutter=25, interval=10, next=24, open=19, pictures=389, 
		picturesTaken=145, sensorVal=25;

	IOInit();
	interruptInit();
	LED = 1;
	SHT = 0;
	LCD_BL = 1;					//Turn on LCD backlight

	__delay_ms(500);
	//LED = 0;
	LCD_Init();					//Initialize LCD display

	GIE = 1;					//Turn on interrupts
	LCD_clr();					//Clear display

	char state = 0;

	while (1) {
		LED ^= 1;	
        switch (state) { 		//MAIN MENU print
            case 0: 
                LCD_String(" --SETTINGS--  ", 0x1); 
                LCD_String("Shutter   ", 0x41);
				LCD_adr(0x4B);
				LCD_int_places(shutter, 2); 
				LCD_adr(0x4D);
                LCD_out(shutterUnit); 
                break; 
            case 1:
                LCD_String("Shutter   ", 0x1); 
                LCD_String("Interval  ", 0x41); 
				LCD_adr(0xB);
				LCD_int_places(shutter, 2);
				LCD_adr(0xD);
                LCD_out(shutterUnit); 
				LCD_adr(0x4B);
				LCD_int_places(interval, 2);
				LCD_adr(0x4D); 
                LCD_out(intervalUnit); 
                break; 
            case 2: 
                LCD_String("Interval  ", 0x1); 
                LCD_String("Pictures  ", 0x41); 
				LCD_adr(0xB);
				LCD_int_places(interval, 2);
				LCD_adr(0xD);
                LCD_out(intervalUnit);
				LCD_adr(0x4B);
				LCD_int_places(pictures, 3);
                break; 
            case 3: 
                LCD_String("Pictures  ", 0x1); 
                LCD_String("Sensor    ", 0x41);
				LCD_adr(0xB);
				LCD_int_places(pictures, 3); 
				LCD_adr(0x4B);
				LCD_int_places(sensor, 3);
                break;   
            case 4: 
                LCD_String("Sensor    ", 0x1); 
                LCD_String("Sensor    ", 0x41); 
                LCD_adr(0xB);
				LCD_int_places(sensor, 3); 
                if (sensorMode == 1) 
                    LCD_String("ON ", 0x4B); 
                else
                    LCD_String("OFF", 0x4B); 
                break; 
            case 5: 
                LCD_String("Sensor    ", 0x1); 
                LCD_String("Trigger   ", 0x41); 
                if (sensorMode == 1) 
                    LCD_String("ON ", 0xB); 
                else
                    LCD_String("OFF", 0xB); 
                if (trigger == 1) 
                    LCD_String("ON ", 0x4B); 
                else
                    LCD_String("OFF", 0x4B); 
                break; 
            case 6:
                LCD_String("Trigger   ", 0x1); 
                LCD_String("Backlight ", 0x41); 
                if (trigger == 1) 
                    LCD_String("ON ", 0xB); 
                else
                    LCD_String("OFF", 0xB);
				LCD_adr(0x4B);
				LCD_int_places(BL, 3);
                break; 
            case 7: 
                LCD_String("Backlight ", 0x1); 
                LCD_String("Backlight ", 0x41); 
				LCD_adr(0xB);
				LCD_int_places(BL, 3);
				if (BLMode == 1) 
                    LCD_String("ON ", 0x4B); 
                else if(BLMode == 2) 
                    LCD_String("OFF", 0x4B); 
                else
                    LCD_String("AUT", 0x4B); 
				
                break; 
			default:
				break;
        } 
  		
		if (arrowPos == 0) {				//Arrow printing and removing 
			LCD_adr(0);
        	LCD_out(0x7E); 
			LCD_adr(0x40);
			LCD_out(' ');  
		} else {
			LCD_adr(0);
			LCD_out(' '); 
			LCD_adr(0x40);
			LCD_out(0x7E); 	
		}	
        
		signed char rotTemp = getROT();		//Check if the rotary encoder has been turned
        if (rotTemp > 0 && arrowRow < 9){ 
            if(arrowPos == 0)  
                arrowPos = 0x40; 
            else
                state++; 
            arrowRow++; 
        } else if (rotTemp < 0 && arrowRow > 2) { 
            if(arrowPos == 0x40) 
                arrowPos = 0; 
            else
                state--; 
            arrowRow--; 
        } else if (rotTemp < 0 && arrowRow == 2 && state == 1) { 
            state = 0; 
            arrowPos = 0x40; 
        }	

		if (getBTN()) {					//Enable modifying value with button press
			LCD_adr(0);					//Remove arrows in the first column
			LCD_out(' ');				//-
			LCD_adr(0x40);				//-
			LCD_out(' '); 				//-
			LCD_adr(arrowPos + 0xA);	//Output the arrow to the left of the value
			LCD_out(0x7E);				//-	
			
			while (!getBTN()) {
				LED ^= 1;
				unsigned int varTemp;
				char unitTemp;
				char type = 0;			//Variable print type, 2 digit int, 3 digit int or string
				switch (arrowRow) {
					case 2:
						shutter = useROT(shutter, 60);
						varTemp = shutter;
						unitTemp = shutterUnit;
						type = 1;
						break;
					case 3:
						interval = useROT(interval, 60);
						varTemp = interval;
						unitTemp = intervalUnit;
						type = 1;
						break;
					case 4:
						pictures = useROT(pictures, 999);
						varTemp = pictures;
						unitTemp = 0;
						type = 2;
						break;
					case 5:
						sensor = useROT(sensor, 255);
						varTemp = sensor;
						unitTemp = 0;
						type = 2;
						break;
					case 6:
						sensorMode = useROT(sensorMode, 3);
						varTemp = sensorMode;
						unitTemp = 0;
						type = 3;
						break;
					default:
						type = 0;
						break;						
				}

				LCD_adr(arrowPos + 0xB);
				switch (type) {
					case 0:
						break;
					case 1:
						LCD_int_places(varTemp, 2); 
						LCD_adr(arrowPos + 0xD);
        		        LCD_out(unitTemp);
						break;
					case 2:
						LCD_int_places(varTemp, 3);
						break;
					case 3:
						if (varTemp == 1)
							LCD_String("ON ",arrowPos + 0xB);
						else if (varTemp == 2)
							LCD_String("OFF", arrowPos + 0xB);
						else if (varTemp == 3)
							LCD_String("AUT", arrowPos + 0xB);
						break;
					default: 
						break;
				}
			}
		}
		//while (rotcnt == 0 && BTN_pressed == 0);
	}
}

Re: Hjälp med buggar, XC8 PIC16F690

Postat: 2 maj 2014, 19:05:16
av ElectricNooB
Känslan man får är att ni skickar data för fort till displayen för då kan man ju få sånt där. Annars kanske interuptsen ställer till det, dvs, kanske bryts sändningen till displayen av något interupt(tex timern), inte för att borde göra något men det kanske ändrar nått annat tex pin states. Kort sagt, interupts kan leda till lustigheter.

Re: Hjälp med buggar, XC8 PIC16F690

Postat: 3 maj 2014, 19:45:56
av 90kar08
Tack för svar :)

Att vi pratade för fort med LCD:n var vår tanke också men det hjälper inte att lägga till längre fördröjningar.

Verkar som om det var pin-change-interrupet som ställde till det. Stängde av det och la rotationsenkoder-checken under timer-interruptet istället och nu funkar det bättre :) Än så länge iaf...

Re: Hjälp med buggar, XC8 PIC16F690

Postat: 3 maj 2014, 19:50:29
av sodjan
Om det är en vanlig/billig mekanisk rot.enkoder så var det väl kontaktstudsar.
Och ja, det bör försvinna om man pollar i ett timer interrupt istället, i alla
fall om timern har tillräckligt lång tid så att studsarna hinner klinga ut.
Om man vill köra IOC så kanske man behöver någon elektronik
mellan som snyggar till signalen.

Re: Hjälp med buggar, XC8 PIC16F690

Postat: 3 maj 2014, 19:54:45
av 90kar08
Vi satte kondingar på rotationsenkodern just för att slippa kontaktstuds men vi blev väl inte av med dem helt antagligen. Men jag tror inte det kommer bli några problem att kolla enkodern i timerinterruptet istället så vi kör på det :)

Re: Hjälp med buggar, XC8 PIC16F690

Postat: 5 maj 2014, 10:49:32
av 90kar08
Så till nästa problem; timerinterruptet är tänkt att komma varje ms men det verkar komma varannan istället. Räknar upp en variabel var 1000:e interrupt och skriver ut den på displayen och tycker att vi borde få ut sekunder men sekunderna blir dubbelt så långa som de ska.

Vi har räknat såhär:

Intern oscillator på 4MHz ger 1MHz instructionscykler (1 MIPS, är det samma sak ?).
Prescaler på 1:4 enl.

Kod: Markera allt

OPTION_REG =0b11010001;   //TIMER 0: internal clock, prescaler 1:4
ger 250kHz

Då borde väl timer0 som är 8-bitars slå runt med ~1kHz?

Re: Hjälp med buggar, XC8 PIC16F690

Postat: 5 maj 2014, 11:42:53
av sodjan
Har du några IOC igång samtidigt?
Jag ser att du i ISR-koden hanterar GIE. GIE "släcks" med
automatik då ett interrupt inträffar och "enablras" igen
automatiskt vid RETFIE (return from intgerrupt).
Du borde nog inte försöka göra samma sak, det kan bli
underligt om du enablar interrupt manuellt innan context
har äterställts korrekt via RETFIE (och den eventuella övríga
kod som kompilatorn lägger till för att hantera context).

Re: Hjälp med buggar, XC8 PIC16F690

Postat: 5 maj 2014, 12:36:24
av 90kar08
Nope, inga andra interrupt alls igång.

Sant det där med GIE, har tagit bort det nu. Men det verkar inte vara det som ställde till det i det här fallet.

Det känns ju som om det skulle vara något enkelt räknefel någonstans, eller något liknande i och med att interruptet kommer mer eller mindre exakt hälften å ofta som det är tänkt. Men jag kan inte hitta några sådana feltänk...

Re: Hjälp med buggar, XC8 PIC16F690

Postat: 5 maj 2014, 12:39:44
av sodjan
Du har inget instrument (frekvensräknare eller oscilloskop) så att du kan
verifiera frekvensen?

Även om 4 MHz ska vara default, så kan det vara vettigt (i alla fall
tydligare, eftersom jag letade efter OSCCON i din kod) att sätta
OSCCON specifikt till det man vill ha. Du kan även testa med
andra prescalers för att verifiera att det fungerar.

Vad har du egentligen gjort för felsökning? Lite onödigt att
föreslå sådant som du redan har kollat... :-)

Re: Hjälp med buggar, XC8 PIC16F690

Postat: 5 maj 2014, 12:40:10
av Glenn
Är du säker på att du har rätt frekvens då ? alltså den du räknar efter, det är ju en klassiker annars, speciellt med intern oscillator.

EN sekund efter sodjan.. :P ;)

Re: Hjälp med buggar, XC8 PIC16F690

Postat: 5 maj 2014, 12:42:19
av Icecap
Jag ser ingen initiering av den interna oscillator och jag drar mig till minnes att den startar på halva av max-frekvensen automatisk.

Re: Hjälp med buggar, XC8 PIC16F690

Postat: 5 maj 2014, 12:48:24
av 90kar08
Sätter jag OSCCON så att frekvensen blir 8MHz så blir sekunderna rätt, men då är ju fortfarande uträkningarna fel... Mao, det är något annat som blivit fel än frekvensen.

Har oscilloskop men det står hemma hos föräldrarna, ska ta mig till skolan strax, passar på att ta en tur in i labbet där och låna oscilloskop. Testar att toggla en pinne varje interrupt för att försäkra mig om att nerdelningen till sekunder som blir fel utan just att timern tickar för långsamt.

Jo, halva max-frekvensen stämmer med det vi räknat. Max är 8MHz.

Re: Hjälp med buggar, XC8 PIC16F690

Postat: 5 maj 2014, 12:57:06
av sodjan
Toggla en pinne på kritiska punkter under uträkningen och
stäm av med oscilloskop. Det är nog enklast. Jag har inte
kört så mycket i C, men kan man inte köra det i MPSIM?

Re: Hjälp med buggar, XC8 PIC16F690

Postat: 5 maj 2014, 19:39:42
av 90kar08
Det visade sig vara ett ganska enkelt fel... Var visst inte så smart att göra if(currentTime%1000 == 0) på en long för att få ut sekunder av millisekunderna :oops:

Dels så tog det ca 10 % av programminnet och dels så tog det för lång tid att beräkna så bara vartannat timer-interrupt hanns med. Men nu funkar det fint :D

Tackar för hjälpen igen, det var oscilloskopet som löste det hela.

Re: Hjälp med buggar, XC8 PIC16F690

Postat: 5 maj 2014, 21:54:17
av sodjan
Aha, där ser man... :-)
De flesta fel är enkla.