Forum: Mikrocontroller und Digitale Elektronik STM32F446: Taster länger drücken


von David (Gast)


Lesenswert?

Hallo zusammen,

zur Zeit brüte ich über folgendes Problem. Ich möchte ein Port schalten 
wenn ich 2 s ein Taster drücke. Wie realisiere ich das am besten?

Meine erste Idee war, über TIM3 das CNT-Register auszulesen. Nach 2000 
ms bei gedrückten Schalter sollte dann der Port geschaltet werden.

Realisiert habe ich das bisher so:
1
if((OK_BUTTON==ON)){
2
    set_timer3_zero();               // Setze CNT auf 0
3
    if((timer_up()==2000)){    // Warte bis CNT auf 2000 ms ist
4
      debug_LED(ON);        
5
     }              
6
     }else{
7
      debug_LED(OFF);
8
    }

Meine Timer-Konfiguration sieht wie folgt aus:
1
void MX_TIM3_Init(void)
2
{
3
  /*Timer auf 1 ms eingestellt
4
    
5
   f_timer = TimerClock / (1+Prescaler) / (1+Period)
6
           = 90 *(1+999)/89+1 = 1 ms 
7
  */
8
  
9
  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
10
  TIM_MasterConfigTypeDef sMasterConfig = {0};
11
  htim3.Instance = TIM3;
12
  htim3.Init.Prescaler = 89; //TIM3->APB1 = 90 MHz, gefordert ms
13
  htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
14
  htim3.Init.Period = 999; 
15
  htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
16
  htim3.Init.RepetitionCounter = 0;
17
  htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
18
  if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
19
  {
20
    Error_Handler();
21
  }
22
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
23
  if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
24
  {
25
    Error_Handler();
26
  }
27
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
28
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
29
  if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
30
  {
31
    Error_Handler();
32
  }
33
 }

Und meine Routine
1
uint16_t timer_up(void){ 
2
uint16_t count;    
3
  assert_param(IS_TIM_ALL_PERIPH(TIM3));
4
  uint16_t cnt = TIM3->CNT;
5
  if (cnt==999){
6
       count++;
7
  }
8
  return count;  
9
}
10
11
void set_timer3_zero(void){
12
  TIM3->CNT = 0;
13
}
14
15
void start_clock_tim3(void){
16
HAL_TIM_Base_Start(&htim3);
17
}

Nun komme ich nicht weiter. Wie bekomme ich es hin, das nach 2 s des 
Schalterdrückens der Port (hier ist eine LED angeschossen) auf High 
geschaltet wird?

von Steffen W. (derwarze)


Lesenswert?

Ich kenne mich mit dem STM32 nicht aus
Doch Dein Code für den Timer sieht nach etwas vorgefertigten eines 
Codegenerators aus, also gehe ich mal davon aus das das funktioniert.

Das Problem liegt in Deinem Programmschnipsel das ja sicher Teil einer 
Programmschleife ist und regelmäßig aufgerufen wird.
Heißt also jedesmal wenn das Programm hier vorbeikommt wird solange der 
Taster gedrückt ist wird erst mal der Zähler gelöscht. Unmittelbar 
danach erfolgt die Abfrage des Zählerstandes der aber so nie 2000 
erreichen kann.
Und selbst wenn er so weit kommen würde, wird die LED nur für den kurzen 
Moment den der Zähler 2000 ist angeschaltet und beim nächsten Durchlauf 
sofort wieder gelöscht. Also die LED blitzt in diesem Fall für die Zeit 
des Programmdurchlaufs kurz auf und bleibt aus.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

David schrieb:
> Realisiert habe ich das bisher so:
Ein Tipp: formatier den Code vernünftig, dass die Klammerebenen passen. 
Der Compiler kümmert sich nicht um die Optik!

Probiers mal so:
1
mainloop:
2
:
3
:
4
   if((OK_BUTTON==ON)) {
5
      if(timer_up()<2000) {   // Warte bis CNT mindestens(!!) 2000 ms ist
6
         debug_LED(ON);
7
      }
8
   }
9
   else{ // Button = off  --> LED aus und Zähler zurücksetzen
10
      debug_LED(OFF);
11
      set_timer3_zero();     // Timeout für Taster zurücksetzen
12
   }
13
:
14
:
Vergleich auf == sind immer dann kritisch, wenn irgendwo "im 
Hintergrund" was weiterlaufen könnte. Vergleiche dann besser auf "<".

: Bearbeitet durch Moderator
von David (Gast)


Lesenswert?

Guten morgen zusammen,

leider funktioniert es immer noch nicht. Das Ziel ist, ein Taster soll 
mindestens 2 Sekunden gedrückt sein, erst dann soll eine LED geschaltet 
werden.

Ich habe mir nochmal die Timereinstellungen vorgenommen. M.E. soll das 
stimmen. Dieser sollte 1 ms betragen:
1
void MX_TIM3_Init(void){
2
  /*Timer auf 1 ms eingestellt
3
   
4
  Tick = PSC+1/f_APB1 = 89999+1/90000 kHz -> 1 ms
5
  
6
  */
7
  
8
  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
9
  TIM_MasterConfigTypeDef sMasterConfig = {0};
10
  htim3.Instance = TIM3;
11
  htim3.Init.Prescaler = 90000-1; //TIM3->APB1 = 90 MHz
12
  htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
13
  htim3.Init.Period = 1000;
14
  htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
15
  htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
16
  if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
17
  {
18
    Error_Handler();
19
  }
20
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
21
  if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
22
  {
23
    Error_Handler();
24
  }
25
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
26
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
27
  if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
28
  {
29
    Error_Handler();
30
  }
31
}

Dann in die Main:
1
 if((OK_BUTTON==ON)) {
2
      if(timer_up()<2000) {   // Warte bis CNT mindestens(!!) 2000 ms ist
3
         debug_LED(ON);
4
      }
5
   }
6
   else{ // Button = off  --> LED aus und Zähler zurücksetzen
7
      debug_LED(OFF);
8
      set_tim3_zero();     // Timeout für Taster zurücksetzen
9
   }

und die Funktionen
1
uint16_t timer_up(void){ 
2
volatile uint16_t count;    
3
  assert_param(IS_TIM_ALL_PERIPH(TIM3));
4
  uint16_t cnt = TIM3->CNT;
5
  if (cnt==1000){
6
       count++;
7
  }
8
  return count;  
9
}
10
11
// zur besseren Zuordung unbenannt
12
void set_tim3_zero(void){
13
  TIM3->CNT = 0;
14
}
15
16
// wird in der Main vor der Schleife initialisiert
17
void start_clock_tim3(void){
18
HAL_TIM_Base_Start(&htim3);
19
}

Wo liegt der Fehler? Mein Gedanke ist, das Register CNT zählt bis 1000 
(Periodeneinstellung) hoch, das in einer ms und setzt sich zurück. Das 
zurücksetzen zähle ich.

Oder bin ich komplett auf dem Holzweg?

Allgemein gefragt: Wie bekomme ich es hin, das wenn der Taster 2 s 
gedrückt wird, erst dann die LED schaltet?

Danke euch :-)

von Peter D. (peda)


Lesenswert?

Bequem ist es, eine Entprellib zu nehmen, die das als fertiges Ereignis 
liefert:
Beitrag "Universelle Tastenabfrage"

: Bearbeitet durch User
von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

David schrieb:
> Oder bin ich komplett auf dem Holzweg?
Ja.
Man nimmt nicht 1 kompletten und wertvollen Zähler zum Entprellen von 1 
Taste.

> if (cnt==1000){
Was schrieb ich zum Thema "Vergleich von Zählerständen auf Gleichheit"?
Ich schrieb, dass das Unsinn ist, weil man dann hin&wieder evtl. den 
richtigen Zeitpunkt nicht genau "trifft". Den Rest kannst du einfach mal 
selber überlegen: was passiert, wenn du diese Abfrage (z.B. durch 
ungeschickte Programmierung der mainloop) bei cnt=999 und dann wieder 
bei cnt=1001 machst? wie lange wird es dauern, bis du wieder auf 
cnt=1000 triffst?
Mit einem "<" oder ">" kommst du dann schlimmstenfalls erst beim 
Zählerstand 1001 oder 1002 und somit ein wenig "zu spät". Aber du 
"verpasst" nicht gleich das ganze Ereignis und wartest 64 Sekunden bis 
der 16-Bit-Integer einmal "die Runde gedreht" hat.

> Mein Gedanke ist, das Register CNT zählt bis 1000 (Periodeneinstellung)
> hoch, das in einer ms und setzt sich zurück. Das zurücksetzen zähle ich.
Initialisiere den automatischen Reload eines Timers so, dass du alle 
1ms einen Interrupt bekommst. Dort kannst du dann alle weiteren Zeiten 
verwalten.

von Georg (Gast)


Lesenswert?

David schrieb:
> Mein Gedanke ist, das Register CNT zählt bis 1000
> (Periodeneinstellung) hoch

Die übliche Entprell-Software zählt eine Variable hoch, wenn die Taste 
gedrückt ist, und herunter wenn nicht. Wird der Wert z.B. 10 erreicht 
(10 ms) wird die Taste als gedrückt erfasst (Flag gesetzt). Für das 
Entprellen muss man nicht weiter hochzählen, kann man aber, dann fallen 
die 2 Sek so nebenher mit an. Die Variable muss natürlich 16bit sein, um 
bis 2000 zählen zu können.

Georg

von Steffen W. (derwarze)


Lesenswert?

Ist nicht hier
1
if((OK_BUTTON==ON)) {
2
      if(timer_up()<2000) {   // Warte bis CNT mindestens(!!) 2000 ms ist
3
         debug_LED(ON);
4
      }
5
   }
das '<' falschrum?
LED geht da doch schon an wenn cnt kleiner als 2000 ist.
Müsste '>' da stehen damit erst wenn cnt größer als 2000 die LED an geht

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Steffen W. schrieb:
> Müsste '>' da stehen
Wäre vermutlich besser. Oder noch besser sogar '>='.
Aber das ist im Grunde eines der kleineren Probleme hier... ;-)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.