| name | c-cpp-industrial-embedded |
| description | Programmer en C et C++ pour l'embarqué et le temps réel. |
| version | 1.1.0 |
| author | EVA |
| license | Privée EVA St-Étienne |
| platforms | ["linux","windows"] |
| metadata | {"tags":["c","cpp","embedded","realtime","plcnext","twincat","ethercat","profinet","fieldbus","cross-compilation"],"related_skills":["iec61131-3-programming-standards","rust-cython-for-industry","ot-it-integration-languages"]} |
Programmation C / C++ Temps Réel et Systèmes Industriels Embarqués
Cette compétence encadre le développement d'applications bas niveau en C et C++ destinées aux microcontrôleurs, aux ordinateurs industriels durcis (IPC), aux automates programmables ouverts (PLCnext, TwinCAT) et aux bus de terrain temps réel.
1. Programmation Temps Réel Déterministe sous Linux RT-PREEMPT
Pour garantir qu'une tâche s'exécute précisément à intervalles réguliers (gigue inférieure à quelques microsecondes), il est indispensable d'utiliser l'ordonnanceur temps réel et de verrouiller l'espace mémoire.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sched.h>
#include <sys/mman.h>
#include <pthread.h>
#define NANO_SECONDS_PER_SEC 1000000000
void* rt_periodic_task(void* arg) {
struct sched_param param;
param.sched_priority = 99;
if (pthread_setschedparam(pthread_self(), SCHED_FIFO, ¶m) == -1) {
perror("pthread_setschedparam a échoué");
return NULL;
}
if (mlockall(MCL_CURRENT | MCL_FUTURE) == -1) {
perror("mlockall a échoué");
return NULL;
}
struct timespec next_period;
clock_gettime(CLOCK_MONOTONIC, &next_period);
period_ns = ;
() {
next_period.tv_nsec += period_ns;
(next_period.tv_nsec >= NANO_SECONDS_PER_SEC) {
next_period.tv_nsec -= NANO_SECONDS_PER_SEC;
next_period.tv_sec++;
}
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next_period, );
}
;
}
2. Intégration C++ sur Automates de Nouvelle Génération
Phoenix Contact PLCnext (Exemple de Component C++)
Les composants C++ PLCnext s'interfacent avec le Global Data Space (GDS).
#include "Arp/System/Core/Arp.h"
#include "Arp/System/Commons/Logging.h"
#include "Arp/Plc/Commons/Domain/ComponentBase.h"
namespace EVA {
class MainPlcComponent : public Arp::Plc::Commons::Domain::ComponentBase
{
public:
MainPlcComponent(IApplication& application, const String& name);
virtual ~MainPlcComponent() = default;
void Initialize() override;
void LoadConfig() override;
void SetupConfig() override;
void ResetConfig() override;
int32 input_laser_distance_mm = 0;
bool output_ejection_cylinder = false;
void ExecuteCyclic()
{
(input_laser_distance_mm > ) {
output_ejection_cylinder = ;
} {
output_ejection_cylinder = ;
}
}
};
}
3. Communication de Bus de Terrain Directe (SocketCAN)
CANopen et DeviceNet s'appuient sur le bus physique CAN. Sous Linux, SocketCAN permet de manipuler les trames comme des connexions réseau.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>
int init_can_port(const char* interface_name) {
int s;
struct sockaddr_can addr;
struct ifreq ifr;
if ((s = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
perror("Erreur création socket CAN");
return -1;
}
strcpy(ifr.ifr_name, interface_name);
ioctl(s, SIOCGIFINDEX, &ifr);
memset(&addr, 0, sizeof(addr));
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
if (bind(s, (struct sockaddr *)&addr, (addr)) < ) {
perror();
close(s);
;
}
s;
}
{
frame.can_id = + node_id;
frame.can_dlc = ;
frame.data[] = (torque_value >> ) & ;
frame.data[] = torque_value & ;
(write(socket_fd, &frame, ( can_frame)) != ( can_frame)) {
perror();
;
}
;
}
4. Règles de Sécurité Mémoire et Temps Réel (C++)
- Pas de STL dynamique en boucle cyclique : N'utilisez pas
std::vector::push_back, std::string ou std::shared_ptr dans la boucle temps réel car ils provoquent des allocations mémoire dynamiques invisibles sous le capot. Utilisez std::array ou pré-allouez la capacité des vecteurs au démarrage (std::vector::reserve).
- Gestion du tas (Heap) : Instanciez tous vos objets au démarrage dans la phase d'initialisation de l'automate. Les destructeurs ne doivent pas non plus être appelés en boucle cyclique.
- Diagnostics statiques : Utilisez l'analyse statique (
cppcheck, clang-tidy) et compilez avec -Wall -Wextra -Werror pour interdire tout comportement indéterminé du compilateur.