sam3: Merge sam4e8e support into sam3 code

Most of the peripherals on the sam4e8e are similar to the ones on the
sam3x8e mcu.  Merge the code together and use just one code directory.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
Kevin O'Connor
2018-12-26 16:50:44 -05:00
parent e278552d44
commit 94c86d6c6c
25 changed files with 127 additions and 551 deletions

View File

@@ -1,12 +1,13 @@
# Kconfig settings for SAM3 processors
# Kconfig settings for SAM3/SAM4 processors
if MACH_SAM3X8E
if MACH_SAM3
config SAM_SELECT
config SAM3_SELECT
bool
default y
select HAVE_GPIO
select HAVE_GPIO_ADC
select HAVE_GPIO_I2C if MACH_SAM4E8E
select HAVE_GPIO_SPI
select HAVE_GPIO_BITBANGING
@@ -14,9 +15,23 @@ config BOARD_DIRECTORY
string
default "sam3"
choice
prompt "Processor model"
config MACH_SAM3X8E
bool "SAM3x8e (Arduino Due)"
config MACH_SAM4E8E
bool "SAM4e8e (Duet Wifi/Eth)"
endchoice
config MCU
string
default "sam3x8e" if MACH_SAM3X8E
default "sam4e8e" if MACH_SAM4E8E
config CLOCK_FREQ
int
default 42000000 # 84000000/2
default 42000000 if MACH_SAM3X8E # 84000000/2
default 60000000 if MACH_SAM4E8E # 120000000/2
config SERIAL
bool

View File

@@ -1,33 +1,42 @@
# Additional SAM3 build rules
# Additional SAM3/SAM4 build rules
# Setup the toolchain
CROSS_PREFIX=arm-none-eabi-
dirs-y += src/sam3 src/generic
dirs-y += lib/sam3x/gcc/gcc
dirs-$(CONFIG_MACH_SAM3X8E) += lib/sam3x/gcc/gcc
dirs-$(CONFIG_MACH_SAM4E8E) += lib/sam4e/gcc/gcc
CFLAGS += -mthumb -mcpu=cortex-m3 -falign-loops=16
CFLAGS += -Ilib/sam3x/include -Ilib/cmsis-core
CFLAGS += -D__SAM3X8E__
CFLAGS-$(CONFIG_MACH_SAM3X8E) += -mcpu=cortex-m3 -falign-loops=16
CFLAGS-$(CONFIG_MACH_SAM3X8E) += -Ilib/sam3x/include -D__SAM3X8E__
CFLAGS-$(CONFIG_MACH_SAM4E8E) += -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard
CFLAGS-$(CONFIG_MACH_SAM4E8E) += -Ilib/sam4e/include -D__SAM4E8E__
CFLAGS += -mthumb $(CFLAGS-y) -Ilib/cmsis-core
CFLAGS_klipper.elf += -Llib/sam3x/gcc/gcc
CFLAGS_klipper.elf += -T lib/sam3x/gcc/gcc/sam3x8e_flash.ld
CFLAGS_klipper.elf += --specs=nano.specs --specs=nosys.specs
eflags-$(CONFIG_MACH_SAM3X8E) += -Llib/sam3x/gcc/gcc
eflags-$(CONFIG_MACH_SAM3X8E) += -T lib/sam3x/gcc/gcc/sam3x8e_flash.ld
eflags-$(CONFIG_MACH_SAM4E8E) += -Llib/sam4e/gcc/gcc
eflags-$(CONFIG_MACH_SAM4E8E) += -T lib/sam4e/gcc/gcc/sam4e8e_flash.ld
CFLAGS_klipper.elf += $(eflags-y) --specs=nano.specs --specs=nosys.specs
# Add source files
src-y += sam3/main.c sam3/timer.c
src-y += sam3/gpio.c sam3/adc.c sam3/spi.c
src-y += sam3/main.c sam3/timer.c sam3/gpio.c
src-y += generic/crc16_ccitt.c generic/alloc.c
src-y += generic/armcm_irq.c generic/timer_irq.c
src-y += ../lib/sam3x/gcc/system_sam3xa.c
src-y += ../lib/sam3x/gcc/gcc/startup_sam3xa.c
src-$(CONFIG_SERIAL) += sam3/serial.c generic/serial_irq.c
src-$(CONFIG_MACH_SAM3X8E) += sam3/adc.c sam3/spi.c
src-$(CONFIG_MACH_SAM3X8E) += ../lib/sam3x/gcc/system_sam3xa.c
src-$(CONFIG_MACH_SAM3X8E) += ../lib/sam3x/gcc/gcc/startup_sam3xa.c
src-$(CONFIG_MACH_SAM4E8E) += sam3/sam4e_afec.c sam3/sam4e_spi.c
src-$(CONFIG_MACH_SAM4E8E) += sam3/i2c.c sam3/sam4_cache.c
src-$(CONFIG_MACH_SAM4E8E) += ../lib/sam4e/gcc/system_sam4e.c
src-$(CONFIG_MACH_SAM4E8E) += ../lib/sam4e/gcc/gcc/startup_sam4e.c
# Build the additional hex output file
target-y += $(OUT)klipper.bin
$(OUT)klipper.bin: $(OUT)klipper.elf
@echo " Creating hex file $@"
@echo " Creating bin file $@"
$(Q)$(OBJCOPY) -O binary $< $@
# Flash rules

View File

@@ -10,7 +10,6 @@
#include "compiler.h" // ARRAY_SIZE
#include "gpio.h" // gpio_adc_setup
#include "internal.h" // GPIO
#include "sam3x8e.h" // ADC
#include "sched.h" // sched_shutdown
static const uint8_t adc_pins[] = {
@@ -43,7 +42,7 @@ gpio_adc_setup(uint8_t pin)
| ADC_MR_STARTUP_SUT768
| ADC_MR_TRANSFER(1));
}
return (struct gpio_adc){ .bit = 1 << chan };
return (struct gpio_adc){ .chan = 1 << chan };
}
// Try to sample a value. Returns zero if sample ready, otherwise
@@ -55,11 +54,11 @@ gpio_adc_sample(struct gpio_adc g)
uint32_t chsr = ADC->ADC_CHSR & 0xffff;
if (!chsr) {
// Start sample
ADC->ADC_CHER = g.bit;
ADC->ADC_CHER = g.chan;
ADC->ADC_CR = ADC_CR_START;
goto need_delay;
}
if (chsr != g.bit)
if (chsr != g.chan)
// Sampling in progress on another channel
goto need_delay;
if (!(ADC->ADC_ISR & ADC_ISR_DRDY))
@@ -75,7 +74,7 @@ need_delay:
uint16_t
gpio_adc_read(struct gpio_adc g)
{
ADC->ADC_CHDR = g.bit;
ADC->ADC_CHDR = g.chan;
return ADC->ADC_LCDR;
}
@@ -84,7 +83,7 @@ void
gpio_adc_cancel_sample(struct gpio_adc g)
{
irqstatus_t flag = irq_save();
if ((ADC->ADC_CHSR & 0xffff) == g.bit)
if ((ADC->ADC_CHSR & 0xffff) == g.chan)
gpio_adc_read(g);
irq_restore(flag);
}

View File

@@ -1,4 +1,4 @@
// GPIO functions on sam3x8e
// GPIO functions on sam3/sam4
//
// Copyright (C) 2016-2018 Kevin O'Connor <kevin@koconnor.net>
//
@@ -9,11 +9,14 @@
#include "compiler.h" // ARRAY_SIZE
#include "gpio.h" // gpio_out_setup
#include "internal.h" // gpio_peripheral
#include "sam3x8e.h" // Pio
#include "sched.h" // sched_shutdown
static Pio * const digital_regs[] = {
#if CONFIG_MACH_SAM3X8E
PIOA, PIOB, PIOC, PIOD
#elif CONFIG_MACH_SAM4E8E
PIOA, PIOB, PIOC, PIOD, PIOE
#endif
};
@@ -24,12 +27,16 @@ static Pio * const digital_regs[] = {
void
gpio_peripheral(uint32_t gpio, char ptype, int32_t pull_up)
{
uint32_t bank = GPIO2PORT(gpio), bit = GPIO2BIT(gpio);
uint32_t bank = GPIO2PORT(gpio), bit = GPIO2BIT(gpio), pt = ptype - 'A';
Pio *regs = digital_regs[bank];
if (ptype == 'A')
regs->PIO_ABSR &= ~bit;
else
regs->PIO_ABSR |= bit;
#if CONFIG_MACH_SAM3X8E
regs->PIO_ABSR = (regs->PIO_ABSR & ~bit) | (pt & 0x01 ? bit : 0);
#else
regs->PIO_ABCDSR[0] = (regs->PIO_ABCDSR[0] & ~bit) | (pt & 0x01 ? bit : 0);
regs->PIO_ABCDSR[1] = (regs->PIO_ABCDSR[1] & ~bit) | (pt & 0x02 ? bit : 0);
#endif
if (pull_up > 0)
regs->PIO_PUER = bit;
else

View File

@@ -22,7 +22,7 @@ void gpio_in_reset(struct gpio_in g, int8_t pull_up);
uint8_t gpio_in_read(struct gpio_in g);
struct gpio_adc {
uint32_t bit;
uint32_t chan;
};
struct gpio_adc gpio_adc_setup(uint8_t pin);
uint32_t gpio_adc_sample(struct gpio_adc g);
@@ -30,6 +30,7 @@ uint16_t gpio_adc_read(struct gpio_adc g);
void gpio_adc_cancel_sample(struct gpio_adc g);
struct spi_config {
void *sspi;
uint32_t cfg;
};
struct spi_config spi_setup(uint32_t bus, uint8_t mode, uint32_t rate);
@@ -37,4 +38,14 @@ void spi_prepare(struct spi_config config);
void spi_transfer(struct spi_config config, uint8_t receive_data
, uint8_t len, uint8_t *data);
struct i2c_config {
void *twi;
uint8_t addr;
};
struct i2c_config i2c_setup(uint32_t bus, uint32_t rate, uint8_t addr);
void i2c_write(struct i2c_config config, uint8_t write_len, uint8_t *write);
void i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg
, uint8_t read_len, uint8_t *read);
#endif // gpio.h

180
src/sam3/i2c.c Normal file
View File

@@ -0,0 +1,180 @@
// SAM4 I2C Port
//
// Copyright (C) 2018 Florian Heilmann <Florian.Heilmann@gmx.net>
// Copyright (C) 2018 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include "autoconf.h" // CONFIG_CLOCK_FREQ
#include "board/misc.h" // timer_from_us
#include "command.h" // shutdown
#include "gpio.h" // i2c_setup
#include "internal.h" // gpio_peripheral
#include "sam4e.h" // TWI0
#include "sched.h" // sched_shutdown
// I2C pin definitions
#define TWI0_SCL_GPIO GPIO('A', 4)
#define TWI0_SCL_PERIPH 'A'
#define TWI0_SDA_GPIO GPIO('A', 3)
#define TWI0_SDA_PERIPH 'A'
#define TWI1_SCL_GPIO GPIO('B', 5)
#define TWI1_SCL_PERIPH 'A'
#define TWI1_SDA_GPIO GPIO('B', 4)
#define TWI1_SDA_PERIPH 'A'
void
i2c_init(Twi *p_twi, uint32_t rate)
{
uint32_t twi_id = (p_twi == TWI0) ? ID_TWI0 : ID_TWI1;
if ((PMC->PMC_PCSR0 & (1u << twi_id)) == 0) {
PMC->PMC_PCER0 = 1 << twi_id;
}
if (p_twi == TWI0) {
gpio_peripheral(TWI0_SCL_GPIO, TWI0_SCL_PERIPH, 0);
gpio_peripheral(TWI0_SDA_GPIO, TWI0_SDA_PERIPH, 0);
} else {
gpio_peripheral(TWI1_SCL_GPIO, TWI1_SCL_PERIPH, 0);
gpio_peripheral(TWI1_SDA_GPIO, TWI1_SDA_PERIPH, 0);
}
p_twi->TWI_IDR = 0xFFFFFFFF;
(void)p_twi->TWI_SR;
p_twi->TWI_CR = TWI_CR_SWRST;
(void)p_twi->TWI_RHR;
p_twi->TWI_CR = TWI_CR_MSDIS;
p_twi->TWI_CR = TWI_CR_SVDIS;
p_twi->TWI_CR = TWI_CR_MSEN;
uint32_t cldiv = 0;
uint32_t chdiv = 0;
uint32_t ckdiv = 0;
cldiv = CONFIG_CLOCK_FREQ / ((rate > 384000 ? 384000 : rate) * 2) - 4;
while((cldiv > 255) && (ckdiv < 7)) {
ckdiv++;
cldiv /= 2;
}
if (rate > 348000) {
chdiv = CONFIG_CLOCK_FREQ / ((2 * rate - 384000) * 2) - 4;
while((chdiv > 255) && (ckdiv < 7)) {
ckdiv++;
chdiv /= 2;
}
} else {
chdiv = cldiv;
}
p_twi->TWI_CWGR = TWI_CWGR_CLDIV(cldiv) | \
TWI_CWGR_CHDIV(chdiv) | \
TWI_CWGR_CKDIV(ckdiv);
}
uint32_t
addr_to_u32(uint8_t addr_len, uint8_t *addr)
{
uint32_t address = addr[0];
if (addr_len > 1) {
address <<= 8;
address |= addr[1];
}
if (addr_len > 2) {
address <<= 8;
address |= addr[2];
}
if (addr_len > 3) {
shutdown("Addresses larger than 3 bytes are not supported");
}
return address;
}
struct i2c_config
i2c_setup(uint32_t bus, uint32_t rate, uint8_t addr)
{
if ((bus > 1) | (rate > 400000))
shutdown("Invalid i2c_setup parameters!");
Twi *p_twi = (bus == 0) ? TWI0 : TWI1;
i2c_init(p_twi, rate);
return (struct i2c_config){ .twi=p_twi, .addr=addr};
}
void
i2c_write(struct i2c_config config,
uint8_t write_len, uint8_t *write)
{
Twi *p_twi = config.twi;
uint32_t status;
uint32_t bytes_to_send = write_len;
p_twi->TWI_MMR = TWI_MMR_DADR(config.addr);
for(;;) {
status = p_twi->TWI_SR;
if (status & TWI_SR_NACK)
shutdown("I2C NACK error encountered!");
if (!(status & TWI_SR_TXRDY))
continue;
if (!bytes_to_send)
break;
p_twi->TWI_THR = *write++;
bytes_to_send--;
}
p_twi->TWI_CR = TWI_CR_STOP;
while(!(p_twi->TWI_SR& TWI_SR_TXCOMP)) {
}
}
static void
i2c_wait(Twi* p_twi, uint32_t bit, uint32_t timeout)
{
for (;;) {
uint32_t flags = p_twi->TWI_SR;
if (flags & bit)
break;
if (!timer_is_before(timer_read_time(), timeout))
shutdown("I2C timeout occured");
}
}
void
i2c_read(struct i2c_config config,
uint8_t reg_len, uint8_t *reg,
uint8_t read_len, uint8_t *read)
{
Twi *p_twi = config.twi;
uint32_t status;
uint32_t bytes_to_send=read_len;
uint8_t stop = 0;
p_twi->TWI_MMR = 0;
p_twi->TWI_MMR = TWI_MMR_MREAD | TWI_MMR_DADR(config.addr) |
((reg_len << TWI_MMR_IADRSZ_Pos) &
TWI_MMR_IADRSZ_Msk);
p_twi->TWI_IADR = 0;
p_twi->TWI_IADR = addr_to_u32(reg_len, reg);
if (bytes_to_send == 1) {
p_twi->TWI_CR = TWI_CR_START | TWI_CR_STOP;
stop = 1;
} else {
p_twi->TWI_CR = TWI_CR_START;
stop = 0;
}
while (bytes_to_send > 0) {
status = p_twi->TWI_SR;
if (status & TWI_SR_NACK) {
shutdown("I2C NACK error encountered!");
}
if (bytes_to_send == 1 && !stop) {
p_twi->TWI_CR = TWI_CR_STOP;
stop = 1;
}
i2c_wait(p_twi, TWI_SR_RXRDY, timer_read_time() + timer_from_us(5000));
if (!(status & TWI_SR_RXRDY)) {
continue;
}
*read++ = p_twi->TWI_RHR;
bytes_to_send--;
}
while (!(p_twi->TWI_SR & TWI_SR_TXCOMP)) {}
(void)p_twi->TWI_SR;
}

View File

@@ -1,8 +1,15 @@
#ifndef __SAM3_INTERNAL_H
#define __SAM3_INTERNAL_H
// Local definitions for sam3 code
// Local definitions for sam3/sam4 code
#include <stdint.h> // uint32_t
#include "autoconf.h" // CONFIG_MACH_SAM3X8E
#if CONFIG_MACH_SAM3X8E
#include "sam3x8e.h"
#elif CONFIG_MACH_SAM4E8E
#include "sam4e.h"
#endif
#define GPIO(PORT, NUM) (((PORT)-'A') * 32 + (NUM))
#define GPIO2PORT(PIN) ((PIN) / 32)

View File

@@ -1,14 +1,14 @@
// Main starting point for SAM3x8e boards.
// Main starting point for SAM3/SAM4 boards
//
// Copyright (C) 2016,2017 Kevin O'Connor <kevin@koconnor.net>
// Copyright (C) 2016-2018 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include "command.h" // DECL_CONSTANT
#include "sam3x8e.h" // WDT
#include "internal.h" // WDT
#include "sched.h" // sched_main
DECL_CONSTANT(MCU, "sam3x8e");
DECL_CONSTANT(MCU, CONFIG_MCU);
/****************************************************************

16
src/sam3/sam4_cache.c Normal file
View File

@@ -0,0 +1,16 @@
// SAM4 cache enable
//
// Copyright (C) 2018 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include "sam4e.h" // CMCC
#include "sched.h" // DECL_INIT
void
sam4_cache_init(void)
{
if (!(CMCC->CMCC_SR & CMCC_SR_CSTS))
CMCC->CMCC_CTRL = CMCC_CTRL_CEN;
}
DECL_INIT(sam4_cache_init);

206
src/sam3/sam4e_afec.c Normal file
View File

@@ -0,0 +1,206 @@
// SAM4e8e Analog Front-End Converter (AFEC) support
//
// Copyright (C) 2018 Florian Heilmann <Florian.Heilmann@gmx.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include "autoconf.h" // CONFIG_CLOCK_FREQ
#include "command.h" // shutdown
#include "gpio.h" // gpio_adc_setup
#include "internal.h" // GPIO
#include "sam4e.h" // AFEC0
#include "sched.h" // sched_shutdown
static const uint8_t afec_pins[] = {
//remove first channel, since it offsets the channel number: GPIO('A', 8),
GPIO('A', 17), GPIO('A', 18), GPIO('A', 19),
GPIO('A', 20), GPIO('B', 0), GPIO('B', 1), GPIO('C', 13),
GPIO('C', 15), GPIO('C', 12), GPIO('C', 29), GPIO('C', 30),
GPIO('C', 31), GPIO('C', 26), GPIO('C', 27), GPIO('C',0),
// AFEC1
GPIO('B', 2), GPIO('B', 3), GPIO('A', 21), GPIO('A', 22),
GPIO('C', 1), GPIO('C', 2), GPIO('C', 3), GPIO('C', 4),
};
#define AFEC1_START 15 // The first 15 pins are on afec0
static inline struct gpio_adc
pin_to_gpio_adc(uint8_t pin)
{
int chan;
for (chan=0; ; chan++) {
if (chan >= ARRAY_SIZE(afec_pins))
shutdown("Not a valid ADC pin");
if (afec_pins[chan] == pin) {
break;
}
}
return (struct gpio_adc){ .chan=chan };
}
static inline Afec *
gpio_adc_to_afec(struct gpio_adc g)
{
return (g.chan >= AFEC1_START ? AFEC1 : AFEC0);
}
static inline uint32_t
gpio_adc_to_afec_chan(struct gpio_adc g)
{
return (g.chan >= AFEC1_START ? g.chan - AFEC1_START : g.chan);
}
#define ADC_FREQ_MAX 6000000UL
DECL_CONSTANT(ADC_MAX, 4095);
static int
init_afec(Afec* afec) {
// Enable PMC
if (afec == AFEC0)
PMC->PMC_PCER0 = 1 << ID_AFEC0;
else
PMC->PMC_PCER0 = 1 << ID_AFEC1;
// If busy, return busy
if ((afec->AFE_ISR & AFE_ISR_DRDY) == AFE_ISR_DRDY) {
return -1;
}
// Reset
afec->AFE_CR = AFE_CR_SWRST;
// Configure afec
afec->AFE_MR = AFE_MR_ANACH_ALLOWED | \
AFE_MR_PRESCAL (SystemCoreClock / (2 * ADC_FREQ_MAX) -1) | \
AFE_MR_SETTLING_AST3 | \
AFE_MR_TRACKTIM(2) | \
AFE_MR_TRANSFER(1) | \
AFE_MR_STARTUP_SUT64;
afec->AFE_EMR = AFE_EMR_TAG | \
AFE_EMR_RES_NO_AVERAGE | \
AFE_EMR_STM;
afec->AFE_ACR = AFE_ACR_IBCTL(1);
// Disable interrupts
afec->AFE_IDR = 0xDF00803F;
// Disable SW triggering
uint32_t mr = afec->AFE_MR;
mr &= ~(AFE_MR_TRGSEL_Msk | AFE_MR_TRGEN | AFE_MR_FREERUN_ON);
mr |= AFE_MR_TRGEN_DIS;
afec->AFE_MR = mr;
return 0;
}
void
gpio_afec_init(void) {
while(init_afec(AFEC0) != 0) {
(void)(AFEC0->AFE_LCDR & AFE_LCDR_LDATA_Msk);
}
while(init_afec(AFEC1) != 0) {
(void)(AFEC1->AFE_LCDR & AFE_LCDR_LDATA_Msk);
}
}
DECL_INIT(gpio_afec_init);
struct gpio_adc
gpio_adc_setup(uint8_t pin)
{
struct gpio_adc adc_pin = pin_to_gpio_adc(pin);
Afec *afec = gpio_adc_to_afec(adc_pin);
uint32_t afec_chan = gpio_adc_to_afec_chan(adc_pin);
//config channel
uint32_t reg = afec->AFE_DIFFR;
reg &= ~(1u << afec_chan);
afec->AFE_DIFFR = reg;
reg = afec->AFE_CGR;
reg &= ~(0x03u << (2 * afec_chan));
reg |= 1 << (2 * afec_chan);
afec->AFE_CGR = reg;
// Configure channel
// afec_ch_get_config_defaults(&ch_cfg);
// afec_ch_set_config(afec, afec_chan, &ch_cfg);
// Remove default internal offset from channel
// See Atmel Appnote AT03078 Section 1.5
afec->AFE_CSELR = afec_chan;
afec->AFE_COCR = (0x800 & AFE_COCR_AOFF_Msk);
// Enable and calibrate Channel
afec->AFE_CHER = 1 << afec_chan;
reg = afec->AFE_CHSR;
afec->AFE_CDOR = reg;
afec->AFE_CR = AFE_CR_AUTOCAL;
return adc_pin;
}
enum { AFE_DUMMY=0xff };
uint8_t active_channel = AFE_DUMMY;
// Try to sample a value. Returns zero if sample ready, otherwise
// returns the number of clock ticks the caller should wait before
// retrying this function.
uint32_t
gpio_adc_sample(struct gpio_adc g)
{
Afec *afec = gpio_adc_to_afec(g);
uint32_t afec_chan = gpio_adc_to_afec_chan(g);
if (active_channel == g.chan) {
if ((afec->AFE_ISR & AFE_ISR_DRDY)
&& (afec->AFE_ISR & (1 << afec_chan))) {
// Sample now ready
return 0;
} else {
// Busy
goto need_delay;
}
} else if (active_channel != AFE_DUMMY) {
goto need_delay;
}
afec->AFE_CHDR = 0x803F; // Disable all channels
afec->AFE_CHER = 1 << afec_chan;
active_channel = g.chan;
for (uint32_t chan = 0; chan < 16; ++chan)
{
if ((afec->AFE_ISR & (1 << chan)) != 0)
{
afec->AFE_CSELR = chan;
(void)(afec->AFE_CDR);
}
}
afec->AFE_CR = AFE_CR_START;
need_delay:
return ADC_FREQ_MAX * 10000ULL / CONFIG_CLOCK_FREQ; // about 400 mcu clock cycles or 40 afec cycles
}
// Read a value; use only after gpio_adc_sample() returns zero
uint16_t
gpio_adc_read(struct gpio_adc g)
{
Afec *afec = gpio_adc_to_afec(g);
uint32_t afec_chan = gpio_adc_to_afec_chan(g);
active_channel = AFE_DUMMY;
afec->AFE_CSELR = afec_chan;
return afec->AFE_CDR;
}
// Cancel a sample that may have been started with gpio_adc_sample()
void
gpio_adc_cancel_sample(struct gpio_adc g)
{
if (active_channel == g.chan) {
active_channel = AFE_DUMMY;
}
}

124
src/sam3/sam4e_spi.c Normal file
View File

@@ -0,0 +1,124 @@
// SAM4e8e SPI port
//
// Copyright (C) 2018 Florian Heilmann <Florian.Heilmann@gmx.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include "autoconf.h" // CONFIG_CLOCK_FREQ
#include "command.h" // shutdown
#include "gpio.h" // spi_setup
#include "internal.h" // gpio_peripheral
#include "sam4e.h" // USART0
#include "sched.h" // sched_shutdown
#define SSPI_USART0 0
#define SSPI_USART1 1
#define SSPI_SPI 2
struct spi_config
spi_setup(uint32_t bus, uint8_t mode, uint32_t rate)
{
Usart *p_usart = USART0;
if (bus > 2) {
shutdown("Invalid spi_setup parameters");
}
if (bus == SSPI_USART0) {
// DUET_USART0_SCK as per dc42 CoreNG
gpio_peripheral(GPIO('B', 13), 'C', 0);
// DUET_USART0_MOSI as per dc42 CoreNG
gpio_peripheral(GPIO('B', 1), 'C', 0);
// DUET_USART0_MISO as per dc42 CoreNG
gpio_peripheral(GPIO('B', 0), 'C', 1);
if ((PMC->PMC_PCSR0 & (1u << ID_USART0)) == 0) {
PMC->PMC_PCER0 = 1 << ID_USART0;
}
p_usart = USART0;
} else if (bus == SSPI_USART1) {
// DUET_USART1_SCK as per dc42 CoreNG
gpio_peripheral(GPIO('A', 23), 'A', 0);
// DUET_USART1_MOSI as per dc42 CoreNG
gpio_peripheral(GPIO('A', 22), 'A', 0);
// DUET_USART1_MISO as per dc42 CoreNG
gpio_peripheral(GPIO('A', 21), 'A', 1);
if ((PMC->PMC_PCSR0 & (1u << ID_USART1)) == 0) {
PMC->PMC_PCER0 = 1 << ID_USART1;
}
p_usart = USART1;
}
if (bus < 2) {
p_usart->US_MR = 0;
p_usart->US_RTOR = 0;
p_usart->US_TTGR = 0;
p_usart->US_CR = US_CR_RSTTX | US_CR_RSTRX | US_CR_TXDIS | US_CR_RXDIS;
uint32_t br = (CONFIG_CLOCK_FREQ + rate / 2) / rate;
p_usart-> US_BRGR = br << US_BRGR_CD_Pos;
uint32_t reg = US_MR_CHRL_8_BIT |
US_MR_USART_MODE_SPI_MASTER |
US_MR_CLKO |
US_MR_CHMODE_NORMAL;
switch (mode) {
case 0:
reg |= US_MR_CPHA;
reg &= ~US_MR_CPOL;
break;
case 1:
reg &= ~US_MR_CPHA;
reg &= ~US_MR_CPOL;
break;
case 2:
reg |= US_MR_CPHA;
reg |= US_MR_CPOL;
break;
case 3:
reg &= ~US_MR_CPHA;
reg |= US_MR_CPOL;
break;
}
p_usart->US_MR |= reg;
p_usart->US_CR = US_CR_RXEN | US_CR_TXEN;
return (struct spi_config){ .sspi=p_usart, .cfg=p_usart->US_MR };
}
// True SPI implementation still ToDo
return (struct spi_config){ .sspi = 0, .cfg=0};
}
void
spi_transfer(struct spi_config config, uint8_t receive_data
, uint8_t len, uint8_t *data)
{
if ((config.sspi == USART0) || (config.sspi == USART1)) {
Usart *p_usart = config.sspi;
if (receive_data) {
for (uint32_t i = 0; i < len; ++i) {
uint32_t co = (uint32_t)*data & 0x000000FF;
while(!(p_usart->US_CSR & US_CSR_TXRDY)) {}
p_usart->US_THR = US_THR_TXCHR(co);
uint32_t ci = 0;
while(!(p_usart->US_CSR & US_CSR_RXRDY)) {}
ci = p_usart->US_RHR & US_RHR_RXCHR_Msk;
*data++ = (uint8_t)ci;
}
} else {
for (uint32_t i = 0; i < len; ++i) {
uint32_t co = (uint32_t)*data & 0x000000FF;
while(!(p_usart->US_CSR & US_CSR_TXRDY)) {}
p_usart->US_THR = US_THR_TXCHR(co);
while(!(p_usart->US_CSR & US_CSR_RXRDY)) {}
(void)(p_usart->US_RHR & US_RHR_RXCHR_Msk);
(void)*data++;
}
}
}
}
void
spi_prepare(struct spi_config config) {}

View File

@@ -1,4 +1,4 @@
// sam3x8e serial port
// sam3/sam4 serial port
//
// Copyright (C) 2016-2018 Kevin O'Connor <kevin@koconnor.net>
//
@@ -7,50 +7,65 @@
#include "autoconf.h" // CONFIG_SERIAL_BAUD
#include "board/serial_irq.h" // serial_rx_data
#include "internal.h" // gpio_peripheral
#include "sam3x8e.h" // UART
#include "sched.h" // DECL_INIT
// Serial port pins
#if CONFIG_MACH_SAM3X8E
#define Serial_IRQ_Handler UART_Handler
static Uart * const Port = UART;
static const uint32_t Pmc_id = ID_UART, Irq_id = UART_IRQn;
static const uint32_t rx_pin = GPIO('A', 8);
static const uint32_t tx_pin = GPIO('A', 9);
#elif CONFIG_MACH_SAM4E8E
#define Serial_IRQ_Handler UART0_Handler
static Uart * const Port = UART0;
static const uint32_t Pmc_id = ID_UART0, Irq_id = UART0_IRQn;
static const uint32_t rx_pin = GPIO('A', 9);
static const uint32_t tx_pin = GPIO('A', 10);
#endif
void
serial_init(void)
{
gpio_peripheral(GPIO('A', 8), 'A', 1);
gpio_peripheral(GPIO('A', 9), 'A', 0);
gpio_peripheral(rx_pin, 'A', 1);
gpio_peripheral(tx_pin, 'A', 0);
// Reset uart
PMC->PMC_PCER0 = 1 << ID_UART;
UART->UART_PTCR = UART_PTCR_RXTDIS | UART_PTCR_TXTDIS;
UART->UART_CR = UART_CR_RSTRX | UART_CR_RSTTX | UART_CR_RXDIS | UART_CR_TXDIS;
UART->UART_IDR = 0xFFFFFFFF;
PMC->PMC_PCER0 = 1 << Pmc_id;
Port->UART_PTCR = UART_PTCR_RXTDIS | UART_PTCR_TXTDIS;
Port->UART_CR = (UART_CR_RSTRX | UART_CR_RSTTX
| UART_CR_RXDIS | UART_CR_TXDIS);
Port->UART_IDR = 0xFFFFFFFF;
// Enable uart
UART->UART_MR = (US_MR_CHRL_8_BIT | US_MR_NBSTOP_1_BIT | UART_MR_PAR_NO
Port->UART_MR = (US_MR_CHRL_8_BIT | US_MR_NBSTOP_1_BIT | UART_MR_PAR_NO
| UART_MR_CHMODE_NORMAL);
UART->UART_BRGR = SystemCoreClock / (16 * CONFIG_SERIAL_BAUD);
UART->UART_IER = UART_IER_RXRDY;
NVIC_EnableIRQ(UART_IRQn);
NVIC_SetPriority(UART_IRQn, 0);
UART->UART_CR = UART_CR_RXEN | UART_CR_TXEN;
Port->UART_BRGR = SystemCoreClock / (16 * CONFIG_SERIAL_BAUD);
Port->UART_IER = UART_IER_RXRDY;
NVIC_EnableIRQ(Irq_id);
NVIC_SetPriority(Irq_id, 0);
Port->UART_CR = UART_CR_RXEN | UART_CR_TXEN;
}
DECL_INIT(serial_init);
void __visible
UART_Handler(void)
Serial_IRQ_Handler(void)
{
uint32_t status = UART->UART_SR;
uint32_t status = Port->UART_SR;
if (status & UART_SR_RXRDY)
serial_rx_byte(UART->UART_RHR);
serial_rx_byte(Port->UART_RHR);
if (status & UART_SR_TXRDY) {
uint8_t data;
int ret = serial_get_tx_byte(&data);
if (ret)
UART->UART_IDR = UART_IDR_TXRDY;
Port->UART_IDR = UART_IDR_TXRDY;
else
UART->UART_THR = data;
Port->UART_THR = data;
}
}
void
serial_enable_tx_irq(void)
{
UART->UART_IER = UART_IDR_TXRDY;
Port->UART_IER = UART_IDR_TXRDY;
}

View File

@@ -1,10 +1,9 @@
// SPI transmissions on sam3x8e
// SPI transmissions on sam3
//
// Copyright (C) 2018 Petri Honkala <cruwaller@gmail.com>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include <sam3x8e.h> // REGPTR
#include "command.h" // shutdown
#include "gpio.h" // spi_setup
#include "internal.h" // gpio_peripheral

View File

@@ -1,6 +1,6 @@
// SAM3x8e timer interrupt scheduling
// SAM3/SAM4 timer interrupt scheduling
//
// Copyright (C) 2016,2017 Kevin O'Connor <kevin@koconnor.net>
// Copyright (C) 2016-2018 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
@@ -8,7 +8,7 @@
#include "board/misc.h" // timer_read_time
#include "board/timer_irq.h" // timer_dispatch_many
#include "command.h" // DECL_SHUTDOWN
#include "sam3x8e.h" // TC0
#include "internal.h" // TC0
#include "sched.h" // DECL_INIT
// Set the next irq time