#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "stm32c0xx_hal.h"
#include "queue.h"
#include "stdio.h"
#include <string.h>
/* ====== Handles RTOS ====== */
QueueHandle_t MyQueueHandle;
SemaphoreHandle_t seuilMutex;
SemaphoreHandle_t SemaphoreUrgence;
SemaphoreHandle_t adcMutex;
/* ====== ADC ====== */
ADC_HandleTypeDef hadc;
/* ====== Variables partagées ====== */
volatile uint16_t seuil = 0; // 0..1000
volatile uint8_t urgenceActive = 0; // 0=normal, 1=urgence
/* ====== Prototypes init ====== */
static void MX_ADC_Init(void);
static void MX_GPIO_Init(void);
/* ====== UART ====== */
static inline void UART_Send(const char *msg) {
printf("%s\r\n", msg);
fflush(stdout);
}
/* ====== Helpers sorties ====== */
static inline void FermerVannes(void) {
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_RESET);
}
static inline void LedUrgence(uint8_t on) {
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, on ? GPIO_PIN_SET : GPIO_PIN_RESET);
}
/* ====== Lecture ADC protégée ====== */
uint16_t ReadADC(uint32_t channel)
{
xSemaphoreTake(adcMutex, portMAX_DELAY);
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.Channel = channel;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES_5;
HAL_ADC_ConfigChannel(&hadc, &sConfig);
HAL_ADC_Start(&hadc);
HAL_ADC_PollForConversion(&hadc, HAL_MAX_DELAY);
uint32_t v = HAL_ADC_GetValue(&hadc);
HAL_ADC_Stop(&hadc);
xSemaphoreGive(adcMutex);
return (uint16_t)(v * 1000 / 1024); // 0..1000
}
/* ===================== TÂCHES ===================== */
/* (1) Surveillance des 2 capteurs simulés (pots A0 et A1)
Compare au seuil, envoie 1 ou 2 dans la queue toutes les 5s */
void task_surveillance(void *pvParameters)
{
(void)pvParameters;
UART_Send("SURV START");
while (1) {
// Si urgence active : on peut choisir de ne rien envoyer (plus sûr)
if (!urgenceActive) {
uint16_t msg1 = 1;
uint16_t msg2 = 2;
uint16_t p1 = ReadADC(ADC_CHANNEL_0); // A0 (pot capteur 1)
uint16_t p2 = ReadADC(ADC_CHANNEL_1); // A1 (pot capteur 2)
uint16_t s;
xSemaphoreTake(seuilMutex, portMAX_DELAY);
s = seuil;
xSemaphoreGive(seuilMutex);
if (p1 < s) xQueueSendToBack(MyQueueHandle, &msg1, 0);
if (p2 < s) xQueueSendToBack(MyQueueHandle, &msg2, 0);
}
vTaskDelay(pdMS_TO_TICKS(5000)); // 5 s
}
}
/* (2) Activation : reçoit 1/2 et ouvre la vanne (LED) correspondante pendant 10s */
void task_activation(void *pvParameters)
{
(void)pvParameters;
UART_Send("ACT START");
uint16_t receivedMessage;
while (1) {
if (xQueueReceive(MyQueueHandle, &receivedMessage, portMAX_DELAY) == pdPASS) {
// Si urgence active : on ignore tout (pour respecter "vannes fermées")
if (urgenceActive) {
FermerVannes();
continue;
}
if (receivedMessage == 1) {
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
} else if (receivedMessage == 2) {
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_SET);
}
vTaskDelay(pdMS_TO_TICKS(10000)); // 10 s (comme les consignes habituelles)
FermerVannes();
}
}
}
/* (3) Bouton urgence : surveille PB3 (pull-up), donne le sémaphore quand pressé.
Sans EXTI => polling + anti-rebond + détection de front */
void task_boutonUrgence(void *pvParameters)
{
(void)pvParameters;
UART_Send("BTN START");
uint8_t last = 1; // pull-up => repos = 1
while (1) {
uint8_t now = (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_3) == GPIO_PIN_SET) ? 1 : 0;
// Front descendant : 1 -> 0 (appui)
if (last == 1 && now == 0) {
xSemaphoreGive(SemaphoreUrgence);
vTaskDelay(pdMS_TO_TICKS(50)); // anti-rebond simple
}
last = now;
vTaskDelay(pdMS_TO_TICKS(10)); // poll rapide
}
}
/* (4) Gestion urgence : attend le sémaphore binaire, coupe tout immédiatement */
void task_gestionUrgence(void *pvParameters)
{
(void)pvParameters;
UART_Send("URG START");
while (1) {
if (xSemaphoreTake(SemaphoreUrgence, portMAX_DELAY) == pdTRUE) {
urgenceActive = 1; // état urgence
FermerVannes(); // fermeture immédiate
LedUrgence(1); // LED rouge urgence ON
UART_Send("ARRET D'URGENCE !");
// Ici on garde l'urgence tant que le bouton reste appuyé.
// Dès relâchement, on revient en mode normal.
while (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_3) == GPIO_PIN_RESET) {
vTaskDelay(pdMS_TO_TICKS(20));
}
// Sortie d'urgence (si tu préfères un reset manuel, supprime ce bloc)
LedUrgence(0);
urgenceActive = 0;
UART_Send("FIN URGENCE");
}
}
}
/* (5) Ajustement du seuil : lit le pot du seuil (A2 -> PA4 -> CH4) toutes les 5s */
void task_ajustementSeuil(void *pvParameters)
{
(void)pvParameters;
UART_Send("TH START");
while (1) {
uint16_t s = ReadADC(ADC_CHANNEL_2); // A2 sur Nucleo C031 = PA4 = ADC_CHANNEL_2
xSemaphoreTake(seuilMutex, portMAX_DELAY);
seuil = s;
xSemaphoreGive(seuilMutex);
vTaskDelay(pdMS_TO_TICKS(5000)); // 5 s
}
}
/* ===================== MAIN ===================== */
void loop() {
// put your main code here, to run repeatedly:
delay(10); // this speeds up the simulation
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_ADC_Init();
// RTOS objets
MyQueueHandle = xQueueCreate(5, sizeof(uint16_t));
seuilMutex = xSemaphoreCreateMutex();
adcMutex = xSemaphoreCreateMutex();
SemaphoreUrgence = xSemaphoreCreateBinary();
if (!MyQueueHandle || !seuilMutex || !adcMutex || !SemaphoreUrgence) {
while (1);
}
// Initialiser seuil au démarrage (cohérent avec la tâche seuil)
seuil = ReadADC(ADC_CHANNEL_4);
// Priorités : urgence plus haute (arrêt immédiat)
xTaskCreate(task_gestionUrgence, "URG", 256, NULL, 4, NULL);
xTaskCreate(task_boutonUrgence, "BTN", 256, NULL, 3, NULL);
xTaskCreate(task_surveillance, "SURV",256, NULL, 2, NULL);
xTaskCreate(task_activation, "ACT", 256, NULL, 1, NULL);
xTaskCreate(task_ajustementSeuil, "TH", 256, NULL, 1, NULL);
vTaskStartScheduler();
while (1);
}
/* ===================== INIT GPIO/ADC ===================== */
static void MX_GPIO_Init(void)
{
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct = {0};
// LEDs PA5, PA6, PA7
GPIO_InitStruct.Pin = GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// Bouton PB3 en entrée pull-up
GPIO_InitStruct.Pin = GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
// ADC pins : PA0, PA1, PA4 en analog
GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_4;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// état initial
FermerVannes();
LedUrgence(0);
}
static void MX_ADC_Init(void)
{
__HAL_RCC_ADC_CLK_ENABLE();
hadc.Instance = ADC1;
hadc.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc.Init.Resolution = ADC_RESOLUTION_10B;
hadc.Init.ScanConvMode = DISABLE;
hadc.Init.ContinuousConvMode = DISABLE;
hadc.Init.DiscontinuousConvMode = DISABLE;
hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc.Init.NbrOfConversion = 1;
hadc.Init.DMAContinuousRequests = DISABLE;
hadc.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
HAL_ADC_Init(&hadc);
}