SmartBar
Loading...
Searching...
No Matches
cli.h
1
2#ifndef CLI_H
3#define CLI_H
4
5#include <stdlib.h>
6#include <string.h>
7
8// Length of the receive buffer
9#define RX_BUFFER_SIZE 24
10
11// Length of the circular buffer
12#define CIRC_BUFFER_SIZE 16
13
14// Length of the command vector
15#define CMD_VCTR_SIZE 8
16
17// Length of the command parameters
18#define ARG_LENGTH 4
19
20// Minimum duty cycle for the servo motor (2.5% or 0°)
21#define SERVO_DUTY_CYCLE_LOWER_BOUND 2.5
22
23// Maximum duty cycle for the servo motor (12.5% or 180°)
24#define SERVO_DUTY_CYCLE_UPPER_BOUND 12.5
25
26// Threshold for the PID controller
27#define PID_THRESHOLD 0.90
28
29// Buffer for storing received data from UART
30char rxBuffer[RX_BUFFER_SIZE];
31
40typedef struct {
41 char name[ARG_LENGTH];
42 char value[ARG_LENGTH];
44
55typedef struct {
56 char cmdName[ARG_LENGTH];
57 void (*function)(void);
58 CommandArgs args[CMD_VCTR_SIZE];
60
69typedef struct {
70 const char *name;
71 void (*function)(void);
72} Command;
73
74// Circular buffer for storing commands
75CommandVector circBuffer[CIRC_BUFFER_SIZE];
76
77volatile uint8_t cmdWriteIdx = 0;
78volatile uint8_t cmdReadIdx = 0;
79volatile uint8_t cmdCount = 0;
80
81double target = 0.0;
82double input = 0.0;
83double output = 0.0;
84volatile long encoderPos;
85double pulsesPerRev = 580.0;
86uint32_t interval = 20000;
87
88double Kp = 0.55273;
89double Ki = 0.30508;
90double Kd = 0.05222;
91
92double lastInput = 0.0;
93double integral = 0.0;
94double integralMax = 100.0;
95double integralMin = -100.0;
96uint32_t lastTime = 0;
97uint8_t motorSpeed = 0;
98
99extern TIM_HandleTypeDef htim3;
100extern TIM_HandleTypeDef htim15;
101extern TIM_HandleTypeDef htim16;
102extern TIM_HandleTypeDef htim17;
103
104/* Function protoypes */
105
106void ParseCommand(void);
107void ExecutePWM(CommandVector*);
108void ExecutePID(CommandVector *);
109void SelectInputChannel(uint8_t);
110void SelectOutputChannel(uint8_t);
111void ComputePID(void);
112void setPWMPulse(int, float);
113float clamp(float, float, float);
114float map(float, float, float, float, float);
115
121void ParseCommand(void) {
122 // Parse the command to get its parameters
123 char *input = (char*) rxBuffer;
124 char *token = strtok(input, " ");
125 int argIndex = 0;
126 CommandVector command;
127
128 // Initialize the command structure
129 memset(&command, 0, sizeof(CommandVector));
130
131 // Get the command name
132 strcpy(command.cmdName, token);
133 token = strtok(NULL, " ");
134
135 // Get the command arguments
136 while (token != NULL && argIndex < CMD_VCTR_SIZE) {
137 if (token[0] == '-') {
138 // Get the argument name
139 strcpy(command.args[argIndex].name, token);
140 token = strtok(NULL, " ");
141
142 // Get the argument value, if provided
143 if (token != NULL) {
144 strcpy(command.args[argIndex].value, token);
145 argIndex++;
146 }
147 }
148
149 token = strtok(NULL, " ");
150 }
151
152 // Store the command in the circular buffer if there is space available
153 if (cmdCount < CIRC_BUFFER_SIZE) {
154 circBuffer[cmdWriteIdx] = command;
155 cmdWriteIdx = (cmdWriteIdx + 1) % CIRC_BUFFER_SIZE;
156 cmdCount++;
157 }
158}
159
168void ExecutePWM(CommandVector *cmdVctr) {
169 int inputChnl = -1;
170 int outputChnl = -1;
171 float dutyCycle = -1;
172 float time = -1;
173
174 // Get the command arguments
175 for (int i = 0; i < CMD_VCTR_SIZE; i++) {
176 // Input channel
177 if (strcmp(cmdVctr->args[i].name, "-i") == 0) {
178 inputChnl = atoi(cmdVctr->args[i].value);
179 }
180 // Output channel
181 else if (strcmp(cmdVctr->args[i].name, "-o") == 0) {
182 outputChnl = atoi(cmdVctr->args[i].value);
183 }
184 // Duty cycle (as a percentage)
185 else if (strcmp(cmdVctr->args[i].name, "-d") == 0) {
186 dutyCycle = atof(cmdVctr->args[i].value);
187 }
188 else if (strcmp(cmdVctr->args[i].name, "-t") == 0) {
189 time = atof(cmdVctr->args[i].value);
190 }
191 }
192
193 // Select the MUX input channel in zero-index notation
194 SelectInputChannel((uint8_t) inputChnl - 1);
195
196 // Select the DMux output channel in zero-index notation
197 SelectOutputChannel((uint8_t) outputChnl - 1);
198
199 // Calculate the pulse width and set the duty cycle of the signal
200 setPWMPulse(inputChnl - 1, dutyCycle);
201
202 // Timer for the PWM signal
203 if (time > 0) {
204 uint32_t target = time * 1000000;
205 uint32_t lastTime = __HAL_TIM_GET_COUNTER(&htim16);
206 uint32_t previousCounter = lastTime;
207 uint32_t overflow = 0;
208 uint32_t elapsed = 0;
209
210 // Wait until the target time is reached
211 while (elapsed <= target) {
212 uint32_t currentCounter = __HAL_TIM_GET_COUNTER(&htim16);
213
214 if (currentCounter < previousCounter) {
215 overflow++;
216 }
217
218 // Calculate the elapsed time by adding the overflow (65535 ticks per overflow)
219 // to the current counter value and subtracting the last time
220 elapsed = (overflow * 0x10000 + currentCounter - lastTime);
221 previousCounter = currentCounter;
222 }
223
224 // Stop the PWM signal
225 setPWMPulse(inputChnl - 1, 0.0);
226 }
227}
228
237void ExecutePID(CommandVector *cmdVctr) {
238 target = atof(cmdVctr->args[0].value) * pulsesPerRev;
239 uint32_t lastTime = __HAL_TIM_GET_COUNTER(&htim16);
240
241 while (encoderPos != (target * PID_THRESHOLD)) {
242 uint32_t now = __HAL_TIM_GET_COUNTER(&htim16);
243 uint32_t elapsed = now >= lastTime ? (now - lastTime) : ((0xFFFF - lastTime) + now + 1);
244
245 if (elapsed >= interval) {
246 lastTime = now;
247 input = encoderPos;
248
249 ComputePID();
250
251 if (output > 0) {
252 HAL_GPIO_WritePin(PH1_GPIO_Port, PH1_Pin, GPIO_PIN_SET);
253 HAL_GPIO_WritePin(PH2_GPIO_Port, PH2_Pin, GPIO_PIN_RESET);
254 motorSpeed = output;
255 } else {
256 HAL_GPIO_WritePin(PH1_GPIO_Port, PH1_Pin, GPIO_PIN_RESET);
257 HAL_GPIO_WritePin(PH2_GPIO_Port, PH2_Pin, GPIO_PIN_SET);
258 motorSpeed = -output;
259 }
260
261 motorSpeed = clamp(motorSpeed, 0, 255);
262 __HAL_TIM_SET_COMPARE(&htim17, TIM_CHANNEL_1, motorSpeed); // Ajusta el PWM
263 }
264 }
265
266 HAL_GPIO_WritePin(PH1_GPIO_Port, PH1_Pin, GPIO_PIN_RESET);
267 HAL_GPIO_WritePin(PH2_GPIO_Port, PH2_Pin, GPIO_PIN_RESET);
268 __HAL_TIM_SET_COMPARE(&htim17, TIM_CHANNEL_1, 0);
269}
270
271void ComputePID() {
272 uint32_t now = __HAL_TIM_GET_COUNTER(&htim16);
273 uint32_t deltaMicros = now - lastTime;
274 lastTime = now;
275
276 double deltaTime = deltaMicros / 1000000.0;
277 double error = target - input;
278 integral += error * deltaTime;
279
280 if (integral > integralMax) integral = integralMax;
281 if (integral < integralMin) integral = integralMin;
282
283 double derivative = (input - lastInput) / deltaTime;
284
285 output = Kp * error + Ki * integral + Kd * derivative;
286
287 if (output > 255) output = 255;
288 if (output < -255) output = -255;
289
290 lastInput = input;
291}
292
302float clamp(float input, float lowerBound, float upperBound) {
303 return input < lowerBound
304 ? lowerBound
305 : input > upperBound
306 ? upperBound
307 : input;
308}
309
323float map(float value, float fromLower, float fromUpper, float toLower, float toUpper) {
324 return (value - fromLower) * (toUpper - toLower) / (fromUpper - fromLower) + toLower;
325}
326
337void setPWMPulse(int inputChannel, float dutyCycle) {
338 // Maps the duty cycle from a percentage to a value between 2.5 and 12.5, corresponding to 0° and 180°
339 // in the servo motor
340 if (inputChannel == 0) {
341 dutyCycle = map(dutyCycle, 0, 100, SERVO_DUTY_CYCLE_LOWER_BOUND, SERVO_DUTY_CYCLE_UPPER_BOUND);
342 }
343
344 uint8_t pulse = (uint8_t)((dutyCycle / 100.0) * 255);
345
346 // Selectes the proper timer and channel to set the duty cycle of the signal
347 if (inputChannel == 0) {
348 __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, pulse);
349 } else if (inputChannel == 1) {
350 __HAL_TIM_SET_COMPARE(&htim15, TIM_CHANNEL_1, pulse);
351 }
352}
353
354#endif // CLI_H
Structure for storing command arguments.
Definition cli.h:40
Structure for storing commands.
Definition cli.h:69
Structure for storing commands.
Definition cli.h:55