Skip to content

Commit

Permalink
metro-snek: Start adding i2c support (not working yet)
Browse files Browse the repository at this point in the history
Signed-off-by: Keith Packard <keithp@keithp.com>
  • Loading branch information
keith-packard committed May 1, 2019
1 parent 6f8d898 commit 00a6c14
Show file tree
Hide file tree
Showing 7 changed files with 228 additions and 0 deletions.
1 change: 1 addition & 0 deletions metro-snek/Makefile
Expand Up @@ -19,6 +19,7 @@ SNEK_SAMD21 = $(SNEK_ROOT)/samd21
include $(SNEK_SAMD21)/snek-samd21.defs

SNEK_LOCAL_SRC = \
ao-i2c.c \
$(SNEK_SAMD21_SRC)

SNEK_LOCAL_INC = \
Expand Down
3 changes: 3 additions & 0 deletions metro-snek/ao-pins.h
Expand Up @@ -32,6 +32,9 @@
#define USE_SERIAL_0_STDIN 0
#define SERIAL_0_PA10_PA11 1

#define HAS_I2C 1
#define HAS_I2C_3 1

#define HAS_USB 1
#define AO_USB_OUT_HOOK 1
#define USE_USB_FIFO 1
Expand Down
2 changes: 2 additions & 0 deletions metro-snek/metro-snek.builtin
Expand Up @@ -31,3 +31,5 @@ A2, -2, 16
A3, -2, 17
A4, -2, 18
A5, -2, 19
i2c.send, 2
i2c.recv, 2
130 changes: 130 additions & 0 deletions samd21/ao-i2c.c
@@ -0,0 +1,130 @@
/*
* Copyright © 2019 Keith Packard <keithp@keithp.com>
*
* This program 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.
*
* This program 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, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*/

#include <ao.h>
#include <ao-i2c.h>

#define AO_I2C_CLOCK 400000
#define AO_I2C_SERCOM (&samd21_sercom3)
#define AO_I2C_SERCOM_UNIT 3
#define HAS_I2C_3 1

void
ao_i2c_start(uint8_t addr)
{
struct samd21_sercom *sercom = AO_I2C_SERCOM;

sercom->addr = ((addr << SAMD21_SERCOM_ADDR_ADDR) |
(0 << SAMD21_SERCOM_ADDR_LENEN) |
(0 << SAMD21_SERCOM_ADDR_HS) |
(0 << SAMD21_SERCOM_ADDR_TENBITEN) |
(0 << SAMD21_SERCOM_ADDR_LEN));
}

void
ao_i2c_send(void *block, uint32_t len, bool stop)
{
struct samd21_sercom *sercom = AO_I2C_SERCOM;
uint8_t *d = block;

while (len--) {
while ((sercom->intflag & (1 << SAMD21_SERCOM_INTFLAG_TXFE)) == 0)
;
sercom->data = *d++;
}
if (stop) {
sercom->ctrlb |= (SAMD21_SERCOM_CTRLB_CMD_STOP << SAMD21_SERCOM_CTRLB_CMD);
while (sercom->syncbusy & (1 << SAMD21_SERCOM_SYNCBUSY_SYSOP))
;
}
}

void
ao_i2c_recv(void *block, uint32_t len, bool stop)
{
struct samd21_sercom *sercom = AO_I2C_SERCOM;
uint8_t *d = block;

while (len--) {
while ((sercom->intflag & (1 << SAMD21_SERCOM_INTFLAG_RXFF)) == 0)
;
*d++ = sercom->data;
}
if (stop) {
sercom->ctrlb |= (SAMD21_SERCOM_CTRLB_CMD_STOP << SAMD21_SERCOM_CTRLB_CMD);
while (sercom->syncbusy & (1 << SAMD21_SERCOM_SYNCBUSY_SYSOP))
;
}
}

static void
ao_i2c_sercom_init(struct samd21_sercom *sercom, int id)
{
/* Send a clock along */
samd21_gclk_clkctrl(0, SAMD21_GCLK_CLKCTRL_ID_SERCOM0_CORE + id);

/* enable */
samd21_pm.apbcmask |= (1 << (SAMD21_PM_APBCMASK_SERCOM0 + id));

/* Reset */
sercom->ctrla = (1 << SAMD21_SERCOM_CTRLA_SWRST);

while ((sercom->ctrla & (1 << SAMD21_SERCOM_CTRLA_SWRST)) ||
(sercom->syncbusy & (1 << SAMD21_SERCOM_SYNCBUSY_SWRST)))
;

/* Configure I2C master mode */
sercom->ctrla = ((0 << SAMD21_SERCOM_CTRLA_SWRST) |
(0 << SAMD21_SERCOM_CTRLA_ENABLE) |
(SAMD21_SERCOM_CTRLA_MODE_I2C_MASTER << SAMD21_SERCOM_CTRLA_MODE) |
(1 << SAMD21_SERCOM_CTRLA_RUNSTDBY) |

(0 << SAMD21_SERCOM_CTRLA_PINOUT) |
(SAMD21_SERCOM_CTRLA_SDAHOLD_DIS << SAMD21_SERCOM_CTRLA_SDAHOLD) |
(0 << SAMD21_SERCOM_CTRLA_MEXTTOEN) |
(0 << SAMD21_SERCOM_CTRLA_SEXTTOEN) |
(SAMD21_SERCOM_CTRLA_SPEED_STANDARD << SAMD21_SERCOM_CTRLA_SPEED) |
(0 << SAMD21_SERCOM_CTRLA_SCLSM) |
(SAMD21_SERCOM_CTRLA_INACTOUT_DIS << SAMD21_SERCOM_CTRLA_INACTOUT) |
(0 << SAMD21_SERCOM_CTRLA_LOWTOUT));

sercom->ctrlb = ((1 << SAMD21_SERCOM_CTRLB_SMEN) |
(0 << SAMD21_SERCOM_CTRLB_QCEN) |
(SAMD21_SERCOM_CTRLB_CMD_NOP << SAMD21_SERCOM_CTRLB_CMD) |
(SAMD21_SERCOM_CTRLB_ACKACT_ACK << SAMD21_SERCOM_CTRLB_ACKACT) |
(3 << SAMD21_SERCOM_CTRLB_FIFOCLR));

sercom->intenclr = 0xff;

sercom->baud = AO_SYSCLK / AO_I2C_CLOCK - 1;

/* finish setup and enable the hardware */
sercom->ctrla |= (1 << SAMD21_SERCOM_CTRLA_ENABLE);
}


void
ao_i2c_init(void)
{
#if HAS_I2C_3
ao_enable_port(&samd21_port_a);
samd21_port_pmux_set(&samd21_port_a, 22, SAMD21_PORT_PMUX_FUNC_C);
samd21_port_pmux_set(&samd21_port_a, 23, SAMD21_PORT_PMUX_FUNC_C);
ao_i2c_sercom_init(AO_I2C_SERCOM, AO_I2C_SERCOM_UNIT);
#endif
}
34 changes: 34 additions & 0 deletions samd21/ao-i2c.h
@@ -0,0 +1,34 @@
/*
* Copyright © 2019 Keith Packard <keithp@keithp.com>
*
* This program 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.
*
* This program 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, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*/

#ifndef _AO_I2C_H_
#define _AO_I2C_H_

void
ao_i2c_start(uint8_t addr);

void
ao_i2c_send(void *block, uint32_t len, bool stop);

void
ao_i2c_recv(void *block, uint32_t len, bool stop);

void
ao_i2c_init(void);

#endif /* _AO_I2C_H_ */
6 changes: 6 additions & 0 deletions samd21/ao-snek.c
Expand Up @@ -19,6 +19,9 @@
#include <ao-tcc-samd21.h>
#include <ao-tc-samd21.h>
#include <ao-adc-samd21.h>
#if HAS_I2C
#include <ao-i2c.h>
#endif
#include <setjmp.h>

void
Expand Down Expand Up @@ -148,6 +151,9 @@ main(void)
ao_tc_samd21_init();
ao_adc_init();
ao_usb_init();
#if HAS_I2C
ao_i2c_init();
#endif

setjmp(snek_reset_buf);
ao_snek_port_init();
Expand Down
52 changes: 52 additions & 0 deletions samd21/snek-altos.c
Expand Up @@ -59,3 +59,55 @@ snek_builtin_random_randrange(snek_poly_t a)
random_x = (random_x >> 32) | (random_x << 32);
return snek_float_to_poly(random_x % snek_poly_get_soffset(a));
}

#if HAS_I2C
#include <ao-i2c.h>

snek_poly_t
snek_builtin_i2c_recv(snek_poly_t addr, snek_poly_t data)
{
snek_offset_t a = (snek_poly_get_soffset(addr) << 1) | 1;
snek_list_t *list;
uint8_t tmp[32];
uint8_t i;

if (snek_poly_type(data) != snek_list ||
snek_list_type(list = snek_poly_to_list(data)) != snek_list_list ||
list->size > sizeof (data))
{
return snek_error_type_1(data);
}
if (snek_abort)
return SNEK_NULL;
ao_i2c_start(a);
ao_i2c_recv(tmp, list->size, true);
snek_poly_t *d = snek_list_data(list);
for (i = 0; i < list->size; i++)
d[i] = snek_float_to_poly(tmp[i]);
return snek_float_to_poly(list->size);
}

snek_poly_t
snek_builtin_i2c_send(snek_poly_t addr, snek_poly_t data)
{
snek_offset_t a = (snek_poly_get_soffset(addr) << 1) | 0;
snek_list_t *list;
uint8_t tmp[32];
uint8_t i;

if (snek_poly_type(data) != snek_list ||
snek_list_type(list = snek_poly_to_list(data)) != snek_list_list ||
list->size > sizeof (data))
{
return snek_error_type_1(data);
}
snek_poly_t *d = snek_list_data(list);
for (i = 0; i < list->size; i++)
tmp[i] = snek_poly_get_soffset(d[i]);
if (snek_abort)
return SNEK_NULL;
ao_i2c_start(a);
ao_i2c_send(tmp, list->size, true);
return snek_float_to_poly(list->size);
}
#endif

0 comments on commit 00a6c14

Please sign in to comment.