Problem med att läsa IO-pinne med atmega328

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
John
Inlägg: 62
Blev medlem: 12 juli 2005, 20:24:47
Ort: Göteborg

Problem med att läsa IO-pinne med atmega328

Inlägg av John »

Har ett mycket underligt problem med en Atmega328P som jag inte lyckas få bukt med.
Problemet är att jag inte kan läsa av knappen S1. En utförligare beskrivning av problemet kommer längre ner.

Så här ser schemat ut: Bild
Kort beskrivning av kretsen:
Klockkretsen DS1307 generar avbrott 2 ggr/s genom att toggla ben 7 SQW/OUT.
Togglingen aktiverar ett "Pin Change Interrrupt" i MCUn.
Drivaren på SQW/OUT är av typen öppen kollektor så MCUns interna pullup är aktiverad på pinne PC3 för att undvika att pinnen flyter.
Detta gäller även för PD3 dit tryckknappen S1 är kopplad.
MCUn använder intern oscillator inställd på 8MHz.

PB0, PB1, PB2 och PD7 används för att styra H-bryggan.
I dagsläget är inget kopplat till H-bryggans utgångar.

Programmet:
Mjukvaran initierar PortB, PortD, timer0 och Pin change interrupt.
Varje gång det kommer ett interrupt på PC3 avläses värdet på PD3.
Beroende på värdet på PD3 sätts en global variabel (boButtonState) till antingen 0 eller 1.
I main finns en evighets-loop som kollar värdet på boButtonState.
Om boButtonState = 1 aktiveras två transistorer i H-bryggan. Efter 100 ms stängs alla transistorerna av och programmet väntar i 100 ms till innan loopen börjar om.
Om boButtonState = 0 görs ingenting och loopen börjar om.
Detta borde således resultera i en pulserande spänning ut från H-bryggan som är hög i 100ms och låg i 100ms.

Beskrivning av problemet:
Problemet är att det inte blir någon snygg pulserande spänning ut från H-bryggan då knappen trycks ner.
Det som händer är att så länge som boButtonState = 1 lägger MCUn ut en fyrkantsspänning på PB1 och PD7 med en periodtid på 2*900us istället för 2*100ms som det borde vara.
När boButtonState återgår till 0 så sätts PB0, PB1, PB2 och PD7 till 0V.
Jag har även provat att invertera avläsningen av knappen så att H-bryggan aktiveras då knappen är uppsläppt. Detta resulterade i att samma snabba fyrkantspänning kunde observeras då knappen var uppsläppt.
Har också provat att kommentera bort if-satsen som läser boButtonState och då blir det fina 100 ms pulser ut.
Först misstänkte jag någon form av EMC-problem, men anser att det kan avfärdas eftersom det funkar fint då man inte läser av knappen. Har kollat matningsspänning med oscilloskop och inte hittat något anmärkningsvärt.

Jag har kapat bort så mycket som möjligt av koden för att isolera problemet men nu har ideerna tagit slut. Är mycket tacksam om någon har några föslag på vad det kan vara eller var jag kan fortsätta letandet.

/John

Bifogar koden
Kompilerat med avr gcc utan någon optimering

Kod: Markera allt

#include <avr\io.h>
#include <avr\interrupt.h>
#include <avr\eeprom.h>

typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;
typedef unsigned long long u64;

typedef signed char s8;
typedef signed int s16;
typedef signed long s32;
typedef signed long long s64;


/* structs */
typedef struct
{
   u8 u8TCCRxA;
   u8 u8TCCRxB;
   u16 u16TCNTx;
   u16 u16OCRxA;
   u16 u16OCRxB;
   u8 u8TIMSKx;
   u8 u8TIFRx;
   u8 u8ASSR;  /* Only used by timer2 */
   u8 u8GTCCR; /* Only used by timer2 */
}timerRegStruct;

/* Prototypes */
void initInterrupts();
void initPorts();
void initTimer0(timerRegStruct *timerRegPtr);
void initTimer1(timerRegStruct *timerRegPtr);
void initTimer2(timerRegStruct *timerRegPtr);
void wait(u16 u16delayMs);
void setPositive();
void setNegative();
void setNone();

/* Pinning aliases */
#define HI_A   (7)
#define LO_A   (0)
#define HI_B   (2)
#define LO_B   (1)
#define BUTTON (3)

/* Macros */
#define setBit(port, bit) \
                  asm("sbi %0, %1" :: "I" (_SFR_IO_ADDR(port)), "I"(bit) )
#define clearBit(port, bit) \
   asm("cbi %0, %1" :: "I" (_SFR_IO_ADDR(port)), "I"(bit) )

/* Global variables */
static u8 boButtonState = 0;
static u8 boManageClockFlag = 0;
/****************************************************************************************
 * Interrupt service routine
 ****************************************************************************************
 */
ISR(PCINT1_vect)
{
   u8 u8PinD = PIND;
   boManageClockFlag = 1;

   if (u8PinD & (1<< BUTTON))
   {
      boButtonState = 0;
   }
   else
   {
      boButtonState = 1;
   }
}

/****************************************************************************************
 * Main
 ****************************************************************************************
 */
int main()
{
   /* Timer settings */
   static const timerRegStruct timer0Str =
      {
            0,    /* Normal operation */
            0,    /* Timer0 OFF */
            0,    /* Counter register */
            255,  /* Output compare register A */
            255,  /* Output compare register B */
            0,    /* Interrupt register */
            0,    /* Interrupt flag register */
            0,    /* ASSR not used by Timer0 */
            0     /* GTCCR not used by Timer0 */
      };

   /* Make sure interrupts are disabled */
   cli();
   /* Init HW modules */
   initTimer0(&timer0Str);
   initPorts();
   ACSR = 0; /* Disable Analog Comparator */
   wait(500);
   initInterrupts();
   while (1)
   {
      if (boButtonState)
      {
         setPositive();
         wait(100);
         setNone();
         wait(100);
         boManageClockFlag = 0;
      }
   }
}

/*
 * wait function uses Timer0 to wait the specified time in milliseconds.
 * Returns when specified time has elapsed
 */
void wait(u16 u16DelayMs)
{
   u16 u16i;

   /* repeat for the number of milliseconds */
   for (u16i = 0; u16i < u16DelayMs; u16i++)
   {
      TCCR0A = 0;
      TCNT0 = 0;
      OCR0A = 124; /* 125 * 64 = 8000 => Timer0 period = 1 ms @ 8 MHz*/
      OCR0B = 255;
      TIMSK0 = 0;
      TIFR0 = (1 << OCF0A);
      TCCR0B = 3; /* Timer0 prescaler = 64 */
      while (!(TIFR0 && (1 << OCF0A)))
      {
         /* Wait for Timer0 to elapse */
      }
   }

}


/*
 * Activates forward current
 */
void setPositive()
{
   clearBit(PORTB,HI_B);
   clearBit(PORTB,LO_A);
   setBit(PORTB,LO_B);
   setBit(PORTD,HI_A);
}

/*
 * Activates reverse current
 */
void setNegative()
{
   clearBit(PORTD,HI_A);
   clearBit(PORTB,LO_B);
   setBit(PORTB,LO_A);
   setBit(PORTB,HI_B);
}

/*
 * Deactivates H-bridge
 */
void setNone()
{
   clearBit(PORTB,LO_A);
   clearBit(PORTB,HI_B);
   clearBit(PORTB,LO_B);
   clearBit(PORTD,HI_A);
}

void initInterrupts()
{
   /* Make sure that INT0 and INT1 are disabled */
   EIMSK = 0;
   /* Clear interrupt flag register */
   PCIFR = (1<<PCIF2) | (1<<PCIF1) | (1<<PCIF0);
   /* Activate pin change interrupt on PC3 */
   PCMSK0 = 0;
   PCMSK1 = (1<<PCINT11);
   PCMSK2 = 0;
   PCICR = (1<<PCIE1);

   /* Enable global interrupts */
   sei();
}


void initTimer0(timerRegStruct *timerRegPtr)
{
   TCCR0A = timerRegPtr->u8TCCRxA;
   TCNT0 = (u8) (timerRegPtr->u16TCNTx);
   OCR0A = (u8) (timerRegPtr->u16OCRxA);
   OCR0B = (u8) (timerRegPtr->u16OCRxB);
   TIMSK0 = timerRegPtr->u8TIMSKx;
   TIFR0 = timerRegPtr->u8TIFRx;
   TCCR0B = timerRegPtr->u8TCCRxB;
}
void initPorts()
{
   PORTC = 0x08; /*Activate pullup on SQW pin */
   PORTB = 0;
   PORTD = 0x08; /* Activate pullup on button PD3 */

   /* H-bridge driver pins to outputs */
   DDRB = (1<<LO_A) | (1<<LO_B) | (1<<HI_B);
   DDRD = (1<<HI_A);
}
Användarvisningsbild
E85
Inlägg: 1274
Blev medlem: 29 maj 2007, 16:24:19
Ort: Övik

Re: Problem med att läsa IO-pinne med atmega328

Inlägg av E85 »

Prova ändra

Kod: Markera allt

/* Global variables */
static u8 boButtonState = 0;
static u8 boManageClockFlag = 0;
till

Kod: Markera allt

/* Global variables */
volatile u8 boButtonState = 0;
volatile u8 boManageClockFlag = 0;
John
Inlägg: 62
Blev medlem: 12 juli 2005, 20:24:47
Ort: Göteborg

Re: Problem med att läsa IO-pinne med atmega328

Inlägg av John »

Provade att ändra till volatile på de globala variablerna, tyvärr gjorde det ingen skillnad.

Tidigare hade jag en egen funktion som läste av IO-pinnen men flyttade detta till avbrottet för att undvika kontaktstudsar. Och då betedde det sig på samma sätt.
Så här såg det ut förut

Kod: Markera allt


u8 getButtonState()
{
   if (!(PIND & (1<<BUTTON)))
   {
      return TRUE;
   }
   else
   {
      return FALSE;
   }

}
If satsen i main använde getButtonState såhär:

Kod: Markera allt

if (getButtonState())
{
   ...
}
Förstår inte vad som kan gå fel, att läsa av en IO-pinne borde vara nåt av det enklaste man kan göra
Användarvisningsbild
E85
Inlägg: 1274
Blev medlem: 29 maj 2007, 16:24:19
Ort: Övik

Re: Problem med att läsa IO-pinne med atmega328

Inlägg av E85 »

Jag vet inte vad som är fel men tänkte tipsa om att det finns färdiga delay-funktioner som används såhär:

Kod: Markera allt

#define F_CPU 8000000UL  // 8 MHz
#include <util/delay.h>

_delay_ms(123); //delay 123ms (void _delay_ms(double __ms))
_delay_us(15); //delay 15us (void _delay_us(double __us))
Då slipper du använda en timer till det.

http://www.nongnu.org/avr-libc/user-man ... delay.html
Användarvisningsbild
BEEP
EF Sponsor
Inlägg: 1593
Blev medlem: 21 januari 2006, 16:57:56
Ort: Mölndal

Re: Problem med att läsa IO-pinne med atmega328

Inlägg av BEEP »

Nu vet jag inte om detta är problemet men dom interna pullup motstånden har för hög resistans för att kunna dämpa störningar som man får från knappar och annat som inte skärmas av jordplanet på kretskortet.
Användarvisningsbild
jesse
Inlägg: 9240
Blev medlem: 10 september 2007, 12:03:55
Ort: Alingsås

Re: Problem med att läsa IO-pinne med atmega328

Inlägg av jesse »

Kommer inte interruptet att utföras en massa gånger när du trycker ner knappen, om det blir kontaktstuds?
Användarvisningsbild
jesse
Inlägg: 9240
Blev medlem: 10 september 2007, 12:03:55
Ort: Alingsås

Re: Problem med att läsa IO-pinne med atmega328

Inlägg av jesse »

E85: fast om man använder interrupt så kommer inte de looparna att stämma i tiden. (om t.ex. en interrupt får tuppjuck och interruptar hela tiden)...

det kan ju vara nåt sånt, att interrupten tar all tid, men om waitloopen och timern är riktigt gjord ska den ändå inte bli fel.

Känns lite skumt att knappen ska påverka wait-tiden... när if-satsen väl är sann så borde ju programmet gå som en klocka (sätt utgångar, wait 100ms, nolla ut, wait 100 ms...

om sista fördröjningen är förlängd så kan det ju bero på if-satsen (då den kan fastna där om inte button == true ) , men knappast första fördröjningen :humm:
Användarvisningsbild
jesse
Inlägg: 9240
Blev medlem: 10 september 2007, 12:03:55
Ort: Alingsås

Re: Problem med att läsa IO-pinne med atmega328

Inlägg av jesse »

Men du ska läsa av PD3 - den pinnen heter PCINT19. och enligt databladet så triggas PCI1 av PCINT 8 - 14 och PCI2 av PCINT 16-23. Om du ska få ett interrupt när knappens lägen ändras ska det vara PCI2-interrupt.

PD3 heter också INT1, och då kan du trigga på antingen

(är inte klar än, ska tänka lite till...)

... jo, i InitInterrupt sätter du PCIFR = (1<<PCIF2) | (1<<PCIF1) | (1<<PCIF0) - du aktiverar alltså PIN CHANGE INTERRUPT på alla kretsens pinnar! , men du har bara interruptrutin skriven för PCIF1.

PCMSK1 = (1<<PCINT11); aktiverar PCINT11 som är PC3, inte PD3 (som är PCINT19)

Är det meningen att något annat än knappen ska aktivera interrupten?
Annars ändra till:

PCIFR = (1<<PCIF2);
PCMSK2 = (1<<PCINT19);


och ändra interruptrutinen till PCI2

tror jag :roll:

men sätt gärna en 100nF kondensator över knappen också.
Användarvisningsbild
jesse
Inlägg: 9240
Blev medlem: 10 september 2007, 12:03:55
Ort: Alingsås

Re: Problem med att läsa IO-pinne med atmega328

Inlägg av jesse »

äääh, är lite för trött tror jag.... borde sova istället.

jovisst ,ja , den externa klockan sitter på PC3. Den genererar ett interrupt 2 ggr / sek. Det är bara när den ändras som knappen blir avläst vad jag kan förstå. Det är förstås meningen att inget ska hända förrän klockan ger interrupt. Då är det ju rätt. :doh:

Men jag misstänker fortfarande wait-loopen. Används timer-parametrarna riktigt osv.. ... jag är inte så bra på hur man använder struct.. så det är ingen idé att jag försöker mig på att kolla det.
Användarvisningsbild
BEEP
EF Sponsor
Inlägg: 1593
Blev medlem: 21 januari 2006, 16:57:56
Ort: Mölndal

Re: Problem med att läsa IO-pinne med atmega328

Inlägg av BEEP »

"I dagsläget är inget kopplat till H-bryggans utgångar."
Jag tror att du behöver ha motstånd på utgångarna för att det ska fungera :?:
Användarvisningsbild
jesse
Inlägg: 9240
Blev medlem: 10 september 2007, 12:03:55
Ort: Alingsås

Re: Problem med att läsa IO-pinne med atmega328

Inlägg av jesse »

i timer0 loopen:

while (!(TIFR0 && (1 << OCF0A))) fel!

ska vara

while (!(TIFR0 & (1 << OCF0A)))

om det är bitvis and du är ute efter (dvs du ska kolla biten OCF0A)
Användarvisningsbild
Icecap
Inlägg: 26623
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Re: Problem med att läsa IO-pinne med atmega328

Inlägg av Icecap »

Jag har inte orkat läsa igenom hela tråden men knappar kan läsas på 2 olika sätt:
1: direkt (if(<pinne x>...), denna metod fungerar bra till vissa saker men definitivt inte om det kan vara kritisk.

2: via en timer-interrupt. Detta är ett enkelt sätt att fixa debounce då det blir "inbyggd". Oftast använder jag en hastighet mellan 10 och 30Hz och lägger till en n-key-rollover också. Viktigt är att bara läsa pinnen/porten EN gång per interrupt, behöver man fibbla med den avläsning ska den sparas i en mellanvariabel, detta ta effektivt bort all stuts. Vill man säkra mot störningar ska 2 avläsningar (en per interrupt) vara identiska.

Så säkrast:
Starta en timer-interrupt (eller häng på en befintlig) och låt den kolla pinnen och sätta en flagga i enlighet med resultatet.
Användarvisningsbild
jesse
Inlägg: 9240
Blev medlem: 10 september 2007, 12:03:55
Ort: Alingsås

Re: Problem med att läsa IO-pinne med atmega328

Inlägg av jesse »

Det borde vara smartare i det här läget att läsa knappen direkt i if-satsen i huvudprogrammet istället för att köra med interrupt. Debounce kan ju knappast bli ett problem om man har en 200 mS fördröjning efter att ha detekterat en "nedtryckt" knapp, innan man kollar igen. Bättre debouncing kan väl inte finnas. Däremot om man kör interrupt så anropas ju detta dubbelt så många gånger som det blir studs, vilket kanske kan bli lite extremt... (ett anrop för nedtryck, och ett för uppsläppt = två gånger per studs)
Användarvisningsbild
Icecap
Inlägg: 26623
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Re: Problem med att läsa IO-pinne med atmega328

Inlägg av Icecap »

I mitt tycke ska man ha MYCKET specifika skäl att ha en knapp att ge interrupt! Och jag ser inga sådana här.
John
Inlägg: 62
Blev medlem: 12 juli 2005, 20:24:47
Ort: Göteborg

Re: Problem med att läsa IO-pinne med atmega328

Inlägg av John »

Oj här har det hänt grejer! Ska försöka besvara alla.

jesse:

Kod: Markera allt

while (!(TIFR0 && (1 << OCF0A)))
Här verkar det som att du har hittat felet! Gjorde ett snabbt test och det verkar fungera Den här koden fungerar utmärkt både att simulera i AVR-studio och i verkligheten så länge man inte använder knappen.(I Avr studio fungerar det även om man använder knappen.)
&& jämför ju de två uttrycken TIFR0 och (1 << OCF0A). (1 << OCF0A) är ju alltid sann och därför beror resultatet alltid på TIFR0. TIFR0 består av bitarna OCF0B,OCF0A,TOV0. Där det endast är tänkt att OCF0A ska påverka resultatet. Om knappen på något sätt kan sätta OCF0B eller TOV0 i tid och otid så förklarar det varför wait-funktionen går ca 10 ggr för fort. Hur den här kopplingen skulle kunna se ut har jag ingen aning om. Tror det är bäst att sova på saken. Ska försöka hinna med fler tester i morgon kväll. Återkommer med resultatet.


Icecap: Knappen genererar inga interrupt utan läses av en interrupt-rutin som körs 2ggr/s. Detta blir ju ungefär som du föreslog i början. I det här fallet räcker att man läser 2ggr/s istället för 10-30.


BEEP: Felet ligger innan H-bryggan. Jag har mätt på signalerna som kommer från MCUn och det är fel redan där. Förstår inte hur du menar med motstånd på utgångarna?

E85: Dessa kände delay-funktioner jag inte till. Tyvärr verkar dessa kräva att man använder optimering. Har haft en del dåliga erfarenheter där kompilatorn har optimerat bort stora delar nödvändig kod, och vill därför helst inte använda det.
Skriv svar