Skip to content

Commit 8ea9906

Browse files
committed
Add: Seeeduino Xiao Hardware SPI slave
1 parent 670c270 commit 8ea9906

File tree

2 files changed

+238
-0
lines changed

2 files changed

+238
-0
lines changed

.travis.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,12 @@ script:
133133
- export BOARD=Seeeduino:samd:seeed_wio_terminal
134134
- buildExampleSketch WioTerminal_Https_Stock_Demo;
135135

136+
- echo "*************************************SeeeduinoXIAO_SPISlave*********************************************"
137+
- rm -rf $HOME/Arduino/libraries/*
138+
- rm -rf /tmp/arduino*
139+
- export BOARD=Seeeduino:samd:seeed_XIAO_m0
140+
- buildExampleSketch SeeeduinoXIAO_SPISlave;
141+
136142
notifications:
137143
webhooks:
138144
urls:
Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
/*
2+
* Seeeduino XIAO Hardware SPI Slave
3+
*
4+
* Thanks goes out to:
5+
* jeremyherbert - https://github.com/jeremyherbert/playful-turtle-xiao
6+
* The atmel SAMD21 library
7+
* and everyone at https://forum.arduino.cc/index.php?topic=360026.0
8+
*
9+
* Notes:
10+
* - We are NOT using the default SPI pins per the Seeeduino documentation - we are reassigning these pins to SERCOM0.
11+
* - MOSI and MISO should be connected direct to the SPI master device - do not reverse/swap them.
12+
* - If you are only receiving/listening, then you can leave pin MISO pin 4 disconnected.
13+
* - You can use Serial for USB and Serial1 for UART TX/RX
14+
*
15+
* Wiring:
16+
* SPI_MOSI - Board Pin: 2 - Connect to MOSI on master
17+
* SPI_SCK - Board Pin: 3
18+
* SPI_MISO - Board Pin: 4 - Connect to MISO on master
19+
* SPI_CS - Board Pin: 5
20+
*/
21+
22+
// DO NOT CHANGE PIN ASSIGMENTS UNLESS YOU KNOW WHAT YOU ARE DOING
23+
// ---- Seeeduino XIAO SPI ----
24+
// Board pin is the numbered pin on the Seeeduino XIAO board (connect wires to these pins)
25+
#define SPI_MOSI_B_PIN 2 // Board Pin
26+
#define SPI_SCK_B_PIN 3 // Board Pin
27+
#define SPI_MISO_B_PIN 4 // Board Pin
28+
#define SPI_CS_B_PIN 5 // Board Pin
29+
// Chip pin is the GPIO pin on the SAMD21 Microcontroller chip (internal only, you can ignore these)
30+
#define SPI_MOSI_PIN 10 // Chip Pin SERCOM PAD2
31+
#define SPI_SCK_PIN 11 // Chip Pin SERCOM PAD3
32+
#define SPI_MISO_PIN 8 // Chip Pin SERCOM PAD0
33+
#define SPI_CS_PIN 9 // Chip Pin SERCOM PAD1
34+
// Macros for the pin and port group
35+
#define GPIO_PIN(n) (((n)&0x1Fu) << 0)
36+
#define GPIO_PORT(n) ((n) >> 5)
37+
#define GPIO(port, pin) ((((port)&0x7u) << 5) + ((pin)&0x1Fu))
38+
#define GPIO_PIN_FUNCTION_OFF 0xffffffff
39+
// Port Critical Sections
40+
#if defined(ENABLE_PORT_CRITICAL_SECTIONS)
41+
#define PORT_CRITICAL_SECTION_ENTER() CRITICAL_SECTION_ENTER()
42+
#define PORT_CRITICAL_SECTION_LEAVE() CRITICAL_SECTION_LEAVE()
43+
#else
44+
#define PORT_CRITICAL_SECTION_ENTER()
45+
#define PORT_CRITICAL_SECTION_LEAVE()
46+
#endif
47+
// Variables
48+
typedef uint8_t hri_port_pmux_reg_t;
49+
50+
void setup() {
51+
52+
// Set all pins are input so we don't disrupt comminication on boot
53+
pinMode(SPI_MOSI_B_PIN, INPUT);
54+
pinMode(SPI_SCK_B_PIN, INPUT);
55+
pinMode(SPI_MISO_B_PIN, INPUT);
56+
pinMode(SPI_CS_B_PIN, INPUT);
57+
58+
// init Serial USB
59+
Serial.begin(115200);//for debugging
60+
61+
//wait for serial to init
62+
while (!Serial);
63+
64+
// Seeduino XIAO - Enable SPI as Slave
65+
Seeeduino_spiSlave_init();
66+
67+
#ifdef ENABLE_PORT_CRITICAL_SECTIONS
68+
Serial.println("ENABLE_PORT_CRITICAL_SECTIONS is defined from the start")
69+
#endif
70+
}
71+
72+
void loop() {
73+
74+
}
75+
76+
void Seeeduino_spiSlave_init()
77+
{
78+
// assign our board pins to SERCOM0
79+
gpio_set_pin_function(SPI_MOSI_PIN, PINMUX_PA10C_SERCOM0_PAD2);
80+
gpio_set_pin_function(SPI_SCK_PIN, PINMUX_PA11C_SERCOM0_PAD3);
81+
gpio_set_pin_function(SPI_MISO_PIN, PINMUX_PA08C_SERCOM0_PAD0);
82+
gpio_set_pin_function(SPI_CS_PIN, PINMUX_PA09C_SERCOM0_PAD1);
83+
84+
//Disable SPI 0
85+
SERCOM0->SPI.CTRLA.bit.ENABLE = 0;
86+
while (SERCOM0->SPI.SYNCBUSY.bit.ENABLE);
87+
88+
//Reset SPI 0
89+
SERCOM0->SPI.CTRLA.bit.SWRST = 1;
90+
while (SERCOM0->SPI.CTRLA.bit.SWRST || SERCOM0->SPI.SYNCBUSY.bit.SWRST);
91+
92+
//Setting up NVIC
93+
NVIC_EnableIRQ(SERCOM0_IRQn);
94+
NVIC_SetPriority(SERCOM0_IRQn, 2);
95+
96+
97+
//Setting Generic Clock Controller!!!!
98+
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(GCM_SERCOM0_CORE) | //Generic Clock 0
99+
GCLK_CLKCTRL_GEN_GCLK0 | // Generic Clock Generator 0 is the source
100+
GCLK_CLKCTRL_CLKEN; // Enable Generic Clock Generator
101+
102+
while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY); //Wait for synchronisation
103+
104+
105+
//Set up SPI Control A Register
106+
SERCOM0->SPI.CTRLA.bit.DORD = 0; //MSB first
107+
SERCOM0->SPI.CTRLA.bit.CPOL = 0; //SCK is low when idle, leading edge is rising edge
108+
SERCOM0->SPI.CTRLA.bit.CPHA = 0; //data sampled on leading sck edge and changed on a trailing sck edge
109+
SERCOM0->SPI.CTRLA.bit.FORM = 0x0; //Frame format = SPI
110+
SERCOM0->SPI.CTRLA.bit.DIPO = 2; //DATA PAD0 MOSI is used as input (slave mode)
111+
SERCOM0->SPI.CTRLA.bit.DOPO = 1; //DATA PAD3 MISO is used as output
112+
//SERCOM0->SPI.CTRLA.bit.DIPO = 0; //DATA PAD0 MOSI is used as input (slave mode)
113+
//SERCOM0->SPI.CTRLA.bit.DOPO = 0x2; //DATA PAD3 MISO is used as output
114+
SERCOM0->SPI.CTRLA.bit.MODE = 0x2; //SPI in Slave mode
115+
SERCOM0->SPI.CTRLA.bit.IBON = 0x1; //Buffer Overflow notification
116+
SERCOM0->SPI.CTRLA.bit.RUNSTDBY = 1; //wake on receiver complete
117+
118+
//Set up SPI control B register
119+
//SERCOM0->SPI.CTRLB.bit.RXEN = 0x1; //Enable Receiver
120+
SERCOM0->SPI.CTRLB.bit.SSDE = 0x1; //Slave Selecte Detection Enabled
121+
SERCOM0->SPI.CTRLB.bit.CHSIZE = 0; //character size 8 Bit
122+
//SERCOM0->SPI.CTRLB.bit.PLOADEN = 0x1; //Enable Preload Data Register
123+
//while (SERCOM0->SPI.SYNCBUSY.bit.CTRLB);
124+
125+
//Set up SPI interrupts
126+
SERCOM0->SPI.INTENSET.bit.SSL = 0x1; //Enable Slave Select low interrupt
127+
SERCOM0->SPI.INTENSET.bit.RXC = 0x1; //Receive complete interrupt
128+
SERCOM0->SPI.INTENSET.bit.TXC = 0x1; //Receive complete interrupt
129+
SERCOM0->SPI.INTENSET.bit.ERROR = 0x1; //Receive complete interrupt
130+
SERCOM0->SPI.INTENSET.bit.DRE = 0x1; //Data Register Empty interrupt
131+
//init SPI CLK
132+
//SERCOM0->SPI.BAUD.reg = SERCOM_FREQ_REF / (2*4000000u)-1;
133+
//Enable SPI
134+
SERCOM0->SPI.CTRLA.bit.ENABLE = 1;
135+
while (SERCOM0->SPI.SYNCBUSY.bit.ENABLE);
136+
SERCOM0->SPI.CTRLB.bit.RXEN = 0x1; //Enable Receiver, this is done here due to errate issue
137+
while (SERCOM0->SPI.SYNCBUSY.bit.CTRLB); //wait until receiver is enabled
138+
}
139+
140+
141+
// Seeduino SPI interrupt routine
142+
void SERCOM0_Handler()
143+
{
144+
noInterrupts();
145+
uint8_t data = 0;
146+
uint8_t interrupts = SERCOM0->SPI.INTFLAG.reg; //Read SPI interrupt register
147+
148+
if (interrupts & (1 << 3))
149+
{
150+
SERCOM0->SPI.INTFLAG.bit.SSL = 1; //clear slave select interrupt
151+
}
152+
153+
// This is where data is received, and is written to a buffer, which is used in the main loop
154+
if (interrupts & (1 << 2))
155+
{
156+
data = SERCOM0->SPI.DATA.reg; //Read data register
157+
SERCOM0->SPI.INTFLAG.bit.RXC = 1; //clear receive complete interrupt
158+
159+
// Put the data somewhere, like a buffer - or print it for testing
160+
Serial.println(data, DEC); // print received data as a number
161+
}
162+
163+
// This is where data is transmitted
164+
if (interrupts & (1 << 1))
165+
{
166+
SERCOM0->SPI.INTFLAG.bit.TXC = 1; //clear transmit complete interrupt
167+
}
168+
169+
// Data Register Empty Interrupt
170+
if (interrupts & (1 << 0))
171+
{
172+
SERCOM0->SPI.DATA.reg = 0xAA;
173+
}
174+
interrupts();
175+
}
176+
177+
static inline void gpio_set_pin_function(const uint32_t gpio, const uint32_t function)
178+
{
179+
uint8_t port = GPIO_PORT(gpio);
180+
uint8_t pin = GPIO_PIN(gpio);
181+
182+
if (function == GPIO_PIN_FUNCTION_OFF) {
183+
hri_port_write_PINCFG_PMUXEN_bit(PORT, port, pin, false);
184+
185+
} else {
186+
hri_port_write_PINCFG_PMUXEN_bit(PORT, port, pin, true);
187+
188+
if (pin & 1) {
189+
// Odd numbered pin
190+
hri_port_write_PMUX_PMUXO_bf(PORT, port, pin >> 1, function & 0xffff);
191+
} else {
192+
// Even numbered pin
193+
hri_port_write_PMUX_PMUXE_bf(PORT, port, pin >> 1, function & 0xffff);
194+
}
195+
}
196+
}
197+
198+
static inline void hri_port_write_PINCFG_PMUXEN_bit(const void *const hw, uint8_t submodule_index, uint8_t index,
199+
bool value)
200+
{
201+
uint8_t tmp;
202+
PORT_CRITICAL_SECTION_ENTER();
203+
tmp = ((Port *)hw)->Group[submodule_index].PINCFG[index].reg;
204+
tmp &= ~PORT_PINCFG_PMUXEN;
205+
tmp |= value << PORT_PINCFG_PMUXEN_Pos;
206+
((Port *)hw)->Group[submodule_index].PINCFG[index].reg = tmp;
207+
PORT_CRITICAL_SECTION_LEAVE();
208+
}
209+
210+
static inline void hri_port_write_PMUX_PMUXO_bf(const void *const hw, uint8_t submodule_index, uint8_t index,
211+
hri_port_pmux_reg_t data)
212+
{
213+
uint8_t tmp;
214+
PORT_CRITICAL_SECTION_ENTER();
215+
tmp = ((Port *)hw)->Group[submodule_index].PMUX[index].reg;
216+
tmp &= ~PORT_PMUX_PMUXO_Msk;
217+
tmp |= PORT_PMUX_PMUXO(data);
218+
((Port *)hw)->Group[submodule_index].PMUX[index].reg = tmp;
219+
PORT_CRITICAL_SECTION_LEAVE();
220+
}
221+
222+
static inline void hri_port_write_PMUX_PMUXE_bf(const void *const hw, uint8_t submodule_index, uint8_t index,
223+
hri_port_pmux_reg_t data)
224+
{
225+
uint8_t tmp;
226+
PORT_CRITICAL_SECTION_ENTER();
227+
tmp = ((Port *)hw)->Group[submodule_index].PMUX[index].reg;
228+
tmp &= ~PORT_PMUX_PMUXE_Msk;
229+
tmp |= PORT_PMUX_PMUXE(data);
230+
((Port *)hw)->Group[submodule_index].PMUX[index].reg = tmp;
231+
PORT_CRITICAL_SECTION_LEAVE();
232+
}

0 commit comments

Comments
 (0)