led.cpp
2.82 KB
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
#include <ion/led.h>
#include <ion/display.h>
#include "led.h"
#include "regs/regs.h"
// Public Ion::LED methods
void Ion::LED::setColor(KDColor c) {
TIM3.CCR2()->set(Device::dutyCycleForUInt8(c.red()));
TIM3.CCR3()->set(Device::dutyCycleForUInt8(c.blue()));
TIM3.CCR4()->set(Device::dutyCycleForUInt8(c.green()));
}
// Private Ion::Device::LED methods
namespace Ion {
namespace LED {
namespace Device {
void init() {
initGPIO();
initTimer();
}
void shutdown() {
shutdownTimer();
shutdownGPIO();
}
void initGPIO() {
/* RED_LED(PC7), GREEN_LED(PB1), and BLUE_LED(PB0) are driven using a timer,
* which is an alternate function. More precisely, we will use AF2, which maps
* PB0 to TIM3_CH2, PB1 to TIM3_CH4, and PC7 to TIM3_CH2. */
for(const GPIOPin & g : RGBPins) {
g.group().MODER()->setMode(g.pin(), GPIO::MODER::Mode::AlternateFunction);
g.group().AFR()->setAlternateFunction(g.pin(), GPIO::AFR::AlternateFunction::AF2);
}
}
void shutdownGPIO() {
for(const GPIOPin & g : RGBPins) {
g.group().MODER()->setMode(g.pin(), GPIO::MODER::Mode::Analog);
g.group().PUPDR()->setPull(g.pin(), GPIO::PUPDR::Pull::None);
}
}
void initTimer() {
/* Let's set the prescaler to 1. Increasing the prescaler would slow down the
* modulation, which can be useful when debugging. */
TIM3.PSC()->set(1);
/* Pulse width modulation mode allows you to generate a signal with a
* frequency determined by the value of the TIMx_ARR register and a duty cycle
* determined by the value of the TIMx_CCRx register. */
TIM3.ARR()->set(PWMPeriod);
TIM3.CCR2()->set(0);
TIM3.CCR3()->set(0);
TIM3.CCR4()->set(0);
// Set Channels 2-4 as outputs, PWM mode 1
TIM3.CCMR()->setOC2M(TIM::CCMR::OCM::PWM1);
TIM3.CCMR()->setOC3M(TIM::CCMR::OCM::PWM1);
TIM3.CCMR()->setOC4M(TIM::CCMR::OCM::PWM1);
// Output preload enable for channels 2-4
TIM3.CCMR()->setOC2PE(true);
TIM3.CCMR()->setOC3PE(true);
TIM3.CCMR()->setOC4PE(true);
// Auto-reload preload enable
TIM3.CR1()->setARPE(true);
// Enable Capture/Compare for channel 2 to 4
TIM3.CCER()->setCC2E(true);
TIM3.CCER()->setCC3E(true);
TIM3.CCER()->setCC4E(true);
TIM3.BDTR()->setMOE(true);
TIM3.CR1()->setCEN(true);
}
void shutdownTimer() {
TIM3.CCMR()->setOC2M(TIM::CCMR::OCM::ForceInactive);
TIM3.CCMR()->setOC3M(TIM::CCMR::OCM::ForceInactive);
TIM3.CCMR()->setOC4M(TIM::CCMR::OCM::ForceInactive);
}
void enforceState(bool red, bool green, bool blue) {
bool states[3] = {red, green, blue};
for (int i=0; i<3; i++) {
GPIOPin p = RGBPins[i];
if (states[i]) {
p.group().MODER()->setMode(p.pin(), GPIO::MODER::Mode::Output);
p.group().ODR()->set(p.pin(), true);
} else {
p.group().MODER()->setMode(p.pin(), GPIO::MODER::Mode::Analog);
p.group().PUPDR()->setPull(p.pin(), GPIO::PUPDR::Pull::None);
}
}
}
}
}
}