Forum: Mikrocontroller und Digitale Elektronik STM32F103 Sleep(WFI) & CAN


von Werner M. (snash)


Angehängte Dateien:

Lesenswert?

Hi!

Ich versuche derzeit (leider eher mehr schlecht als recht) den 
Sleep-Modus in mein aktuelles Programm zu integrieren. Um es 
übersichtlich zu gestalten habe ich für erste Tests derzeit nur die 
beiden wichtigsten Funktionen meines Codes in einen Testcode gepackt und 
per CubeMX mit HAL-Lib. umgesetzt - debounce von 1 Taster nach Peter 
Dannegger & darauf folgendes Versenden von Nachrichten über CAN. Der 
Sleep-Modus sollte immer nach Versenden der Nachricht aktiv, und erst 
bei Interrupt durch Tastendruck wieder deaktiviert werden.
Bei Tastendruck wird nur der Interrupt kurz aktiviert. Danach wird über 
den Systick Peter's Debounce-Routine durchlaufen. Wird ein Tastendruck 
(kurz oder lang) erkannt, wird eine entsprechende CAN-Nachricht 
verschickt, ein Flag gesetzt und der Sleep-Modus sollte wieder aktiviert 
werden.

Ohne Sleep funktioniert alles prima, jedoch springt mir das Programm bei 
integrierter Sleep-Funktion 
(HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI) immer 
wieder aus dem Sleep - scheint als würde nach dem Versenden der 
CAN-Nachricht noch ein Interrupt erfolgen (ACK?!), der den Sleep wieder 
deaktiviert. Ich bin hier leider etwas ratlos.
Hab hier zuletzt versucht über die HAL_CAN_TxMailbox0CompleteCallback 
(sowie für Mailbox 1 und 2) über ein Flag den Status des erledigten 
Versandes mit ins Programm zu integrieren, um erst danach den Sleep zu 
aktivieren. Klappt jedoch nicht. Ebenso, konnte ich in der IF-Abfrage 
vor dem Sleep diverse Interrupt-Flags nicht erfolgreich mit einbinden, 
bzw. habe hier eventuell das entsprechen notwendige vergessen.

Kann mir hier jemand eventuell weiterhelfen? Bin schon echt am 
verzweifeln und "doktor" hier schon seit Tagen herum.
Hier mein aktueller Code ->

1
#include "main.h"
2
#include "debounce.h"
3
4
CAN_HandleTypeDef hcan;
5
UART_HandleTypeDef huart2;
6
7
volatile uint8_t sleep_ok=1;
8
9
void SystemClock_Config(void);
10
static void MX_GPIO_Init(void);
11
static void MX_CAN_Init(void);
12
13
void CAN_Tx(uint8_t message[]);
14
void CAN_Filter_Config(void);
15
16
17
int main(void)
18
{
19
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
20
  HAL_Init();
21
22
  /* Configure the system clock */
23
  SystemClock_Config();
24
25
  /* Initialize all configured peripherals */
26
  MX_GPIO_Init();
27
  MX_USART2_UART_Init();
28
  MX_CAN_Init();
29
30
  CAN_Filter_Config();
31
  if(HAL_CAN_ActivateNotification(&hcan,CAN_IT_TX_MAILBOX_EMPTY|CAN_IT_RX_FIFO0_MSG_PENDING|CAN_IT_BUSOFF)!= HAL_OK)
32
  {
33
    Error_Handler();
34
  }
35
36
  if( HAL_CAN_Start(&hcan) != HAL_OK)
37
  {
38
    Error_Handler();
39
  }
40
41
  uint8_t message[3] = {'O','N','_'};
42
  CAN_Tx(message);
43
44
45
  while (1)
46
  {
47
48
        HAL_GPIO_WritePin(GPIOA,CH7_Pin,SET);  //Kontrolle µC on
49
50
51
    if(get_button_short(1<<Button1))
52
    {
53
      uint8_t message[3] = {'B','1','S'};
54
      CAN_Tx(message);
55
    }
56
57
    // Langer Tastendruck?
58
    if(get_button_long(1<<Button1))
59
    {
60
      uint8_t message[3] = {'B','1','L'};
61
      CAN_Tx(message);
62
    }
63
64
65
    if (sleep_ok == 1)
66
    {
67
68
      HAL_GPIO_WritePin(GPIOA,CH7_Pin,RESET);  //Kontrolle µC off
69
70
      HAL_GPIO_WritePin(GPIOA,CH6_Pin,SET);  //Kontrolle  sleep on
71
72
      HAL_SuspendTick();
73
      HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);
74
      HAL_ResumeTick();
75
76
      HAL_GPIO_WritePin(GPIOA,CH6_Pin,RESET);  //Kontrolle sleep off
77
78
      sleep_ok = 0;
79
    }
80
  }
81
}
82
83
/**
84
  * @brief System Clock Configuration
85
  * @retval None
86
  */
87
void SystemClock_Config(void)
88
{
89
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
90
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
91
92
  /**Initializes the CPU, AHB and APB busses clocks 
93
  */
94
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
95
  RCC_OscInitStruct.HSEState = RCC_HSE_BYPASS;
96
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
97
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
98
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
99
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
100
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
101
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
102
  {
103
    Error_Handler();
104
  }
105
  /**Initializes the CPU, AHB and APB busses clocks 
106
  */
107
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
108
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
109
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
110
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
111
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
112
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
113
114
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
115
  {
116
    Error_Handler();
117
  }
118
}
119
120
/**
121
  * @brief CAN Initialization Function
122
  * @param None
123
  * @retval None
124
  */
125
static void MX_CAN_Init(void)
126
{
127
128
  /* USER CODE BEGIN CAN_Init 0 */
129
130
  /* USER CODE END CAN_Init 0 */
131
132
  /* USER CODE BEGIN CAN_Init 1 */
133
134
  /* USER CODE END CAN_Init 1 */
135
  hcan.Instance = CAN1;
136
  hcan.Init.Prescaler = 18;
137
  hcan.Init.Mode = CAN_MODE_NORMAL;
138
  hcan.Init.SyncJumpWidth = CAN_SJW_1TQ;
139
  hcan.Init.TimeSeg1 = CAN_BS1_13TQ;
140
  hcan.Init.TimeSeg2 = CAN_BS2_2TQ;
141
  hcan.Init.TimeTriggeredMode = DISABLE;
142
  hcan.Init.AutoBusOff = ENABLE;
143
  hcan.Init.AutoWakeUp = DISABLE;
144
  hcan.Init.AutoRetransmission = ENABLE;
145
  hcan.Init.ReceiveFifoLocked = DISABLE;
146
  hcan.Init.TransmitFifoPriority = DISABLE;
147
  if (HAL_CAN_Init(&hcan) != HAL_OK)
148
  {
149
    Error_Handler();
150
  }
151
  /* USER CODE BEGIN CAN_Init 2 */
152
153
  /* USER CODE END CAN_Init 2 */
154
155
}
156
157
/**
158
  * @brief GPIO Initialization Function
159
  * @param None
160
  * @retval None
161
  */
162
static void MX_GPIO_Init(void)
163
{
164
  GPIO_InitTypeDef GPIO_InitStruct = {0};
165
166
  /* GPIO Ports Clock Enable */
167
  __HAL_RCC_GPIOC_CLK_ENABLE();
168
  __HAL_RCC_GPIOD_CLK_ENABLE();
169
  __HAL_RCC_GPIOA_CLK_ENABLE();
170
  __HAL_RCC_GPIOB_CLK_ENABLE();
171
172
  /*Configure GPIO pin Output Level */
173
  HAL_GPIO_WritePin(GPIOA, LD2_Pin|CH6_Pin|CH7_Pin|CH8_Pin, GPIO_PIN_RESET);
174
175
  /*Configure GPIO pin : B1_Pin */
176
  GPIO_InitStruct.Pin = B1_Pin;
177
  GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
178
  GPIO_InitStruct.Pull = GPIO_NOPULL;
179
  HAL_GPIO_Init(B1_GPIO_Port, &GPIO_InitStruct);
180
181
  /*Configure GPIO pins : LD2_Pin CH6_Pin CH7_Pin CH8_Pin */
182
  GPIO_InitStruct.Pin = LD2_Pin|CH6_Pin|CH7_Pin|CH8_Pin;
183
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
184
  GPIO_InitStruct.Pull = GPIO_NOPULL;
185
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
186
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
187
188
  /* EXTI interrupt init*/
189
  HAL_NVIC_SetPriority(EXTI15_10_IRQn, 0, 0);
190
  HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
191
192
}
193
194
void CAN_Filter_Config(void)
195
{
196
  CAN_FilterTypeDef can_filter_init;
197
  can_filter_init.FilterActivation = ENABLE;
198
  can_filter_init.FilterBank = 0;
199
  can_filter_init.FilterFIFOAssignment = CAN_RX_FIFO0;
200
  can_filter_init.FilterIdHigh = 0x0000;
201
  can_filter_init.FilterIdLow = 0x0000;
202
  can_filter_init.FilterMaskIdHigh = 0x0000;
203
  can_filter_init.FilterMaskIdLow = 0x0000;
204
  can_filter_init.FilterMode = CAN_FILTERMODE_IDMASK;
205
  can_filter_init.FilterScale = CAN_FILTERSCALE_32BIT;
206
207
  if( HAL_CAN_ConfigFilter(&hcan,&can_filter_init) != HAL_OK)
208
  {
209
    Error_Handler();
210
  }
211
}
212
213
void CAN_Tx(uint8_t message[])
214
{
215
  CAN_TxHeaderTypeDef TxHeader;
216
217
  uint32_t TxMailbox;
218
219
  TxHeader.DLC = 3;
220
  TxHeader.StdId = 0x125;
221
  TxHeader.IDE = CAN_ID_STD;
222
  TxHeader.RTR = CAN_RTR_DATA;
223
224
  if( HAL_CAN_AddTxMessage(&hcan,&TxHeader,message,&TxMailbox) != HAL_OK)
225
  {
226
    Error_Handler();
227
  }
228
}
229
230
void HAL_CAN_TxMailbox0CompleteCallback(CAN_HandleTypeDef *hcan)
231
{
232
  sleep_ok=1;
233
}
234
235
void HAL_CAN_TxMailbox1CompleteCallback(CAN_HandleTypeDef *hcan)
236
{
237
  sleep_ok=1;
238
}
239
240
void HAL_CAN_TxMailbox2CompleteCallback(CAN_HandleTypeDef *hcan)
241
{
242
  sleep_ok=1;
243
}
244
245
246
/**
247
  * @brief  This function is executed in case of error occurrence.
248
  * @retval None
249
  */
250
void Error_Handler(void)
251
{
252
  /* USER CODE BEGIN Error_Handler_Debug */
253
  /* User can add his own implementation to report the HAL error return state */
254
  
255
  /* USER CODE END Error_Handler_Debug */
256
}

von mts (Gast)


Lesenswert?

sleep_ok = 0;
vor
HAL_SuspendTick();
ausführen?

von Dr. Sommer (Gast)


Lesenswert?

Werner M. schrieb:
> scheint als würde nach dem Versenden der
> CAN-Nachricht noch ein Interrupt erfolgen (ACK?!), der den Sleep wieder
> deaktiviert.

Vermutlich aktiviert die HAL irgendwo den Transmit Complete Interrupt. 
WFI darf zu beliebigen Zeitpunkten beliebig aufwachen ("spurious 
wakeup"). Die Software muss damit klarkommen.

Sleep-Modi verwendet man grundsätzlich nicht "on demand" in einem 
linearen Programmablauf, sondern typischerweise direkt in einer 
Endlosschleife. In den ISRs reagiert man dann auf Ereignisse. Wenn die 
ISR zurückkehrt, wird sofort der Sleep-Modus reaktiviert. Wenn man in 
der main() selbst Ereignisse abarbeiten möchte - welche z.B. per Flag in 
der ISR gestartet werden, oder nach Abfrage von Peripherie-Bits - dann 
so:
1
int main () {
2
  // Initialisierung ...
3
  
4
  while (1) {
5
    if (ereignis1) behandlung1 ();
6
    if (ereignis2) behandlung2 ();
7
    // ...
8
    
9
    Sleep ();
10
  }
11
}

Wichtig ist dabei, abzufragen, ob etwas zu tun ist. Wenn nicht, wird 
Sleep direkt wieder betreten.

Man sollte sich nie darauf verlassen dass WFI nicht vor einem bestimmten 
Zeitpunkt zurückkehrt. Zwischendurch den Sleep aktivieren und wieder 
deaktivieren ist sehr untypisch. In deinem konkreten Fall würde ich es 
so machen:

* In der main() nur ein schlichtes "while (1) 
HAL_PWR_EnterSLEEPMode(...);" unterbringen, sodass der Sleep-Modus 
praktisch immer aktiv ist.
* Den SysTick immer aktiviert lassen. In dessen ISR die Entprellroutine 
laufen lassen, und abfragen ob ein Taster gedrückt wurde. Ist das der 
Fall, die gewünschte Nachricht absenden und eine Variable setzen, dass 
die Nachricht unterwegs ist. Solange das der Fall ist, weitere 
Tastendrücke ignorieren.
* Im Transmit Complete Interrupt die Variable zurücksetzen, sodass 
erneute Tastendrücke akzeptiert werden.

PS: In deinem Fall wird das Problem sein, dass die Interrupts während 
der HAL_PWR_EnterSLEEPMode kommen. Im Callback setzt du "sleep_ok=1". 
Nachdem HAL_PWR_EnterSLEEPMode zurückkehrt, setzt du direkt wieder 
"sleep_ok = 0;" und alles startet von vorne. Du hast hier praktisch Race 
Conditions. Das lässt sich zwar bestimmt irgendwie flicken - aber baue 
lieber die grundlegende Programmstruktur um (s.o.)!

Werner M. schrieb:
> Der
> Sleep-Modus sollte immer nach Versenden der Nachricht aktiv, und erst
> bei Interrupt durch Tastendruck wieder deaktiviert werden.

Das ist auch schon komisch - am Anfang willst du ohne Sleep-Modus auf 
Tastendrücke warten, aber nach dem Absenden der Nachricht dann mit 
Sleep-Modus?

von Dr. Sommer (Gast)


Lesenswert?

PPS: Im ARMv7M Architecture Reference Manual heißt es auf S. B1-560:

Both WFI and WFE are hint instructions, that might have no effect on 
program execution. Normally, they are used in software idle loops that 
resume program execution only after an interrupt or event of interest 
occurs.
Note:
Code using WFE and WFI must handle any spurious wakeup event caused by a 
debug halt or other IMPLEMENTATION DEFINED reason. Applications must be 
aware that the architecture permits WFE, WFI, and SEV instructions to be 
implemented as NOPs.

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.