sam3: Rename src/sam3x8e to src/sam3

This is in preparation for merging sam3 and sam4 code into one
directory.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
Kevin O'Connor
2018-12-26 15:41:37 -05:00
parent e70b70fb75
commit 70bbdf9334
12 changed files with 20 additions and 21 deletions

29
src/sam3/Kconfig Normal file
View File

@@ -0,0 +1,29 @@
# Kconfig settings for SAM3 processors
if MACH_SAM3X8E
config SAM_SELECT
bool
default y
select HAVE_GPIO
select HAVE_GPIO_ADC
select HAVE_GPIO_SPI
select HAVE_GPIO_BITBANGING
config BOARD_DIRECTORY
string
default "sam3"
config CLOCK_FREQ
int
default 42000000 # 84000000/2
config SERIAL
bool
default y
config SERIAL_BAUD
depends on SERIAL
int "Baud rate for serial port"
default 250000
endif

42
src/sam3/Makefile Normal file
View File

@@ -0,0 +1,42 @@
# Additional SAM3 build rules
# Setup the toolchain
CROSS_PREFIX=arm-none-eabi-
dirs-y += src/sam3 src/generic
dirs-y += lib/sam3x/gcc/gcc
CFLAGS += -mthumb -mcpu=cortex-m3 -falign-loops=16
CFLAGS += -Ilib/sam3x/include -Ilib/cmsis-core
CFLAGS += -D__SAM3X8E__
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
# Add source files
src-y += sam3/main.c sam3/timer.c
src-y += sam3/gpio.c sam3/adc.c sam3/spi.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
# Build the additional hex output file
target-y += $(OUT)klipper.bin
$(OUT)klipper.bin: $(OUT)klipper.elf
@echo " Creating hex file $@"
$(Q)$(OBJCOPY) -O binary $< $@
# Flash rules
lib/bossac/bin/bossac:
@echo " Building bossac"
$(Q)make -C lib/bossac bin/bossac
flash: $(OUT)klipper.bin lib/bossac/bin/bossac
@echo " Flashing $^ to $(FLASH_DEVICE) via bossac"
$(Q)if [ -z $(FLASH_DEVICE) ]; then echo "Please specify FLASH_DEVICE"; exit 1; fi
$(Q)lib/bossac/bin/bossac -U -p "$(FLASH_DEVICE)" -a -e -w $(OUT)klipper.bin -v -b
$(Q)lib/bossac/bin/bossac -p "$(FLASH_DEVICE)" -R > /dev/null 2>&1 || true

90
src/sam3/adc.c Normal file
View File

@@ -0,0 +1,90 @@
// Analog to digital support
//
// Copyright (C) 2016-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/irq.h" // irq_save
#include "command.h" // shutdown
#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[] = {
GPIO('A', 2), GPIO('A', 3), GPIO('A', 4), GPIO('A', 6),
GPIO('A', 22), GPIO('A', 23), GPIO('A', 24), GPIO('A', 16),
GPIO('B', 12), GPIO('B', 13), GPIO('B', 17), GPIO('B', 18),
GPIO('B', 19), GPIO('B', 20)
};
#define ADC_FREQ_MAX 20000000
DECL_CONSTANT(ADC_MAX, 4095);
struct gpio_adc
gpio_adc_setup(uint8_t pin)
{
// Find pin in adc_pins table
int chan;
for (chan=0; ; chan++) {
if (chan >= ARRAY_SIZE(adc_pins))
shutdown("Not a valid ADC pin");
if (adc_pins[chan] == pin)
break;
}
if (!(PMC->PMC_PCSR1 & (1 << (ID_ADC-32)))) {
// Setup ADC
PMC->PMC_PCER1 = 1 << (ID_ADC-32);
uint32_t prescal = SystemCoreClock / (2 * ADC_FREQ_MAX) - 1;
ADC->ADC_MR = (ADC_MR_PRESCAL(prescal)
| ADC_MR_STARTUP_SUT768
| ADC_MR_TRANSFER(1));
}
return (struct gpio_adc){ .bit = 1 << chan };
}
// 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)
{
uint32_t chsr = ADC->ADC_CHSR & 0xffff;
if (!chsr) {
// Start sample
ADC->ADC_CHER = g.bit;
ADC->ADC_CR = ADC_CR_START;
goto need_delay;
}
if (chsr != g.bit)
// Sampling in progress on another channel
goto need_delay;
if (!(ADC->ADC_ISR & ADC_ISR_DRDY))
// Conversion still in progress
goto need_delay;
// Conversion ready
return 0;
need_delay:
return ADC_FREQ_MAX * 1000ULL / CONFIG_CLOCK_FREQ;
}
// Read a value; use only after gpio_adc_sample() returns zero
uint16_t
gpio_adc_read(struct gpio_adc g)
{
ADC->ADC_CHDR = g.bit;
return ADC->ADC_LCDR;
}
// Cancel a sample that may have been started with gpio_adc_sample()
void
gpio_adc_cancel_sample(struct gpio_adc g)
{
irqstatus_t flag = irq_save();
if ((ADC->ADC_CHSR & 0xffff) == g.bit)
gpio_adc_read(g);
irq_restore(flag);
}

133
src/sam3/gpio.c Normal file
View File

@@ -0,0 +1,133 @@
// GPIO functions on sam3x8e
//
// Copyright (C) 2016-2018 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include "board/irq.h" // irq_save
#include "command.h" // shutdown
#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[] = {
PIOA, PIOB, PIOC, PIOD
};
/****************************************************************
* Pin multiplexing
****************************************************************/
void
gpio_peripheral(uint32_t gpio, char ptype, int32_t pull_up)
{
uint32_t bank = GPIO2PORT(gpio), bit = GPIO2BIT(gpio);
Pio *regs = digital_regs[bank];
if (ptype == 'A')
regs->PIO_ABSR &= ~bit;
else
regs->PIO_ABSR |= bit;
if (pull_up > 0)
regs->PIO_PUER = bit;
else
regs->PIO_PUDR = bit;
regs->PIO_PDR = bit;
}
/****************************************************************
* General Purpose Input Output (GPIO) pins
****************************************************************/
struct gpio_out
gpio_out_setup(uint8_t pin, uint8_t val)
{
if (GPIO2PORT(pin) >= ARRAY_SIZE(digital_regs))
goto fail;
Pio *regs = digital_regs[GPIO2PORT(pin)];
struct gpio_out g = { .regs=regs, .bit=GPIO2BIT(pin) };
gpio_out_reset(g, val);
return g;
fail:
shutdown("Not an output pin");
}
void
gpio_out_reset(struct gpio_out g, uint8_t val)
{
Pio *regs = g.regs;
irqstatus_t flag = irq_save();
if (val)
regs->PIO_SODR = g.bit;
else
regs->PIO_CODR = g.bit;
regs->PIO_OER = g.bit;
regs->PIO_OWER = g.bit;
regs->PIO_PER = g.bit;
regs->PIO_PUDR = g.bit;
irq_restore(flag);
}
void
gpio_out_toggle_noirq(struct gpio_out g)
{
Pio *regs = g.regs;
regs->PIO_ODSR ^= g.bit;
}
void
gpio_out_toggle(struct gpio_out g)
{
irqstatus_t flag = irq_save();
gpio_out_toggle_noirq(g);
irq_restore(flag);
}
void
gpio_out_write(struct gpio_out g, uint8_t val)
{
Pio *regs = g.regs;
if (val)
regs->PIO_SODR = g.bit;
else
regs->PIO_CODR = g.bit;
}
struct gpio_in
gpio_in_setup(uint8_t pin, int8_t pull_up)
{
if (GPIO2PORT(pin) >= ARRAY_SIZE(digital_regs))
goto fail;
uint32_t port = GPIO2PORT(pin);
PMC->PMC_PCER0 = 1 << (ID_PIOA + port);
struct gpio_in g = { .regs=digital_regs[port], .bit=GPIO2BIT(pin) };
gpio_in_reset(g, pull_up);
return g;
fail:
shutdown("Not an input pin");
}
void
gpio_in_reset(struct gpio_in g, int8_t pull_up)
{
Pio *regs = g.regs;
irqstatus_t flag = irq_save();
if (pull_up)
regs->PIO_PUER = g.bit;
else
regs->PIO_PUDR = g.bit;
regs->PIO_ODR = g.bit;
regs->PIO_PER = g.bit;
irq_restore(flag);
}
uint8_t
gpio_in_read(struct gpio_in g)
{
Pio *regs = g.regs;
return !!(regs->PIO_PDSR & g.bit);
}

40
src/sam3/gpio.h Normal file
View File

@@ -0,0 +1,40 @@
#ifndef __SAM3_GPIO_H
#define __SAM3_GPIO_H
#include <stdint.h> // uint32_t
struct gpio_out {
void *regs;
uint32_t bit;
};
struct gpio_out gpio_out_setup(uint8_t pin, uint8_t val);
void gpio_out_reset(struct gpio_out g, uint8_t val);
void gpio_out_toggle_noirq(struct gpio_out g);
void gpio_out_toggle(struct gpio_out g);
void gpio_out_write(struct gpio_out g, uint8_t val);
struct gpio_in {
void *regs;
uint32_t bit;
};
struct gpio_in gpio_in_setup(uint8_t pin, int8_t pull_up);
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;
};
struct gpio_adc gpio_adc_setup(uint8_t pin);
uint32_t gpio_adc_sample(struct gpio_adc g);
uint16_t gpio_adc_read(struct gpio_adc g);
void gpio_adc_cancel_sample(struct gpio_adc g);
struct spi_config {
uint32_t cfg;
};
struct spi_config spi_setup(uint32_t bus, uint8_t mode, uint32_t rate);
void spi_prepare(struct spi_config config);
void spi_transfer(struct spi_config config, uint8_t receive_data
, uint8_t len, uint8_t *data);
#endif // gpio.h

13
src/sam3/internal.h Normal file
View File

@@ -0,0 +1,13 @@
#ifndef __SAM3_INTERNAL_H
#define __SAM3_INTERNAL_H
// Local definitions for sam3 code
#include <stdint.h> // uint32_t
#define GPIO(PORT, NUM) (((PORT)-'A') * 32 + (NUM))
#define GPIO2PORT(PIN) ((PIN) / 32)
#define GPIO2BIT(PIN) (1<<((PIN) % 32))
void gpio_peripheral(uint32_t gpio, char ptype, int32_t pull_up);
#endif // internal.h

52
src/sam3/main.c Normal file
View File

@@ -0,0 +1,52 @@
// Main starting point for SAM3x8e boards.
//
// Copyright (C) 2016,2017 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 "sched.h" // sched_main
DECL_CONSTANT(MCU, "sam3x8e");
/****************************************************************
* watchdog handler
****************************************************************/
void
watchdog_reset(void)
{
WDT->WDT_CR = 0xA5000001;
}
DECL_TASK(watchdog_reset);
void
watchdog_init(void)
{
uint32_t timeout = 500 * 32768 / 128 / 1000; // 500ms timeout
WDT->WDT_MR = WDT_MR_WDRSTEN | WDT_MR_WDV(timeout) | WDT_MR_WDD(timeout);
}
DECL_INIT(watchdog_init);
/****************************************************************
* misc functions
****************************************************************/
void
command_reset(uint32_t *args)
{
NVIC_SystemReset();
}
DECL_COMMAND_FLAGS(command_reset, HF_IN_SHUTDOWN, "reset");
// Main entry point
int
main(void)
{
SystemInit();
sched_main();
return 0;
}

56
src/sam3/serial.c Normal file
View File

@@ -0,0 +1,56 @@
// sam3x8e serial port
//
// Copyright (C) 2016-2018 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#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
void
serial_init(void)
{
gpio_peripheral(GPIO('A', 8), 'A', 1);
gpio_peripheral(GPIO('A', 9), '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;
// Enable uart
UART->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;
}
DECL_INIT(serial_init);
void __visible
UART_Handler(void)
{
uint32_t status = UART->UART_SR;
if (status & UART_SR_RXRDY)
serial_rx_byte(UART->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;
else
UART->UART_THR = data;
}
}
void
serial_enable_tx_irq(void)
{
UART->UART_IER = UART_IDR_TXRDY;
}

129
src/sam3/spi.c Normal file
View File

@@ -0,0 +1,129 @@
// SPI transmissions on sam3x8e
//
// 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
#include "sched.h" // sched_shutdown
#define REGPTR SPI0
#define PERIPH_ID ID_SPI0
#define CHANNEL 0 // Use same channel for all
static void
spi_init(void)
{
/* Configure SCK, MISO and MOSI */
gpio_peripheral(GPIO('A', 25), 'A', 0); // Arduino 74
gpio_peripheral(GPIO('A', 26), 'A', 0); // Arduino 75
gpio_peripheral(GPIO('A', 27), 'A', 0); // Arduino 76
// Enable SPI clocks
if (!(PMC->PMC_PCSR0 & (1u << PERIPH_ID))) {
PMC->PMC_PCER0 = (1 << PERIPH_ID);
}
/* Disable SPI */
REGPTR->SPI_CR = SPI_CR_SPIDIS;
/* Execute a software reset of the SPI twice */
REGPTR->SPI_CR = SPI_CR_SWRST;
REGPTR->SPI_CR = SPI_CR_SWRST;
REGPTR->SPI_MR = ( SPI_MR_MSTR | // Set master mode
SPI_MR_MODFDIS | // Disable fault detection
SPI_MR_PCS(CHANNEL) // Fixes peripheral select
);
REGPTR->SPI_IDR = 0xffffffff; // Disable ISRs
/* Clear Chip Select Registers */
REGPTR->SPI_CSR[0] = 0;
REGPTR->SPI_CSR[1] = 0;
REGPTR->SPI_CSR[2] = 0;
REGPTR->SPI_CSR[3] = 0;
/* Set basic channel config */
REGPTR->SPI_CSR[CHANNEL] = 0;
/* Enable SPI */
REGPTR->SPI_CR = SPI_CR_SPIEN;
}
struct spi_config
spi_setup(uint32_t bus, uint8_t mode, uint32_t rate)
{
if (bus != CHANNEL)
shutdown("Invalid spi_setup parameters");
// Make sure bus is enabled
spi_init();
uint32_t config = 0;
uint32_t clockDiv;
if (rate < (CHIP_FREQ_CPU_MAX / 255)) {
clockDiv = 255;
} else if (rate >= (CHIP_FREQ_CPU_MAX / 2)) {
clockDiv = 2;
} else {
clockDiv = (CHIP_FREQ_CPU_MAX / (rate + 1)) + 1;
}
/****** Will be written to SPI_CSRx register ******/
// CSAAT : Chip Select Active After Transfer
config = SPI_CSR_CSAAT;
config |= SPI_CSR_BITS_8_BIT; // TODO: support for SPI_CSR_BITS_16_BIT
// NOTE: NCPHA is inverted, CPHA normal!!
switch(mode) {
case 0:
config |= SPI_CSR_NCPHA;
break;
case 1:
config |= 0;
break;
case 2:
config |= SPI_CSR_NCPHA;
config |= SPI_CSR_CPOL;
break;
case 3:
config |= SPI_CSR_CPOL;
break;
};
config |= ((clockDiv & 0xffu) << SPI_CSR_SCBR_Pos);
return (struct spi_config){.cfg = config};
}
void
spi_prepare(struct spi_config config)
{
REGPTR->SPI_CSR[CHANNEL] = config.cfg;
}
void
spi_transfer(struct spi_config config, uint8_t receive_data
, uint8_t len, uint8_t *data)
{
Spi* const pSpi = REGPTR;
if (receive_data) {
while (len--) {
pSpi->SPI_TDR = *data;
// wait for receive register
while (!(pSpi->SPI_SR & SPI_SR_RDRF))
;
// get data
*data++ = pSpi->SPI_RDR;
}
} else {
while (len--) {
pSpi->SPI_TDR = *data++;
// wait for receive register
while (!(pSpi->SPI_SR & SPI_SR_RDRF))
;
// read data (to clear RDRF)
pSpi->SPI_RDR;
}
}
}

65
src/sam3/timer.c Normal file
View File

@@ -0,0 +1,65 @@
// SAM3x8e timer interrupt scheduling
//
// Copyright (C) 2016,2017 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include "board/irq.h" // irq_disable
#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 "sched.h" // DECL_INIT
// Set the next irq time
static void
timer_set(uint32_t value)
{
TC0->TC_CHANNEL[0].TC_RA = value;
}
// Return the current time (in absolute clock ticks).
uint32_t
timer_read_time(void)
{
return TC0->TC_CHANNEL[0].TC_CV;
}
// Activate timer dispatch as soon as possible
void
timer_kick(void)
{
timer_set(timer_read_time() + 50);
TC0->TC_CHANNEL[0].TC_SR; // read to clear irq pending
}
void
timer_init(void)
{
TcChannel *tc = &TC0->TC_CHANNEL[0];
// Reset the timer
tc->TC_CCR = TC_CCR_CLKDIS;
tc->TC_IDR = 0xFFFFFFFF;
// Enable it
PMC->PMC_PCER0 = 1 << ID_TC0;
tc->TC_CMR = TC_CMR_WAVE | TC_CMR_WAVSEL_UP | TC_CMR_TCCLKS_TIMER_CLOCK1;
tc->TC_IER = TC_IER_CPAS;
NVIC_SetPriority(TC0_IRQn, 1);
NVIC_EnableIRQ(TC0_IRQn);
timer_kick();
tc->TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG;
}
DECL_INIT(timer_init);
// IRQ handler
void __visible __aligned(16) // aligning helps stabilize perf benchmarks
TC0_Handler(void)
{
irq_disable();
uint32_t status = TC0->TC_CHANNEL[0].TC_SR; // read to clear irq pending
if (likely(status & TC_SR_CPAS)) {
uint32_t next = timer_dispatch_many();
timer_set(next);
}
irq_enable();
}