-
-
Notifications
You must be signed in to change notification settings - Fork 746
/
Copy pathIRQ.cpp
218 lines (188 loc) · 6.86 KB
/
IRQ.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
/*
* IRQ.c
*
* Created on: 30 May 2020
* Author: Ralim
*/
#include "IRQ.h"
#include "Pins.h"
#include "configuration.h"
#include "history.hpp"
extern "C" {
#include "bflb_platform.h"
#include "bl702_adc.h"
#include "bl702_glb.h"
#include "bl702_pwm.h"
#include "bl702_timer.h"
}
void start_PWM_output(void);
#define ADC_Filter_Smooth 4 /* This basically smooths over one PWM cycle / set of readings */
history<uint16_t, ADC_Filter_Smooth> ADC_Vin;
history<uint16_t, ADC_Filter_Smooth> ADC_Temp;
history<uint16_t, ADC_Filter_Smooth> ADC_Tip;
// IRQ is called at the end of the 8 set readings, pop these from the FIFO and send to filters
void adc_fifo_irq(void) {
if (ADC_GetIntStatus(ADC_INT_FIFO_READY) == SET) {
// Read out all entries in the fifo
while (ADC_Get_FIFO_Count()) {
uint32_t reading = ADC_Read_FIFO();
// As per manual, 26 bit reading; lowest 16 are the ADC
uint16_t sample = reading & 0xFFFF;
uint8_t source = (reading >> 21) & 0b11111;
switch (source) {
case TMP36_ADC_CHANNEL:
ADC_Temp.update(sample);
break;
case TIP_TEMP_ADC_CHANNEL:
ADC_Tip.update(sample);
break;
case VIN_ADC_CHANNEL:
ADC_Vin.update(sample);
break;
default:
break;
}
}
// unblock the PID controller thread
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if (pidTaskNotification) {
vTaskNotifyGiveFromISR(pidTaskNotification, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
}
// Clear IRQ
ADC_IntClr(ADC_INT_ALL);
}
volatile bool inFastPWMMode = false;
static void switchToFastPWM(void);
static void switchToSlowPWM(void);
volatile uint16_t PWMSafetyTimer = 0;
volatile uint8_t pendingPWM = 0;
volatile bool pendingNextPeriodIsFast = false;
void start_PWM_output(void) {
if (PWMSafetyTimer) {
PWMSafetyTimer--;
if (pendingNextPeriodIsFast != inFastPWMMode) {
if (pendingNextPeriodIsFast) {
switchToFastPWM();
} else {
switchToSlowPWM();
}
}
// Update trigger for the end point of the PWM cycle
if (pendingPWM > 0) {
TIMER_SetCompValue(TIMER_CH0, TIMER_COMP_ID_1, pendingPWM - 1);
// Turn on output
PWM_Channel_Enable(PWM_Channel);
} else {
// Leave output off
PWM_Channel_Disable(PWM_Channel);
}
} else {
PWM_Channel_Disable(PWM_Channel);
switchToFastPWM();
}
}
// Timer 0 is used to co-ordinate the ADC and the output PWM
void timer0_comp0_callback(void) {
if (PWM_Channel_Is_Enabled(PWM_Channel)) {
// So there appears to be a bug _somewhere_ where sometimes the comparator doesn't fire
// Its not re-occurring with specific values, so suspect its a weird bug
// For now, we just skip the cycle and throw away the ADC readings. Its a waste but
// It stops stupid glitches in readings, i'd take slight instability from the time jump
// Over the readings we get that are borked as the header is left on
// <Ralim 2023/10/14>
PWM_Channel_Disable(PWM_Channel);
// MSG("ALERT PWM Glitch\r\n");
// Triger the PID now instead
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if (pidTaskNotification) {
vTaskNotifyGiveFromISR(pidTaskNotification, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
} else {
ADC_Start();
}
TIMER_ClearIntStatus(TIMER_CH0, TIMER_COMP_ID_0);
}
void timer0_comp1_callback(void) {
// Trigged at end of output cycle; turn off the tip PWM
PWM_Channel_Disable(PWM_Channel);
TIMER_ClearIntStatus(TIMER_CH0, TIMER_COMP_ID_1);
}
void timer0_comp2_callback(void) {
// Triggered at end of timer cycle; re-start the tip driver
start_PWM_output();
TIMER_ClearIntStatus(TIMER_CH0, TIMER_COMP_ID_2);
}
void switchToFastPWM(void) {
inFastPWMMode = true;
holdoffTicks = 10;
tempMeasureTicks = 10;
totalPWM = powerPWM + tempMeasureTicks + holdoffTicks;
TIMER_SetCompValue(TIMER_CH0, TIMER_COMP_ID_2, totalPWM);
// ~10Hz
TIMER_SetCompValue(TIMER_CH0, TIMER_COMP_ID_0, powerPWM + holdoffTicks);
// Set divider to 10 ~= 10.5Hz
uint32_t tmpVal = BL_RD_REG(TIMER_BASE, TIMER_TCDR);
tmpVal = BL_SET_REG_BITS_VAL(tmpVal, TIMER_TCDR2, 10);
BL_WR_REG(TIMER_BASE, TIMER_TCDR, tmpVal);
}
void switchToSlowPWM(void) {
// 5Hz
inFastPWMMode = false;
holdoffTicks = 5;
tempMeasureTicks = 5;
totalPWM = powerPWM + tempMeasureTicks + holdoffTicks;
TIMER_SetCompValue(TIMER_CH0, TIMER_COMP_ID_2, totalPWM);
// Adjust ADC
TIMER_SetCompValue(TIMER_CH0, TIMER_COMP_ID_0, powerPWM + holdoffTicks);
// Set divider for ~ 5Hz
uint32_t tmpVal = BL_RD_REG(TIMER_BASE, TIMER_TCDR);
tmpVal = BL_SET_REG_BITS_VAL(tmpVal, TIMER_TCDR2, 20);
BL_WR_REG(TIMER_BASE, TIMER_TCDR, tmpVal);
}
void setTipPWM(const uint8_t pulse, const bool shouldUseFastModePWM) {
PWMSafetyTimer = 10; // This is decremented in the handler for PWM so that the tip pwm is
// disabled if the PID task is not scheduled often enough.
pendingPWM = pulse;
pendingNextPeriodIsFast = shouldUseFastModePWM;
}
extern osThreadId POWTaskHandle;
void GPIO_IRQHandler(void) {
if (SET == GLB_Get_GPIO_IntStatus(FUSB302_IRQ_GLB_Pin)) {
GLB_GPIO_IntClear(FUSB302_IRQ_GLB_Pin, SET);
#ifdef POW_PD
if (POWTaskHandle != nullptr) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xTaskNotifyFromISR(POWTaskHandle, 1, eSetBits, &xHigherPriorityTaskWoken);
/* Force a context switch if xHigherPriorityTaskWoken is now set to pdTRUE.
The macro used to do this is dependent on the port and may be called
portEND_SWITCHING_ISR. */
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
#endif
/* timeout check */
uint32_t timeOut = 32;
do {
timeOut--;
} while ((SET == GLB_Get_GPIO_IntStatus(FUSB302_IRQ_GLB_Pin)) && timeOut);
if (!timeOut) {
// MSG("WARNING: Clear GPIO interrupt status fail.\r\n");
}
GLB_GPIO_IntClear(FUSB302_IRQ_GLB_Pin, RESET);
}
}
bool getFUS302IRQLow() {
// Return true if the IRQ line is still held low
return !gpio_read(FUSB302_IRQ_Pin);
}
uint16_t getADCHandleTemp(uint8_t sample) { return ADC_Temp.average(); }
uint16_t getADCVin(uint8_t sample) { return ADC_Vin.average(); }
// Returns the current raw tip reading after any cleanup filtering
// For Pinecil V2 we dont do any rolling filtering other than just averaging all 4 readings in the adc snapshot
uint16_t getTipRawTemp(uint8_t sample) { return ADC_Tip.average() >> 1; }