-
Notifications
You must be signed in to change notification settings - Fork 22
/
Copy pathboot.c
222 lines (168 loc) · 6.08 KB
/
boot.c
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
219
220
221
/**
boot.c: Openchronos-ng on boot wireless updater
Copyright (C) 2012 Angelo Arrifano <[email protected]>
Copyright (C) 2016 Benjamin Sølberg <[email protected]>
http://github.com/BenjaminSoelberg/openchronos-ng-elf
This file is part of openchronos-ng.
openchronos-ng is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
openchronos-ng is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
**/
/*
Developer note: I thought about using SYSRIVECT, to override the
interrupt handler of PORT2 (buttons). This would allow temporarily
replacing the interrupt vector in RAM at boot, providing button
interrupts needed to wake up the system from low power.
Unfortunately this would require to modify the linker script to
reserve 128 bytes at the TOP of the RAM for the new interrupt vector.
Since we are not handling watchdog timer interrupts in the main program,
I thought we could use it as a source of external events to wakeup the
CPU from low power mode and check buttons state.
*/
#include "openchronos.h"
#include "drivers/pmm.h"
#include "drivers/wdt.h"
/* Entry point of of the Flash Updater in BSL memory */
#define CALL_RFSBL() ((void (*)())0x1000)()
#define ALL_BUTTONS 0x1F
#define PORTS_BTN_DOWN_PIN (BIT0)
inline void initialize_aclk()
{
/* Select XIN, XOUT on P5.0 and P5.1 */
P5SEL |= 0x03;
/* XT1 On, Highest drive strength */
UCSCTL6 &= ~XT1OFF;
/* Internal load cap */
UCSCTL6 |= XCAP_3;
/* Select XT1 as FLL reference */
UCSCTL3 = SELA__XT1CLK;
/* Set ACLK source to XT1CLK, set SMCLK to DCOCLKDIV, set MCLK to DCOCLKDIV, Enable the FLL control loop */
UCSCTL4 = SELA__XT1CLK | SELS__DCOCLKDIV | SELM__DCOCLKDIV;
}
inline void initialize_cpu_12mhz()
{
/* Disable the FLL control loop */
_BIS_SR(SCG0);
/* Set lowest possible DCOx, MODx */
UCSCTL0 = 0x0000;
/* Select suitable range */
UCSCTL1 = DCORSEL_5;
/* Set DCO Multiplier */
UCSCTL2 = FLLD_1 + 0x16E; // (32768 * 0x16e) almost 12 mhz
_BIC_SR(SCG0);
/* Worst-case settling time for the DCO when the DCO range bits have been
changed is n x 32 x 32 x f_MCLK / f_FLL_reference. See UCS chapter in 5xx
UG for optimization.
32 x 32 x 12 MHz / 32,768 Hz = 250000 = MCLK cycles for DCO to settle */
__delay_cycles(375000);
/* Loop until XT1 & DCO stabilizes, use do-while to insure that
body is executed at least once */
do {
UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + XT1HFOFFG + DCOFFG);
/* Clear fault flags */
SFRIFG1 &= ~OFIFG;
} while ((SFRIFG1 & OFIFG));
}
inline void initialize_buttons()
{
/* Set button ports to input */
P2DIR &= ~ALL_BUTTONS;
/* Enable internal pull-downs */
P2OUT &= ~ALL_BUTTONS;
P2REN |= ALL_BUTTONS;
}
inline void initialize_lcd()
{
/* clear entire display memory */
LCDBMEMCTL |= LCDCLRBM + LCDCLRM;
/* flickers in the sun */
/* LCD_FREQ = ACLK/12/8 = 341.3Hz */
/* still flickers in the sun, when watch is moving */
/* LCD_FREQ = ACLK/10/8 = 409.6Hz */
/* no flickering */
/* LCD_FREQ = ACLK/8/8 = 512Hz */
/* Frame frequency = 512Hz/2/4 = 64Hz, LCD mux 4, LCD on */
LCDBCTL0 = (LCDDIV0 + LCDDIV1 + LCDDIV2)
| (LCDPRE0 + LCDPRE1) | LCD4MUX | LCDON;
/* LCB_BLK_FREQ = ACLK/8/2048 = 2Hz */
LCDBBLKCTL = LCDBLKPRE1 | LCDBLKDIV0 | LCDBLKDIV1
| LCDBLKDIV2 | LCDBLKMOD0;
/* I/O to COM outputs */
P5SEL |= (BIT5 | BIT6 | BIT7);
P5DIR |= (BIT5 | BIT6 | BIT7);
/* Activate LCD output */
/* Select LCD segments S0-S15 */
LCDBPCTL0 = 0xFFFF;
/* Select LCD segments int16_t-S22 */
LCDBPCTL1 = 0x00FF;
#ifdef USE_LCD_CHARGE_PUMP
/* Charge pump voltage generated internally,
internal bias (V2-V4) generation */
LCDBVCTL = LCDCPEN | VLCD_2_72;
#endif
}
inline void jump_to_rfbsl()
{
/* clear display memory (useful to know if rfbsl failed) */
LCDBMEMCTL |= LCDCLRBM + LCDCLRM;
/* finally jump to the BSL memory address */
CALL_RFSBL();
}
/* put bootmenu in the crt_0042 section which is executed before main */
__attribute__((naked, section(".crt_0042"), used))
static void crt_0042(void)
{
wdt_stop();
/* Configure PMM */
SetVCore(3);
/* Set global high power request enable */
{
PMMCTL0_H = 0xA5;
PMMCTL0_L |= PMMHPMRE;
PMMCTL0_H = 0x00;
}
/* Enable 32kHz ACLK */
initialize_aclk();
/* Configure CPU clock for 12MHz */
initialize_cpu_12mhz();
/* Configure buttons for input */
initialize_buttons();
/* Initialize LCD */
initialize_lcd();
/* Write 'boot' to the screen without using display functions */
LCDM2 = 199; /* 'b' */
LCDM3 = 198; /* 'o' */
LCDM4 = 198; /* 'o' */
LCDM6 = 135; /* 't' */
/* configure watchdog interrupt timer, used for polling buttons */
{
/* ACLK timer source, 250ms timer mode, resume watchdog */
WDTCTL = WDT_ADLY_250;
/* Enable watchdog timer interrupts */
SFRIE1 |= WDTIE;
}
/* Enable global interrupts */
__enable_interrupt();
/* loop if no button is pressed, enter RFBSL if backlight is pressed */
do {
_BIS_SR(LPM3_bits | GIE);
__no_operation();
if ((P2IN & ALL_BUTTONS) == PORTS_BTN_DOWN_PIN)
jump_to_rfbsl();
} while ((P2IN & ALL_BUTTONS) == 0);
/* Disable them again, they will be re-enabled later on in main() */
__disable_interrupt();
}
__attribute__((interrupt(WDT_VECTOR)))
void WDT_ISR(void)
{
/* exit from LPM3 after interrupt */
_BIC_SR_IRQ(LPM3_bits);
}