-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathHAL_SAMD51J19A.c
124 lines (111 loc) · 6.06 KB
/
HAL_SAMD51J19A.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
#include "HAL.h"
#ifdef __SAMD51J19A__
#if defined(__DEBUG) && __DEBUG
volatile int SIDE_EFFECT_FOR_DEBUGGING = 0x12345678;
#endif
#include "samd51j19a.h"
#include <string.h>
void* const PORT_A = PORT_REGS->GROUP;
void* const PORT_B = PORT_REGS->GROUP + 1;
void HAL_GPIO_Init(void* port, uint32_t pins) {
((port_group_registers_t*) port)->PORT_DIRSET = pins;
}
void HAL_GPIO_TogglePin(void* port, uint32_t pins) {
((port_group_registers_t*) port)->PORT_OUTTGL = pins;
}
void TC0_Handler() {
// turn off the sleep timer, since we're now awake
NVIC_DisableIRQ(TC0_IRQn);
TC0_REGS->COUNT32.TC_INTENCLR = TC_INTENCLR_MC0_Msk;
}
void HAL_32kHz_Init() {
// set GCLK0 to use the internal ultra low power 332kHz oscillator
OSC32KCTRL_REGS->OSC32KCTRL_OSCULP32K = (OSC32KCTRL_REGS->OSC32KCTRL_OSCULP32K & ~OSC32KCTRL_OSCULP32K_EN32K_Msk) | OSC32KCTRL_OSCULP32K_EN32K(1);
GCLK_REGS->GCLK_GENCTRL[2] = (GCLK_REGS->GCLK_GENCTRL[2] &
~GCLK_GENCTRL_GENEN_Msk & ~GCLK_GENCTRL_DIVSEL_Msk & ~GCLK_GENCTRL_DIV_Msk & ~GCLK_GENCTRL_SRC_Msk)
| (GCLK_GENCTRL_GENEN(1) | GCLK_GENCTRL_DIVSEL(0) | GCLK_GENCTRL_DIV(1) | GCLK_GENCTRL_SRC_OSCULP32K);
// set TC0/TC1 to use the 32kHz clock given by GCLK0
GCLK_REGS->GCLK_PCHCTRL[9] = (GCLK_REGS->GCLK_PCHCTRL[9] & ~GCLK_PCHCTRL_CHEN_Msk) | GCLK_PCHCTRL_CHEN(0);
while (GCLK_REGS->GCLK_SYNCBUSY);
while ((GCLK_REGS->GCLK_PCHCTRL[9] & GCLK_PCHCTRL_CHEN_Msk) != GCLK_PCHCTRL_CHEN(0));
GCLK_REGS->GCLK_PCHCTRL[9] = (GCLK_REGS->GCLK_PCHCTRL[9] & ~GCLK_PCHCTRL_GEN_Msk) | GCLK_PCHCTRL_GEN_GCLK0;
GCLK_REGS->GCLK_PCHCTRL[9] = (GCLK_REGS->GCLK_PCHCTRL[9] & ~GCLK_PCHCTRL_CHEN_Msk) | GCLK_PCHCTRL_CHEN(1);
while (GCLK_REGS->GCLK_SYNCBUSY);
while ((GCLK_REGS->GCLK_PCHCTRL[9] & GCLK_PCHCTRL_CHEN_Msk) != GCLK_PCHCTRL_CHEN(1));
}
void HAL_Sleep(uint32_t millis) {
// start the timer for however long we're running
TC0_REGS->COUNT32.TC_CC[0] = TC0_REGS->COUNT32.TC_COUNT + (millis << 5);
// save port configurations for seamless low-frequency interactions (ports still
// turn off while the device is asleep, so they don't draw significant power)
// do nothing with serial or other similar output configurations because there's
// no way to prevent high-frequency interactions from being interrupted while
// sleeping, that's the whole idea of sleeping
PM_REGS->PM_CTRLA = (PM_REGS->PM_CTRLA & ~PM_CTRLA_IORET_Msk) | PM_CTRLA_IORET(1);
uint32_t saved_ports[PORT_GROUP_NUMBER][4 + sizeof(PORT_REGS->GROUP[0].PORT_PINCFG) + sizeof(PORT_REGS->GROUP[0].PORT_PMUX)];
for (unsigned i = 0U; i < PORT_GROUP_NUMBER; i++) {
saved_ports[i][0] = PORT_REGS->GROUP[i].PORT_DIR;
saved_ports[i][1] = PORT_REGS->GROUP[i].PORT_OUT;
saved_ports[i][2] = PORT_REGS->GROUP[i].PORT_CTRL;
saved_ports[i][3] = PORT_REGS->GROUP[i].PORT_EVCTRL;
memcpy(saved_ports[i] + 4,
// casting away volatility is safe here because if we are going to sleep,
// we don't expect to be interrupted, and this function doesn't change
// PINCFG values with third-party IO registers like WRCONFIG or side effects
(uint8_t*) PORT_REGS->GROUP[i].PORT_PINCFG,
sizeof(PORT_REGS->GROUP[i].PORT_PINCFG)
);
memcpy(saved_ports[i] + 4 + sizeof(PORT_REGS->GROUP[i].PORT_PINCFG),
// casting away volatility is safe here because if we are going to sleep,
// we don't expect to be interrupted, and this function doesn't change
// PMUX values with third-party IO registers like WRCONFIG or side effects
(uint8_t*) PORT_REGS->GROUP[i].PORT_PMUX,
sizeof(PORT_REGS->GROUP[i].PORT_PMUX)
);
}
// set to use HIBERNATE sleep mode with full RAM retention
PM_REGS->PM_HIBCFG = (PM_REGS->PM_HIBCFG & ~PM_HIBCFG_BRAMCFG_Msk & ~PM_HIBCFG_RAMCFG_Msk) | PM_HIBCFG_BRAMCFG_RET | PM_HIBCFG_RAMCFG_RET;
PM_REGS->PM_SLEEPCFG = (PM_REGS->PM_SLEEPCFG & ~PM_SLEEPCFG_Msk) | PM_SLEEPCFG_SLEEPMODE(PM_SLEEPCFG_SLEEPMODE_HIBERNATE);
while ((PM_REGS->PM_SLEEPCFG & PM_SLEEPCFG_Msk) != PM_SLEEPCFG_SLEEPMODE(PM_SLEEPCFG_SLEEPMODE_HIBERNATE));
// wait for interrupt
NVIC_EnableIRQ(TC0_IRQn);
TC0_REGS->COUNT32.TC_INTENSET = TC_INTENSET_MC0_Msk;
__WFI();
// restore saved port configuration
for (unsigned i = 0U; i < PORT_GROUP_NUMBER; i++) {
PORT_REGS->GROUP[i].PORT_DIR = saved_ports[i][0];
PORT_REGS->GROUP[i].PORT_OUT = saved_ports[i][1];
PORT_REGS->GROUP[i].PORT_CTRL = saved_ports[i][2];
PORT_REGS->GROUP[i].PORT_EVCTRL = saved_ports[i][3];
memcpy(
// casting away volatility is safe here because if we just woke up from sleep,
// we don't expect to be interrupted, and this function doesn't change
// PINCFG values with third-party IO registers like WRCONFIG or side effects
(uint8_t*) PORT_REGS->GROUP[i].PORT_PINCFG,
saved_ports[i] + 4,
sizeof(PORT_REGS->GROUP[i].PORT_PINCFG)
);
memcpy(
// casting away volatility is safe here because if we just woke up from sleep,
// we don't expect to be interrupted, and this function doesn't change
// PMUX values with third-party IO registers like WRCONFIG or side effects
(uint8_t*) PORT_REGS->GROUP[i].PORT_PMUX,
saved_ports[i] + 4 + sizeof(PORT_REGS->GROUP[i].PORT_PINCFG),
sizeof(PORT_REGS->GROUP[i].PORT_PMUX)
);
}
PM_REGS->PM_CTRLA = (PM_REGS->PM_CTRLA & ~PM_CTRLA_IORET_Msk) | PM_CTRLA_IORET(0);
}
uint32_t HAL_getTime() {
return TC0_REGS->COUNT32.TC_COUNT;
}
void HAL_I2C_sendData(
enum HAL_Device device, // device number
char data[], // array of data to be sent
int dataSize // the size of data[]
) {}
void HAL_I2C_registerDataRecievedCallback(
enum HAL_Device device, // device must send its address in I2C transmission
void (*dataRecieved)(char data[], int dataSize) // data received callback
) {}
#endif