mirror of
https://github.com/markqvist/OpenModem.git
synced 2025-08-10 23:50:32 -04:00
Working
This commit is contained in:
commit
c898b090dd
1049 changed files with 288572 additions and 0 deletions
50
bertos/cpu/cortex-m3/drv/adc_cm3.h
Normal file
50
bertos/cpu/cortex-m3/drv/adc_cm3.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief Low-level ADC module for ARM (interface).
|
||||
*
|
||||
* \author Daniele Basile <asterix@develer.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <cpu/detect.h>
|
||||
|
||||
#if CPU_CM3_LM3S
|
||||
#include "adc_lm3s.h"
|
||||
#elif CPU_CM3_STM32
|
||||
#include "adc_stm32.h"
|
||||
#elif CPU_CM3_SAM3X
|
||||
#include "adc_sam3.h"
|
||||
/*#elif Add other ARM families here */
|
||||
#else
|
||||
#error Unknown CPU
|
||||
#endif
|
199
bertos/cpu/cortex-m3/drv/adc_lm3s.c
Normal file
199
bertos/cpu/cortex-m3/drv/adc_lm3s.c
Normal file
|
@ -0,0 +1,199 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief ADC hardware-specific implementation
|
||||
*
|
||||
* This ADC module should be use both whit kernel or none.
|
||||
* If you are using a kernel, the adc drive does not wait the finish of
|
||||
* conversion but use a singal every time a required conversion are
|
||||
* ended. This signal wake up a process that return a result of
|
||||
* conversion. Otherwise, if you not use a kernl, this module wait
|
||||
* whit a loop the finishing of conversion.
|
||||
*
|
||||
*
|
||||
* \author Daniele Basile <asterix@develer.com>
|
||||
*/
|
||||
|
||||
|
||||
#include "adc_lm3s.h"
|
||||
|
||||
#include <cpu/irq.h>
|
||||
|
||||
#include "cfg/cfg_adc.h"
|
||||
#include "cfg/cfg_proc.h"
|
||||
#include "cfg/cfg_signal.h"
|
||||
|
||||
#include <cfg/macros.h>
|
||||
#include <cfg/compiler.h>
|
||||
#include <cfg/debug.h>
|
||||
|
||||
// Define log settings for cfg/log.h.
|
||||
#define LOG_LEVEL ADC_LOG_LEVEL
|
||||
#define LOG_FORMAT ADC_LOG_FORMAT
|
||||
#include <cfg/log.h>
|
||||
|
||||
#include <drv/adc.h>
|
||||
#include <drv/timer.h>
|
||||
#include <drv/clock_lm3s.h>
|
||||
|
||||
#include <io/lm3s.h>
|
||||
|
||||
/* Select witch ADC use */
|
||||
#if CPU_CM3_LM3S1968 || CPU_CM3_LM3S8962
|
||||
#define ADC_BASE ADC0_BASE
|
||||
#define SYSCTL_RCGC_R SYSCTL_RCGC0_R
|
||||
#define SYSCTL_RCGC_ADC SYSCTL_RCGC0_ADC0
|
||||
#else
|
||||
#error Unknow ADC register for select cpu core
|
||||
#endif
|
||||
|
||||
|
||||
#if CONFIG_KERN
|
||||
#include <cfg/module.h>
|
||||
|
||||
#include <kern/proc.h>
|
||||
#include <kern/signal.h>
|
||||
|
||||
#include <drv/irq_cm3.h>
|
||||
|
||||
|
||||
#if !CONFIG_KERN_SIGNALS
|
||||
#error Signals must be active to use ADC with kernel
|
||||
#endif
|
||||
|
||||
/* Signal adc convertion end */
|
||||
#define SIG_ADC_COMPLETE SIG_USER0
|
||||
|
||||
/* ADC waiting process */
|
||||
static struct Process *adc_process;
|
||||
|
||||
/**
|
||||
* ADC ISR.
|
||||
* Simply signal the adc process that convertion is complete.
|
||||
*/
|
||||
static DECLARE_ISR(adc_conversion_end_irq)
|
||||
{
|
||||
sig_post(adc_process, SIG_ADC_COMPLETE);
|
||||
|
||||
/* Clear the status bit */
|
||||
HWREG(ADC_BASE + ADC_O_ISC) |= ADC_ISC_IN3;
|
||||
|
||||
}
|
||||
|
||||
static void adc_enable_irq(void)
|
||||
{
|
||||
/* Clear all pending irq */
|
||||
HWREG(ADC_BASE + ADC_O_ISC) = 0;
|
||||
/* Register the IRQ handler */
|
||||
sysirq_setHandler(INT_ADC3, adc_conversion_end_irq);
|
||||
/* Enable IRQ */
|
||||
HWREG(ADC_BASE + ADC_O_SSCTL3) |= ADC_SSCTL3_IE0;
|
||||
HWREG(ADC_BASE + ADC_O_IM) |= ADC_IM_MASK3;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_KERN */
|
||||
|
||||
/**
|
||||
* Select mux channel \a ch.
|
||||
* Generally the stm32 cpu family allow us to program the order
|
||||
* of adc channel that we want to read.
|
||||
* In this driver implementation we put as fist channel to read the
|
||||
* select ones.
|
||||
*/
|
||||
void adc_hw_select_ch(uint8_t ch)
|
||||
{
|
||||
/* Select channel that we want read */
|
||||
HWREG(ADC_BASE + ADC_O_SSMUX3) = ch;
|
||||
/* Make single acquisition */
|
||||
HWREG(ADC_BASE + ADC_O_SSCTL3) |= ADC_SSCTL3_END0;
|
||||
/* Enable sequence S03 (single sample on select channel) */
|
||||
HWREG(ADC_BASE + ADC_O_ACTSS) |= ADC_ACTSS_ASEN3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start an ADC convertion.
|
||||
* If a kernel is present, preempt until convertion is complete, otherwise
|
||||
* a busy wait on ADC_DRDY bit is done.
|
||||
*/
|
||||
uint16_t adc_hw_read(void)
|
||||
{
|
||||
#if CONFIG_KERN
|
||||
/* Ensure ADC is not already in use by another process */
|
||||
ASSERT(adc_process == NULL);
|
||||
adc_process = proc_current();
|
||||
#endif
|
||||
|
||||
/* Start convertion */
|
||||
HWREG(ADC0_BASE + ADC_O_PSSI) |= ADC_PSSI_SS3;
|
||||
|
||||
#if CONFIG_KERN
|
||||
/* Ensure IRQs enabled. */
|
||||
IRQ_ASSERT_ENABLED();
|
||||
sig_wait(SIG_ADC_COMPLETE);
|
||||
|
||||
/* Prevent race condition in case of preemptive kernel */
|
||||
uint16_t ret = (uint16_t)HWREG(ADC_BASE + ADC_O_SSFIFO3);
|
||||
MEMORY_BARRIER;
|
||||
adc_process = NULL;
|
||||
return ret;
|
||||
#else
|
||||
/* Wait in polling until conversion is done */
|
||||
while (!(HWREG(ADC_BASE + ADC_O_SSFSTAT3) & ADC_SSFSTAT3_FULL));
|
||||
|
||||
/* Return the last converted data */
|
||||
return (uint16_t)HWREG(ADC_BASE + ADC_O_SSFIFO3);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Init ADC hardware.
|
||||
*/
|
||||
void adc_hw_init(void)
|
||||
{
|
||||
/* Enable ADC0 clock */
|
||||
SYSCTL_RCGC_R |= SYSCTL_RCGC_ADC;
|
||||
|
||||
/*
|
||||
* We wait some time because the clock is istable
|
||||
* and that could cause system hardfault
|
||||
*/
|
||||
lm3s_busyWait(10);
|
||||
|
||||
/* Disable all sequence */
|
||||
HWREG(ADC_BASE + ADC_O_ACTSS) = 0;
|
||||
/* Set trigger event to programmed (for all sequence) */
|
||||
HWREG(ADC_BASE + ADC_O_EMUX) = 0;
|
||||
|
||||
#if CONFIG_KERN
|
||||
adc_enable_irq();
|
||||
#endif
|
||||
}
|
68
bertos/cpu/cortex-m3/drv/adc_lm3s.h
Normal file
68
bertos/cpu/cortex-m3/drv/adc_lm3s.h
Normal file
|
@ -0,0 +1,68 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief ADC hardware-specific definition
|
||||
*
|
||||
* \author Daniele Basile <asterix@develer.com>
|
||||
*/
|
||||
|
||||
#ifndef DRV_ADC_LM3S_H
|
||||
#define DRV_ADC_LM3S_H
|
||||
|
||||
#include "cfg/cfg_adc.h"
|
||||
|
||||
#include <cfg/compiler.h>
|
||||
|
||||
/**
|
||||
* ADC config define.
|
||||
*/
|
||||
#if CPU_CM3_LM3S1968
|
||||
#define ADC_MUX_MAXCH 8 //Max number of channel for ADC.
|
||||
#define ADC_BITS 10 //Bit resolution for ADC converter.
|
||||
#elif CPU_CM3_LM3S8962
|
||||
#define ADC_MUX_MAXCH 4 //Max number of channel for ADC.
|
||||
#define ADC_BITS 10 //Bit resolution for ADC convert
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Init the ADC pins.
|
||||
* Implement it if necessary.
|
||||
*/
|
||||
#define ADC_INIT_PINS() \
|
||||
do { \
|
||||
} while (0)
|
||||
|
||||
void adc_hw_select_ch(uint8_t ch);
|
||||
uint16_t adc_hw_read(void);
|
||||
void adc_hw_init(void);
|
||||
|
||||
#endif /* DRV_ADC_LM3S_H */
|
157
bertos/cpu/cortex-m3/drv/adc_sam3.c
Normal file
157
bertos/cpu/cortex-m3/drv/adc_sam3.c
Normal file
|
@ -0,0 +1,157 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2011 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief ADC hardware-specific implementation
|
||||
*
|
||||
* \author Daniele Basile <asterix@develer.com>
|
||||
*/
|
||||
|
||||
|
||||
#include "adc_sam3.h"
|
||||
|
||||
#include "cfg/cfg_adc.h"
|
||||
|
||||
#include <cfg/macros.h>
|
||||
#include <cfg/compiler.h>
|
||||
|
||||
// Define log settings for cfg/log.h.
|
||||
#define LOG_LEVEL ADC_LOG_LEVEL
|
||||
#define LOG_FORMAT ADC_LOG_FORMAT
|
||||
#include <cfg/log.h>
|
||||
|
||||
#include <drv/adc.h>
|
||||
#include <drv/irq_cm3.h>
|
||||
|
||||
#include <cpu/irq.h>
|
||||
|
||||
#include <mware/event.h>
|
||||
|
||||
#include <io/cm3.h>
|
||||
|
||||
|
||||
/* We use event to signal the end of conversion */
|
||||
static Event data_ready;
|
||||
/* The last converted data */
|
||||
static uint32_t data;
|
||||
|
||||
/**
|
||||
* ADC ISR.
|
||||
*
|
||||
* The interrupt is connected to ready data, so when the
|
||||
* adc ends the conversion we generate an event and then
|
||||
* we return the converted value.
|
||||
*
|
||||
* \note to clear the Ready data bit and End of conversion
|
||||
* bit we should read the Last Converted Data register, otherwise
|
||||
* the ready data interrupt loop on this call.
|
||||
*/
|
||||
static DECLARE_ISR(adc_conversion_end_irq)
|
||||
{
|
||||
data = 0;
|
||||
if (ADC_ISR & BV(ADC_DRDY))
|
||||
{
|
||||
data = ADC_LDATA;
|
||||
event_do(&data_ready);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Select mux channel \a ch.
|
||||
*/
|
||||
void adc_hw_select_ch(uint8_t ch)
|
||||
{
|
||||
/* Disable all channels */
|
||||
ADC_CHDR = ADC_CH_MASK;
|
||||
/* Enable select channel */
|
||||
ADC_CHER = BV(ch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start an ADC convertion.
|
||||
*/
|
||||
uint16_t adc_hw_read(void)
|
||||
{
|
||||
ADC_CR = BV(ADC_START);
|
||||
event_wait(&data_ready);
|
||||
return(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Init ADC hardware.
|
||||
*/
|
||||
void adc_hw_init(void)
|
||||
{
|
||||
/* Make sure that interrupt are enabled */
|
||||
IRQ_ASSERT_ENABLED();
|
||||
|
||||
/* Initialize the dataready event */
|
||||
event_initGeneric(&data_ready);
|
||||
|
||||
/* Clock ADC peripheral */
|
||||
pmc_periphEnable(ADC_ID);
|
||||
|
||||
/* Reset adc controller */
|
||||
ADC_CR = ADC_SWRST;
|
||||
|
||||
/*
|
||||
* Set adc mode register:
|
||||
* - Disable hardware trigger and enable software trigger.
|
||||
* - Select normal mode.
|
||||
*/
|
||||
ADC_MR = 0;
|
||||
|
||||
/* Set ADC_BITS bit convertion resolution. */
|
||||
#if ADC_BITS == 12
|
||||
ADC_MR &= ~BV(ADC_LOWRES);
|
||||
#elif ADC_BITS == 10
|
||||
ADC_MR |= BV(ADC_LOWRES);
|
||||
#else
|
||||
#error No select bit resolution is supported to this CPU
|
||||
#endif
|
||||
|
||||
/* Setup ADC */
|
||||
LOG_INFO("Computed ADC_CLOCK %ld\n", ADC_CLOCK);
|
||||
ADC_MR |= ((ADC_PRESCALER << ADC_PRESCALER_SHIFT) & ADC_PRESCALER_MASK);
|
||||
LOG_INFO("prescaler[%ld]\n", ADC_PRESCALER);
|
||||
ADC_MR |= ((CONFIG_ADC_SUT << ADC_STARTUP_SHIFT) & ADC_STARTUP_MASK);
|
||||
LOG_INFO("starup[%d]\n", CONFIG_ADC_SUT);
|
||||
ADC_MR |= ((CONFIG_ADC_STTLING << ADC_SETTLING_SHIFT) & ADC_SETTLING_MASK);
|
||||
LOG_INFO("sttime[%d]\n", CONFIG_ADC_STTLING);
|
||||
ADC_MR |= ((CONFIG_ADC_TRACKTIM << ADC_TRACKTIM_SHIFT) & ADC_TRACKTIM_MASK);
|
||||
LOG_INFO("tracking[%d]\n", CONFIG_ADC_TRACKTIM);
|
||||
ADC_MR |= ((CONFIG_ADC_TRANSFER << ADC_TRANSFER_SHIFT) & ADC_TRANSFER_MASK);
|
||||
LOG_INFO("tranfer[%d]\n", CONFIG_ADC_TRANSFER);
|
||||
|
||||
/* Register and enable irq for adc. */
|
||||
sysirq_setHandler(INT_ADC, adc_conversion_end_irq);
|
||||
ADC_IER = BV(ADC_DRDY);
|
||||
}
|
64
bertos/cpu/cortex-m3/drv/adc_sam3.h
Normal file
64
bertos/cpu/cortex-m3/drv/adc_sam3.h
Normal file
|
@ -0,0 +1,64 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2008 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief ADC hardware-specific definition
|
||||
*
|
||||
* \author Daniele Basile <asterix@develer.com>
|
||||
*/
|
||||
|
||||
#ifndef DRV_ADC_SAM3_H
|
||||
#define DRV_ADC_SAM3_H
|
||||
|
||||
#include <hw/hw_cpufreq.h>
|
||||
|
||||
#include "cfg/cfg_adc.h"
|
||||
|
||||
#include <cfg/compiler.h>
|
||||
|
||||
/**
|
||||
* ADC config define.
|
||||
*/
|
||||
#define ADC_MUX_MAXCH 16 //Max number of channel for ADC.
|
||||
#define ADC_BITS 12 //Bit resolution for ADC converter.
|
||||
|
||||
/**
|
||||
* Macro for computing correct value to write into ADC
|
||||
* register.
|
||||
*/
|
||||
#define ADC_PRESCALER (DIV_ROUNDUP(CPU_FREQ, 2 * CONFIG_ADC_CLOCK) - 1)
|
||||
#define ADC_CLOCK (CPU_FREQ / ((ADC_PRESCALER + 1) * 2))
|
||||
|
||||
void adc_hw_select_ch(uint8_t ch);
|
||||
uint16_t adc_hw_read(void);
|
||||
void adc_hw_init(void);
|
||||
|
||||
#endif /* DRV_ADC_SAM3_H */
|
215
bertos/cpu/cortex-m3/drv/adc_stm32.c
Normal file
215
bertos/cpu/cortex-m3/drv/adc_stm32.c
Normal file
|
@ -0,0 +1,215 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief ADC hardware-specific implementation
|
||||
*
|
||||
* This ADC module should be use both whit kernel or none.
|
||||
* If you are using a kernel, the adc drive does not wait the finish of
|
||||
* conversion but use a singal every time a required conversion are
|
||||
* ended. This signal wake up a process that return a result of
|
||||
* conversion. Otherwise, if you not use a kernl, this module wait
|
||||
* whit a loop the finishing of conversion.
|
||||
*
|
||||
*
|
||||
* \author Daniele Basile <asterix@develer.com>
|
||||
*/
|
||||
|
||||
|
||||
#include "adc_stm32.h"
|
||||
|
||||
#include <cpu/irq.h>
|
||||
|
||||
#include "cfg/cfg_adc.h"
|
||||
#include "cfg/cfg_proc.h"
|
||||
#include "cfg/cfg_signal.h"
|
||||
|
||||
#include <cfg/macros.h>
|
||||
#include <cfg/compiler.h>
|
||||
#include <cfg/debug.h>
|
||||
|
||||
// Define log settings for cfg/log.h.
|
||||
#define LOG_LEVEL ADC_LOG_LEVEL
|
||||
#define LOG_FORMAT ADC_LOG_FORMAT
|
||||
#include <cfg/log.h>
|
||||
|
||||
#include <drv/adc.h>
|
||||
#include <drv/clock_stm32.h>
|
||||
#include <drv/gpio_stm32.h>
|
||||
|
||||
#include <io/stm32.h>
|
||||
|
||||
struct stm32_adc *adc = (struct stm32_adc *)ADC1_BASE;
|
||||
|
||||
#if CONFIG_KERN
|
||||
#include <cfg/module.h>
|
||||
|
||||
#include <kern/proc.h>
|
||||
#include <kern/signal.h>
|
||||
|
||||
#include <drv/irq_cm3.h>
|
||||
|
||||
|
||||
#if !CONFIG_KERN_SIGNALS
|
||||
#error Signals must be active to use ADC with kernel
|
||||
#endif
|
||||
|
||||
/* Signal adc convertion end */
|
||||
#define SIG_ADC_COMPLETE SIG_USER0
|
||||
|
||||
/* ADC waiting process */
|
||||
static struct Process *adc_process;
|
||||
|
||||
/**
|
||||
* ADC ISR.
|
||||
* Simply signal the adc process that convertion is complete.
|
||||
*/
|
||||
static DECLARE_ISR(adc_conversion_end_irq)
|
||||
{
|
||||
sig_post(adc_process, SIG_ADC_COMPLETE);
|
||||
|
||||
/* Clear the status bit */
|
||||
adc->SR &= ~BV(SR_EOC);
|
||||
}
|
||||
|
||||
static void adc_enable_irq(void)
|
||||
{
|
||||
/* Register the IRQ handler */
|
||||
sysirq_setHandler(ADC_IRQHANDLER, adc_conversion_end_irq);
|
||||
adc->CR1 |= BV(CR1_EOCIE);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_KERN */
|
||||
|
||||
/**
|
||||
* Select mux channel \a ch.
|
||||
* Generally the stm32 cpu family allow us to program the order
|
||||
* of adc channel that we want to read.
|
||||
* In this driver implementation we put as fist channel to read the
|
||||
* select ones.
|
||||
*/
|
||||
void adc_hw_select_ch(uint8_t ch)
|
||||
{
|
||||
/* We sample only from one channel */
|
||||
adc->SQR1 |= BV(SQR1_SQ_LEN_SHIFT);
|
||||
adc->SQR3 = (ch & SQR3_SQ_MASK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start an ADC convertion.
|
||||
* If a kernel is present, preempt until convertion is complete, otherwise
|
||||
* a busy wait on ADC_DRDY bit is done.
|
||||
*/
|
||||
uint16_t adc_hw_read(void)
|
||||
{
|
||||
#if CONFIG_KERN
|
||||
/* Ensure ADC is not already in use by another process */
|
||||
ASSERT(adc_process == NULL);
|
||||
adc_process = proc_current();
|
||||
#endif
|
||||
|
||||
/* Start convertion */
|
||||
adc->CR2 |= CR2_EXTTRIG_SWSTRT_SET;
|
||||
|
||||
#if CONFIG_KERN
|
||||
/* Ensure IRQs enabled. */
|
||||
IRQ_ASSERT_ENABLED();
|
||||
sig_wait(SIG_ADC_COMPLETE);
|
||||
|
||||
/* Prevent race condition in case of preemptive kernel */
|
||||
uint16_t ret = adc->DR;
|
||||
MEMORY_BARRIER;
|
||||
adc_process = NULL;
|
||||
return ret;
|
||||
#else
|
||||
/* Wait in polling until conversion is done */
|
||||
while (!(adc->SR & BV(SR_EOC)));
|
||||
|
||||
/* Return the last converted data */
|
||||
return (adc->DR);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Init ADC hardware.
|
||||
*/
|
||||
void adc_hw_init(void)
|
||||
{
|
||||
RCC->APB2ENR |= (RCC_APB2_GPIOA | RCC_APB2_GPIOB | RCC_APB2_GPIOC);
|
||||
RCC->APB2ENR |= RCC_APB2_ADC1;
|
||||
|
||||
/* Reset registry */
|
||||
adc->CR1 = 0;
|
||||
adc->CR2 = 0;
|
||||
adc->SQR1 = 0;
|
||||
adc->SQR2 = 0;
|
||||
adc->SQR3 = 0;
|
||||
|
||||
/* Calibrate ADC */
|
||||
adc->CR2 |= BV(CR2_RTSCAL);
|
||||
adc->CR2 |= BV(CR2_CAL);
|
||||
|
||||
/* Wait in polling until calibration is done */
|
||||
while (adc->CR2 & BV(CR2_CAL));
|
||||
|
||||
/*
|
||||
* Configure ADC
|
||||
* - Regular mode
|
||||
* - Wake up adc
|
||||
* - Wake up temperature and Vrefint
|
||||
*/
|
||||
adc->CR2 |= (BV(CR2_ADON) | ADC_EXTERNALTRIGCONV_NONE | BV(CR2_TSVREFE));
|
||||
|
||||
/* Set 17.1usec sampling time*/
|
||||
adc->SMPR1 |= ((ADC_SAMPLETIME_239CYCLES5 << SMPR1_CH17) |
|
||||
(ADC_SAMPLETIME_239CYCLES5 << SMPR1_CH16) |
|
||||
(ADC_SAMPLETIME_239CYCLES5 << SMPR1_CH15) |
|
||||
(ADC_SAMPLETIME_239CYCLES5 << SMPR1_CH14) |
|
||||
(ADC_SAMPLETIME_239CYCLES5 << SMPR1_CH13) |
|
||||
(ADC_SAMPLETIME_239CYCLES5 << SMPR1_CH12) |
|
||||
(ADC_SAMPLETIME_239CYCLES5 << SMPR1_CH11) |
|
||||
(ADC_SAMPLETIME_239CYCLES5 << SMPR1_CH10));
|
||||
|
||||
adc->SMPR2 |= ((ADC_SAMPLETIME_239CYCLES5 << SMPR2_CH9) |
|
||||
(ADC_SAMPLETIME_239CYCLES5 << SMPR2_CH8) |
|
||||
(ADC_SAMPLETIME_239CYCLES5 << SMPR2_CH7) |
|
||||
(ADC_SAMPLETIME_239CYCLES5 << SMPR2_CH6) |
|
||||
(ADC_SAMPLETIME_239CYCLES5 << SMPR2_CH5) |
|
||||
(ADC_SAMPLETIME_239CYCLES5 << SMPR2_CH4) |
|
||||
(ADC_SAMPLETIME_239CYCLES5 << SMPR2_CH3) |
|
||||
(ADC_SAMPLETIME_239CYCLES5 << SMPR2_CH2) |
|
||||
(ADC_SAMPLETIME_239CYCLES5 << SMPR2_CH1) |
|
||||
(ADC_SAMPLETIME_239CYCLES5 << SMPR2_CH0));
|
||||
|
||||
#if CONFIG_KERN
|
||||
adc_enable_irq();
|
||||
#endif
|
||||
}
|
65
bertos/cpu/cortex-m3/drv/adc_stm32.h
Normal file
65
bertos/cpu/cortex-m3/drv/adc_stm32.h
Normal file
|
@ -0,0 +1,65 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief ADC hardware-specific definition
|
||||
*
|
||||
* \author Daniele Basile <asterix@develer.com>
|
||||
*/
|
||||
|
||||
#ifndef DRV_ADC_STM32_H
|
||||
#define DRV_ADC_STM32_H
|
||||
|
||||
#include <hw/hw_cpufreq.h>
|
||||
|
||||
#include "cfg/cfg_adc.h"
|
||||
|
||||
#include <cfg/compiler.h>
|
||||
|
||||
/**
|
||||
* ADC config define.
|
||||
*/
|
||||
#define ADC_MUX_MAXCH 17 //Max number of channel for ADC.
|
||||
#define ADC_BITS 12 //Bit resolution for ADC converter.
|
||||
|
||||
/**
|
||||
* Init the ADC pins.
|
||||
* Implement it if necessary.
|
||||
*/
|
||||
#define ADC_INIT_PINS() \
|
||||
do { \
|
||||
} while (0)
|
||||
|
||||
void adc_hw_select_ch(uint8_t ch);
|
||||
uint16_t adc_hw_read(void);
|
||||
void adc_hw_init(void);
|
||||
|
||||
#endif /* DRV_ADC_STM32_H */
|
51
bertos/cpu/cortex-m3/drv/clock_cm3.h
Normal file
51
bertos/cpu/cortex-m3/drv/clock_cm3.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief Low-level Clock module for ARM Cortex-m3 (interface).
|
||||
*
|
||||
* \author Daniele Basile <asterix@develer.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <cpu/detect.h>
|
||||
|
||||
#if CPU_CM3_LM3S
|
||||
#include "clock_lm3s.h"
|
||||
#elif CPU_CM3_STM32
|
||||
#include "clock_stm32.h"
|
||||
#elif CPU_CM3_SAM3
|
||||
#include "clock_sam3.h"
|
||||
/*#elif Add other Cortex-M3 CPUs here */
|
||||
#else
|
||||
#error Unknown CPU
|
||||
#endif
|
||||
|
199
bertos/cpu/cortex-m3/drv/clock_lm3s.c
Normal file
199
bertos/cpu/cortex-m3/drv/clock_lm3s.c
Normal file
|
@ -0,0 +1,199 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief LM3S1968 Clocking driver.
|
||||
*
|
||||
* \author Andrea Righi <arighi@develer.com>
|
||||
*/
|
||||
|
||||
#include "clock_lm3s.h"
|
||||
|
||||
#include <cfg/compiler.h>
|
||||
#include <cfg/debug.h>
|
||||
|
||||
#include <io/lm3s.h>
|
||||
|
||||
|
||||
/* The PLL VCO frequency is 400 MHz */
|
||||
#define PLL_VCO 400000000UL
|
||||
|
||||
/* Extract the system clock divisor from the RCC register */
|
||||
#define RCC_TO_DIV(rcc) \
|
||||
(((rcc & SYSCTL_RCC_SYSDIV_MASK) >> \
|
||||
SYSCTL_RCC_SYSDIV_SHIFT) + 1)
|
||||
|
||||
/*
|
||||
* Very small delay: each loop takes 3 cycles.
|
||||
*/
|
||||
void NAKED lm3s_busyWait(unsigned long iterations)
|
||||
{
|
||||
register uint32_t __n asm("r0") = iterations;
|
||||
|
||||
asm volatile (
|
||||
"1: subs r0, #1\n\t"
|
||||
"bne 1b\n\t"
|
||||
"bx lr\n\t"
|
||||
: : "r"(__n) : "memory", "cc");
|
||||
}
|
||||
|
||||
INLINE unsigned long clock_get_rate(void)
|
||||
{
|
||||
reg32_t rcc = HWREG(SYSCTL_RCC);
|
||||
|
||||
return rcc & SYSCTL_RCC_USESYSDIV ?
|
||||
PLL_VCO / 2 / RCC_TO_DIV(rcc) : PLL_VCO;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to evaluate the correct SYSDIV value depending on the desired CPU
|
||||
* frequency.
|
||||
*/
|
||||
INLINE int evaluate_sysdiv(unsigned long freq)
|
||||
{
|
||||
int i;
|
||||
|
||||
/*
|
||||
* NOTE: with BYPASS=0, SYSDIV < 3 are reserved values (see LM3S1968
|
||||
* Microcontroller DATASHEET, p.78).
|
||||
*/
|
||||
for (i = 3; i < 16; i++)
|
||||
if (freq >= (PLL_VCO / 2 / (i + 1)))
|
||||
break;
|
||||
return i;
|
||||
}
|
||||
|
||||
void clock_init(void)
|
||||
{
|
||||
reg32_t rcc, rcc2;
|
||||
unsigned long clk;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* PLL may not function properly at default LDO setting.
|
||||
*
|
||||
* Description:
|
||||
*
|
||||
* In designs that enable and use the PLL module, unstable device
|
||||
* behavior may occur with the LDO set at its default of 2.5 volts or
|
||||
* below (minimum of 2.25 volts). Designs that do not use the PLL
|
||||
* module are not affected.
|
||||
*
|
||||
* Workaround: Prior to enabling the PLL module, it is recommended that
|
||||
* the default LDO voltage setting of 2.5 V be adjusted to 2.75 V using
|
||||
* the LDO Power Control (LDOPCTL) register.
|
||||
*
|
||||
* Silicon Revision Affected: A1, A2
|
||||
*
|
||||
* See also: Stellaris LM3S1968 A2 Errata documentation.
|
||||
*/
|
||||
if (REVISION_IS_A1 | REVISION_IS_A2)
|
||||
HWREG(SYSCTL_LDOPCTL) = SYSCTL_LDOPCTL_2_75V;
|
||||
|
||||
rcc = HWREG(SYSCTL_RCC);
|
||||
rcc2 = HWREG(SYSCTL_RCC2);
|
||||
|
||||
/*
|
||||
* Step #1: bypass the PLL and system clock divider by setting the
|
||||
* BYPASS bit and clearing the USESYS bit in the RCC register. This
|
||||
* configures the system to run off a “raw” clock source (using the
|
||||
* main oscillator or internal oscillator) and allows for the new PLL
|
||||
* configuration to be validated before switching the system clock to
|
||||
* the PLL.
|
||||
*/
|
||||
rcc |= SYSCTL_RCC_BYPASS;
|
||||
rcc &= ~SYSCTL_RCC_USESYSDIV;
|
||||
rcc2 |= SYSCTL_RCC2_BYPASS2;
|
||||
|
||||
/* Write back RCC/RCC2 registers */
|
||||
HWREG(SYSCTL_RCC) = rcc;
|
||||
HWREG(SYSCTL_RCC) = rcc2;
|
||||
|
||||
lm3s_busyWait(16);
|
||||
|
||||
/*
|
||||
* Step #2: select the crystal value (XTAL) and oscillator source
|
||||
* (OSCSRC), and clear the PWRDN bit in RCC/RCC2. Setting the XTAL
|
||||
* field automatically pulls valid PLL configuration data for the
|
||||
* appropriate crystal, and clearing the PWRDN bit powers and enables
|
||||
* the PLL and its output.
|
||||
*/
|
||||
|
||||
/* Enable the main oscillator first. */
|
||||
rcc &= ~(SYSCTL_RCC_IOSCDIS | SYSCTL_RCC_MOSCDIS);
|
||||
rcc |= SYSCTL_RCC_IOSCDIS;
|
||||
|
||||
/* Do not override RCC register fields */
|
||||
rcc2 &= ~SYSCTL_RCC2_USERCC2;
|
||||
|
||||
rcc &= ~(SYSCTL_RCC_XTAL_M | SYSCTL_RCC_OSCSRC_M | SYSCTL_RCC_PWRDN);
|
||||
rcc |= XTAL_FREQ | SYSCTL_RCC_OSCSRC_MAIN;
|
||||
|
||||
/* Clear the PLL lock interrupt. */
|
||||
HWREG(SYSCTL_MISC) = SYSCTL_INT_PLL_LOCK;
|
||||
|
||||
HWREG(SYSCTL_RCC) = rcc;
|
||||
HWREG(SYSCTL_RCC) = rcc2;
|
||||
|
||||
lm3s_busyWait(16);
|
||||
|
||||
/*
|
||||
* Step #3: select the desired system divider (SYSDIV) in RCC/RCC2 and
|
||||
* set the USESYS bit in RCC. The SYSDIV field determines the system
|
||||
* frequency for the microcontroller.
|
||||
*/
|
||||
rcc &= ~(SYSCTL_RCC_SYSDIV_M | SYSCTL_RCC_USESYSDIV);
|
||||
|
||||
clk = PLL_VCO / 2;
|
||||
for (i = 3; i < 16; i++)
|
||||
if (CPU_FREQ >= (clk / (i + 1)))
|
||||
break;
|
||||
rcc |= SYSCTL_RCC_USESYSDIV |
|
||||
(evaluate_sysdiv(CPU_FREQ) << SYSCTL_RCC_SYSDIV_SHIFT);
|
||||
|
||||
/*
|
||||
* Step #4: wait for the PLL to lock by polling the PLLLRIS bit in the
|
||||
* Raw Interrupt Status (RIS) register.
|
||||
*/
|
||||
for (i = 0; i < 32768; i++)
|
||||
if (HWREG(SYSCTL_RIS) & SYSCTL_INT_PLL_LOCK)
|
||||
break;
|
||||
|
||||
/*
|
||||
* Step #5: enable use of the PLL by clearing the BYPASS bit in
|
||||
* RCC/RCC2.
|
||||
*/
|
||||
rcc &= ~SYSCTL_RCC_BYPASS;
|
||||
|
||||
HWREG(SYSCTL_RCC) = rcc;
|
||||
|
||||
lm3s_busyWait(16);
|
||||
}
|
48
bertos/cpu/cortex-m3/drv/clock_lm3s.h
Normal file
48
bertos/cpu/cortex-m3/drv/clock_lm3s.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief Low-level clocking driver for LM3S1968.
|
||||
*
|
||||
* \author Andrea Righi <arighi@develer.com>
|
||||
*/
|
||||
|
||||
#ifndef DRV_LM3S1968_CLOCK_H
|
||||
#define DRV_LM3S1968_CLOCK_H
|
||||
|
||||
/* Crystal frequency attached to the main oscillator. */
|
||||
#define XTAL_FREQ SYSCTL_RCC_XTAL_8MHZ
|
||||
|
||||
extern void lm3s_busyWait(unsigned long iterations);
|
||||
|
||||
void clock_init(void);
|
||||
|
||||
#endif /* DRV_LM3S1968_CLOCK_H */
|
146
bertos/cpu/cortex-m3/drv/clock_sam3.c
Normal file
146
bertos/cpu/cortex-m3/drv/clock_sam3.c
Normal file
|
@ -0,0 +1,146 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief Atmel SAM3 clock setup.
|
||||
*
|
||||
* \author Stefano Fedrigo <aleph@develer.com>
|
||||
*/
|
||||
|
||||
#include "clock_sam3.h"
|
||||
#include <cfg/compiler.h>
|
||||
#include <cfg/macros.h>
|
||||
#include <io/sam3.h>
|
||||
|
||||
|
||||
/* Frequency of board main oscillator */
|
||||
#define BOARDOSC_FREQ 12000000
|
||||
|
||||
/* Timer countdown timeout for clock initialization operations */
|
||||
#define CLOCK_TIMEOUT 0xFFFFFFFF
|
||||
|
||||
|
||||
#if CPU_FREQ == 84000000 || CPU_FREQ == 48000000
|
||||
|
||||
INLINE uint32_t evaluate_pll(void)
|
||||
{
|
||||
return CKGR_PLLR_MUL(CPU_FREQ / BOARDOSC_FREQ * 2 - 1) | CKGR_PLLR_DIV(2);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#warning CPU clock frequency non-standard setting: multiplier and divider values \
|
||||
will be computed at runtime: effective computed frequency could be different \
|
||||
from expected.
|
||||
|
||||
/*
|
||||
* Try to evaluate the correct divider and multiplier value depending
|
||||
* on the desired CPU frequency.
|
||||
*
|
||||
* We try all combinations in a certain range of divider and multiplier
|
||||
* values. Start with higher multipliers and divisors, generally better.
|
||||
*/
|
||||
INLINE uint32_t evaluate_pll(void)
|
||||
{
|
||||
int mul, div, best_mul, best_div;
|
||||
int best_delta = CPU_FREQ;
|
||||
int freq = 0;
|
||||
|
||||
for (mul = 13; mul > 0; mul--)
|
||||
{
|
||||
for (div = 24; div > 0; div--)
|
||||
{
|
||||
freq = BOARDOSC_FREQ / div * (1 + mul);
|
||||
if (ABS((int)CPU_FREQ - freq) < best_delta) {
|
||||
best_delta = ABS((int)CPU_FREQ - freq);
|
||||
best_mul = mul;
|
||||
best_div = div;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return CKGR_PLLR_DIV(best_div) | CKGR_PLLR_MUL(best_mul);
|
||||
}
|
||||
|
||||
#endif /* CPU_FREQ */
|
||||
|
||||
void clock_init(void)
|
||||
{
|
||||
uint32_t timeout;
|
||||
|
||||
/* Disable watchdog */
|
||||
WDT_MR = BV(WDT_WDDIS);
|
||||
|
||||
/* Set wait states for flash access, needed for higher CPU clock rates */
|
||||
EEFC0_FMR = EEFC_FMR_FWS(3);
|
||||
#ifdef EEFC1_FMR
|
||||
EEFC1_FMR = EEFC_FMR_FWS(3);
|
||||
#endif
|
||||
|
||||
// Initialize main oscillator
|
||||
if (!(CKGR_MOR & BV(CKGR_MOR_MOSCSEL)))
|
||||
{
|
||||
CKGR_MOR = CKGR_MOR_KEY(0x37) | CKGR_MOR_MOSCXTST(0x8)
|
||||
| BV(CKGR_MOR_MOSCRCEN) | BV(CKGR_MOR_MOSCXTEN);
|
||||
timeout = CLOCK_TIMEOUT;
|
||||
while (!(PMC_SR & BV(PMC_SR_MOSCXTS)) && --timeout);
|
||||
}
|
||||
|
||||
// Switch to external oscillator
|
||||
CKGR_MOR = CKGR_MOR_KEY(0x37) | CKGR_MOR_MOSCXTST(0x8)
|
||||
| BV(CKGR_MOR_MOSCRCEN) | BV(CKGR_MOR_MOSCXTEN) | BV(CKGR_MOR_MOSCSEL);
|
||||
timeout = CLOCK_TIMEOUT;
|
||||
while (!(PMC_SR & BV(PMC_SR_MOSCXTS)) && --timeout);
|
||||
|
||||
// Initialize and enable PLL clock
|
||||
CKGR_PLLR = evaluate_pll() | BV(CKGR_PLLR_STUCKTO1) | CKGR_PLLR_PLLCOUNT(0x2);
|
||||
timeout = CLOCK_TIMEOUT;
|
||||
while (!(PMC_SR & BV(PMC_SR_LOCK)) && --timeout);
|
||||
|
||||
PMC_MCKR = PMC_MCKR_CSS_MAIN_CLK;
|
||||
timeout = CLOCK_TIMEOUT;
|
||||
while (!(PMC_SR & BV(PMC_SR_MCKRDY)) && --timeout);
|
||||
|
||||
PMC_MCKR = PMC_MCKR_CSS_PLL_CLK;
|
||||
timeout = CLOCK_TIMEOUT;
|
||||
while (!(PMC_SR & BV(PMC_SR_MCKRDY)) && --timeout);
|
||||
|
||||
/* Enable clock on PIO for inputs */
|
||||
// TODO: move this in gpio_init() for better power management?
|
||||
pmc_periphEnable(PIOA_ID);
|
||||
pmc_periphEnable(PIOB_ID);
|
||||
pmc_periphEnable(PIOC_ID);
|
||||
#ifdef PIOF_ID
|
||||
pmc_periphEnable(PIOD_ID);
|
||||
pmc_periphEnable(PIOE_ID);
|
||||
pmc_periphEnable(PIOF_ID);
|
||||
#endif
|
||||
}
|
43
bertos/cpu/cortex-m3/drv/clock_sam3.h
Normal file
43
bertos/cpu/cortex-m3/drv/clock_sam3.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief Low-level clocking driver for SAM3.
|
||||
*
|
||||
* \author Stefano Fedrigo <aleph@develer.com>
|
||||
*/
|
||||
|
||||
#ifndef DRV_SAM3_CLOCK_H
|
||||
#define DRV_SAM3_CLOCK_H
|
||||
|
||||
void clock_init(void);
|
||||
|
||||
#endif /* DRV_SAM3_CLOCK_H */
|
151
bertos/cpu/cortex-m3/drv/clock_stm32.c
Normal file
151
bertos/cpu/cortex-m3/drv/clock_stm32.c
Normal file
|
@ -0,0 +1,151 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief STM32 Clocking driver.
|
||||
*
|
||||
* \author Andrea Righi <arighi@develer.com>
|
||||
*/
|
||||
|
||||
#include "clock_stm32.h"
|
||||
|
||||
#include <cfg/compiler.h>
|
||||
#include <cfg/debug.h>
|
||||
|
||||
#include <io/stm32.h>
|
||||
|
||||
struct RCC *RCC;
|
||||
|
||||
INLINE int rcc_get_flag_status(uint32_t flag)
|
||||
{
|
||||
uint32_t id;
|
||||
reg32_t reg;
|
||||
|
||||
/* Get the RCC register index */
|
||||
id = flag >> 5;
|
||||
/* The flag to check is in CR register */
|
||||
if (id == 1)
|
||||
reg = RCC->CR;
|
||||
/* The flag to check is in BDCR register */
|
||||
else if (id == 2)
|
||||
reg = RCC->BDCR;
|
||||
/* The flag to check is in CSR register */
|
||||
else
|
||||
reg = RCC->CSR;
|
||||
/* Get the flag position */
|
||||
id = flag & FLAG_MASK;
|
||||
|
||||
return reg & (1 << id);
|
||||
}
|
||||
|
||||
INLINE uint16_t pll_clock(void)
|
||||
{
|
||||
unsigned int div, mul;
|
||||
|
||||
/* Hopefully this is evaluate at compile time... */
|
||||
for (div = 2; div; div--)
|
||||
for (mul = 2; mul <= 16; mul++)
|
||||
if (CPU_FREQ <= (PLL_VCO / div * mul))
|
||||
break;
|
||||
return mul << 8 | div;
|
||||
}
|
||||
|
||||
INLINE void rcc_pll_config(void)
|
||||
{
|
||||
reg32_t reg = RCC->CFGR & CFGR_PLL_MASK;
|
||||
|
||||
/* Evaluate clock parameters */
|
||||
uint16_t clock = pll_clock();
|
||||
uint32_t pll_mul = ((clock >> 8) - 2) << 18;
|
||||
uint32_t pll_div = ((clock & 0xff) << 1 | 1) << 16;
|
||||
|
||||
/* Set the PLL configuration bits */
|
||||
reg |= pll_div | pll_mul;
|
||||
|
||||
/* Store the new value */
|
||||
RCC->CFGR = reg;
|
||||
|
||||
/* Enable PLL */
|
||||
*CR_PLLON_BB = 1;
|
||||
}
|
||||
|
||||
INLINE void rcc_set_clock_source(uint32_t source)
|
||||
{
|
||||
reg32_t reg;
|
||||
|
||||
reg = RCC->CFGR & CFGR_SW_MASK;
|
||||
reg |= source;
|
||||
RCC->CFGR = reg;
|
||||
}
|
||||
|
||||
void clock_init(void)
|
||||
{
|
||||
/* Initialize global RCC structure */
|
||||
RCC = (struct RCC *)RCC_BASE;
|
||||
|
||||
/* Enable the internal oscillator */
|
||||
*CR_HSION_BB = 1;
|
||||
while (!rcc_get_flag_status(RCC_FLAG_HSIRDY));
|
||||
|
||||
/* Clock the system from internal HSI RC (8 MHz) */
|
||||
rcc_set_clock_source(RCC_SYSCLK_HSI);
|
||||
|
||||
/* Enable external oscillator */
|
||||
RCC->CR &= CR_HSEON_RESET;
|
||||
RCC->CR &= CR_HSEBYP_RESET;
|
||||
RCC->CR |= CR_HSEON_SET;
|
||||
while (!rcc_get_flag_status(RCC_FLAG_HSERDY));
|
||||
|
||||
/* Initialize PLL according to CPU_FREQ */
|
||||
rcc_pll_config();
|
||||
while(!rcc_get_flag_status(RCC_FLAG_PLLRDY));
|
||||
|
||||
/* Configure USB clock (48MHz) */
|
||||
*CFGR_USBPRE_BB = RCC_USBCLK_PLLCLK_1DIV5;
|
||||
/* Configure ADC clock: PCLK2 (9MHz) */
|
||||
RCC->CFGR &= CFGR_ADCPRE_RESET_MASK;
|
||||
RCC->CFGR |= RCC_PCLK2_DIV8;
|
||||
/* Configure system clock dividers: PCLK2 (72MHz) */
|
||||
RCC->CFGR &= CFGR_PPRE2_RESET_MASK;
|
||||
RCC->CFGR |= RCC_HCLK_DIV1 << 3;
|
||||
/* Configure system clock dividers: PCLK1 (36MHz) */
|
||||
RCC->CFGR &= CFGR_PPRE1_RESET_MASK;
|
||||
RCC->CFGR |= RCC_HCLK_DIV2;
|
||||
/* Configure system clock dividers: HCLK */
|
||||
RCC->CFGR &= CFGR_HPRE_RESET_MASK;
|
||||
RCC->CFGR |= RCC_SYSCLK_DIV1;
|
||||
|
||||
/* Set 1 wait state for the flash memory */
|
||||
*(reg32_t *)FLASH_BASE = 0x12;
|
||||
|
||||
/* Clock the system from the PLL */
|
||||
rcc_set_clock_source(RCC_SYSCLK_PLLCLK);
|
||||
}
|
276
bertos/cpu/cortex-m3/drv/clock_stm32.h
Normal file
276
bertos/cpu/cortex-m3/drv/clock_stm32.h
Normal file
|
@ -0,0 +1,276 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief Low-level clocking driver for Cortex-M3 STM32.
|
||||
*
|
||||
* \author Andrea Righi <arighi@develer.com>
|
||||
*/
|
||||
|
||||
#ifndef CLOCK_STM32_H
|
||||
#define CLOCK_STM32_H
|
||||
|
||||
|
||||
#include <cfg/compiler.h>
|
||||
|
||||
/* RCC registers bit address */
|
||||
#define RCC_OFFSET (RCC_BASE - PERIPH_BASE)
|
||||
|
||||
/**
|
||||
* CR Register
|
||||
*/
|
||||
/*\{*/
|
||||
/* Alias word address of HSION bit */
|
||||
#define CR_OFFSET (RCC_OFFSET + 0x00)
|
||||
#define HSION_BITNUMBER 0x00
|
||||
#define CR_HSION_BB ((reg32_t *)(PERIPH_BB_BASE + (CR_OFFSET * 32) + (HSION_BITNUMBER * 4)))
|
||||
|
||||
/* Alias word address of PLLON bit */
|
||||
#define PLLON_BITNUMBER 0x18
|
||||
#define CR_PLLON_BB ((reg32_t *)(PERIPH_BB_BASE + (CR_OFFSET * 32) + (PLLON_BITNUMBER * 4)))
|
||||
|
||||
/* Alias word address of CSSON bit */
|
||||
#define CSSON_BITNUMBER 0x13
|
||||
#define CR_CSSON_BB ((reg32_t *)(PERIPH_BB_BASE + (CR_OFFSET * 32) + (CSSON_BITNUMBER * 4)))
|
||||
/*\}*/
|
||||
|
||||
/**
|
||||
* CFGR Register
|
||||
*/
|
||||
/*\{*/
|
||||
/* Alias word address of USBPRE bit */
|
||||
#define CFGR_OFFSET (RCC_OFFSET + 0x04)
|
||||
#define USBPRE_BITNUMBER 0x16
|
||||
#define CFGR_USBPRE_BB ((reg32_t *)(PERIPH_BB_BASE + (CFGR_OFFSET * 32) + (USBPRE_BITNUMBER * 4)))
|
||||
/*\}*/
|
||||
|
||||
/**
|
||||
* BDCR Register
|
||||
*/
|
||||
/*\{*/
|
||||
/* Alias word address of RTCEN bit */
|
||||
#define BDCR_OFFSET (RCC_OFFSET + 0x20)
|
||||
#define RTCEN_BITNUMBER 0x0F
|
||||
#define BDCR_RTCEN_BB ((reg32_t *)(PERIPH_BB_BASE + (BDCR_OFFSET * 32) + (RTCEN_BITNUMBER * 4)))
|
||||
|
||||
/* Alias word address of BDRST bit */
|
||||
#define BDRST_BITNUMBER 0x10
|
||||
#define BDCR_BDRST_BB ((reg32_t *)(PERIPH_BB_BASE + (BDCR_OFFSET * 32) + (BDRST_BITNUMBER * 4)))
|
||||
/*\}*/
|
||||
|
||||
/**
|
||||
* CSR Register
|
||||
*/
|
||||
/*\{*/
|
||||
/* Alias word address of LSION bit */
|
||||
#define CSR_OFFSET (RCC_OFFSET + 0x24)
|
||||
#define LSION_BITNUMBER 0x00
|
||||
#define CSR_LSION_BB ((reg32_t *)(PERIPH_BB_BASE + (CSR_OFFSET * 32) + (LSION_BITNUMBER * 4)))
|
||||
/*\}*/
|
||||
|
||||
/**
|
||||
* RCC registers bit mask
|
||||
*/
|
||||
/*\{*/
|
||||
/* CR register bit mask */
|
||||
#define CR_HSEBYP_RESET (0xFFFBFFFF)
|
||||
#define CR_HSEBYP_SET (0x00040000)
|
||||
#define CR_HSEON_RESET (0xFFFEFFFF)
|
||||
#define CR_HSEON_SET (0x00010000)
|
||||
#define CR_HSITRIM_MASK (0xFFFFFF07)
|
||||
|
||||
/* CFGR register bit mask */
|
||||
#define CFGR_PLL_MASK (0xFFC0FFFF)
|
||||
#define CFGR_PLLMull_MASK (0x003C0000)
|
||||
#define CFGR_PLLSRC_MASK (0x00010000)
|
||||
#define CFGR_PLLXTPRE_MASK (0x00020000)
|
||||
#define CFGR_SWS_MASK (0x0000000C)
|
||||
#define CFGR_SW_MASK (0xFFFFFFFC)
|
||||
#define CFGR_HPRE_RESET_MASK (0xFFFFFF0F)
|
||||
#define CFGR_HPRE_SET_MASK (0x000000F0)
|
||||
#define CFGR_PPRE1_RESET_MASK (0xFFFFF8FF)
|
||||
#define CFGR_PPRE1_SET_MASK (0x00000700)
|
||||
#define CFGR_PPRE2_RESET_MASK (0xFFFFC7FF)
|
||||
#define CFGR_PPRE2_SET_MASK (0x00003800)
|
||||
#define CFGR_ADCPRE_RESET_MASK (0xFFFF3FFF)
|
||||
#define CFGR_ADCPRE_SET_MASK (0x0000C000)
|
||||
|
||||
/* CSR register bit mask */
|
||||
#define CSR_RVMF_SET (0x01000000)
|
||||
|
||||
/* RCC Flag MASK */
|
||||
#define FLAG_MASK (0x1F)
|
||||
|
||||
/* Typical VALUE of the HSI in Hz */
|
||||
#define HSI_VALUE (8000000)
|
||||
|
||||
/* BDCR register base address */
|
||||
#define BDCR_BASE (PERIPH_BASE + BDCR_OFFSET)
|
||||
|
||||
/* RCC Flag */
|
||||
#define RCC_FLAG_HSIRDY (0x20)
|
||||
#define RCC_FLAG_HSERDY (0x31)
|
||||
#define RCC_FLAG_PLLRDY (0x39)
|
||||
#define RCC_FLAG_LSERDY (0x41)
|
||||
#define RCC_FLAG_LSIRDY (0x61)
|
||||
#define RCC_FLAG_PINRST (0x7A)
|
||||
#define RCC_FLAG_PORRST (0x7B)
|
||||
#define RCC_FLAG_SFTRST (0x7C)
|
||||
#define RCC_FLAG_IWDGRST (0x7D)
|
||||
#define RCC_FLAG_WWDGRST (0x7E)
|
||||
#define RCC_FLAG_LPWRRST (0x7F)
|
||||
|
||||
/* System clock source */
|
||||
#define RCC_SYSCLK_HSI (0x00000000)
|
||||
#define RCC_SYSCLK_HSE (0x00000001)
|
||||
#define RCC_SYSCLK_PLLCLK (0x00000002)
|
||||
|
||||
/* PLL entry clock source */
|
||||
#define RCC_PLL_HSI_DIV2 (0x00000000)
|
||||
#define RCC_PLL_HSE_DIV1 (0x00010000)
|
||||
#define RCC_PLL_HSE_DIV2 (0x00030000)
|
||||
|
||||
/* PLL multiplication factor */
|
||||
#define RCC_PLLMUL_2 (0x00000000)
|
||||
#define RCC_PLLMUL_3 (0x00040000)
|
||||
#define RCC_PLLMUL_4 (0x00080000)
|
||||
#define RCC_PLLMUL_5 (0x000C0000)
|
||||
#define RCC_PLLMUL_6 (0x00100000)
|
||||
#define RCC_PLLMUL_7 (0x00140000)
|
||||
#define RCC_PLLMUL_8 (0x00180000)
|
||||
#define RCC_PLLMUL_9 (0x001C0000)
|
||||
#define RCC_PLLMUL_10 (0x00200000)
|
||||
#define RCC_PLLMUL_11 (0x00240000)
|
||||
#define RCC_PLLMUL_12 (0x00280000)
|
||||
#define RCC_PLLMUL_13 (0x002C0000)
|
||||
#define RCC_PLLMUL_14 (0x00300000)
|
||||
#define RCC_PLLMUL_15 (0x00340000)
|
||||
#define RCC_PLLMUL_16 (0x00380000)
|
||||
|
||||
/* APB1/APB2 clock source */
|
||||
#define RCC_HCLK_DIV1 (0x00000000)
|
||||
#define RCC_HCLK_DIV2 (0x00000400)
|
||||
#define RCC_HCLK_DIV4 (0x00000500)
|
||||
#define RCC_HCLK_DIV8 (0x00000600)
|
||||
#define RCC_HCLK_DIV16 (0x00000700)
|
||||
|
||||
/* USB clock source */
|
||||
#define RCC_USBCLK_PLLCLK_1DIV5 (0x00)
|
||||
#define RCC_USBCLK_PLLCLK_DIV1 (0x01)
|
||||
|
||||
/* ADC clock source */
|
||||
#define RCC_PCLK2_DIV2 (0x00000000)
|
||||
#define RCC_PCLK2_DIV4 (0x00004000)
|
||||
#define RCC_PCLK2_DIV6 (0x00008000)
|
||||
#define RCC_PCLK2_DIV8 (0x0000C000)
|
||||
|
||||
/* AHB clock source */
|
||||
#define RCC_SYSCLK_DIV1 (0x00000000)
|
||||
#define RCC_SYSCLK_DIV2 (0x00000080)
|
||||
#define RCC_SYSCLK_DIV4 (0x00000090)
|
||||
#define RCC_SYSCLK_DIV8 (0x000000A0)
|
||||
#define RCC_SYSCLK_DIV16 (0x000000B0)
|
||||
#define RCC_SYSCLK_DIV64 (0x000000C0)
|
||||
#define RCC_SYSCLK_DIV128 (0x000000D0)
|
||||
#define RCC_SYSCLK_DIV256 (0x000000E0)
|
||||
#define RCC_SYSCLK_DIV512 (0x000000F0)
|
||||
/*\}*/
|
||||
|
||||
/**
|
||||
* RCC register: APB1 peripheral
|
||||
*/
|
||||
/*\{*/
|
||||
#define RCC_APB1_TIM2 (0x00000001)
|
||||
#define RCC_APB1_TIM3 (0x00000002)
|
||||
#define RCC_APB1_TIM4 (0x00000004)
|
||||
#define RCC_APB1_WWDG (0x00000800)
|
||||
#define RCC_APB1_SPI2 (0x00004000)
|
||||
#define RCC_APB1_USART2 (0x00020000)
|
||||
#define RCC_APB1_USART3 (0x00040000)
|
||||
#define RCC_APB1_I2C1 (0x00200000)
|
||||
#define RCC_APB1_I2C2 (0x00400000)
|
||||
#define RCC_APB1_USB (0x00800000)
|
||||
#define RCC_APB1_CAN (0x02000000)
|
||||
#define RCC_APB1_BKP (0x08000000)
|
||||
#define RCC_APB1_PWR (0x10000000)
|
||||
#define RCC_APB1_ALL (0x1AE64807)
|
||||
/*\}*/
|
||||
|
||||
/**
|
||||
* RCC register: APB2 peripheral
|
||||
*/
|
||||
/*\{*/
|
||||
#define RCC_APB2_AFIO (0x00000001)
|
||||
#define RCC_APB2_GPIOA (0x00000004)
|
||||
#define RCC_APB2_GPIOB (0x00000008)
|
||||
#define RCC_APB2_GPIOC (0x00000010)
|
||||
#define RCC_APB2_GPIOD (0x00000020)
|
||||
#define RCC_APB2_GPIOE (0x00000040)
|
||||
#define RCC_APB2_ADC1 (0x00000200)
|
||||
#define RCC_APB2_ADC2 (0x00000400)
|
||||
#define RCC_APB2_TIM1 (0x00000800)
|
||||
#define RCC_APB2_SPI1 (0x00001000)
|
||||
#define RCC_APB2_USART1 (0x00004000)
|
||||
#define RCC_APB2_ALL (0x00005E7D)
|
||||
|
||||
/**
|
||||
* RCC register: BCDR
|
||||
*/
|
||||
#define RCC_BDCR_LSEON (0x00000001)
|
||||
#define RCC_BDCR_LSERDY (0x00000002)
|
||||
#define RCC_BDCR_RTCSEL (0x00000300)
|
||||
#define RCC_BDCR_RTCEN (0x00008000)
|
||||
/*\}*/
|
||||
|
||||
/* Crystal frequency of the main oscillator (8MHz) */
|
||||
#define PLL_VCO 8000000
|
||||
|
||||
/* Reset and Clock Controller registers */
|
||||
struct RCC
|
||||
{
|
||||
reg32_t CR;
|
||||
reg32_t CFGR;
|
||||
reg32_t CIR;
|
||||
reg32_t APB2RSTR;
|
||||
reg32_t APB1RSTR;
|
||||
reg32_t AHBENR;
|
||||
reg32_t APB2ENR;
|
||||
reg32_t APB1ENR;
|
||||
reg32_t BDCR;
|
||||
reg32_t CSR;
|
||||
};
|
||||
|
||||
/* RCC registers base */
|
||||
extern struct RCC *RCC;
|
||||
|
||||
void clock_init(void);
|
||||
|
||||
#endif /* CLOCK_STM32_h */
|
46
bertos/cpu/cortex-m3/drv/dac_cm3.h
Normal file
46
bertos/cpu/cortex-m3/drv/dac_cm3.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2011 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief Low-level DAC module for Cortex-m3.
|
||||
*
|
||||
* \author Daniele Basile <asterix@develer.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <cpu/detect.h>
|
||||
|
||||
#if CPU_CM3_SAM3X
|
||||
#include "adc_sam3.h"
|
||||
/*#elif Add other ARM families here */
|
||||
#else
|
||||
#error Unknown CPU
|
||||
#endif
|
334
bertos/cpu/cortex-m3/drv/dac_sam3.c
Normal file
334
bertos/cpu/cortex-m3/drv/dac_sam3.c
Normal file
|
@ -0,0 +1,334 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2011 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief DAC hardware-specific implementation
|
||||
*
|
||||
* \author Daniele Basile <asterix@develer.com>
|
||||
*/
|
||||
|
||||
#include "dac_sam3.h"
|
||||
|
||||
#include "cfg/cfg_dac.h"
|
||||
|
||||
#include <cfg/macros.h>
|
||||
#include <cfg/compiler.h>
|
||||
|
||||
// Define log settings for cfg/log.h.
|
||||
#define LOG_LEVEL DAC_LOG_LEVEL
|
||||
#define LOG_FORMAT DAC_LOG_FORMAT
|
||||
#include <cfg/log.h>
|
||||
|
||||
#include <drv/dac.h>
|
||||
#include <cpu/irq.h>
|
||||
#include <drv/irq_cm3.h>
|
||||
|
||||
#include <cpu/types.h>
|
||||
|
||||
#include <mware/event.h>
|
||||
|
||||
#include <io/cm3.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
struct DacHardware
|
||||
{
|
||||
uint16_t channels;
|
||||
uint32_t rate;
|
||||
bool end;
|
||||
};
|
||||
|
||||
struct DacHardware dac_hw;
|
||||
|
||||
|
||||
/* We use event to signal the end of conversion */
|
||||
static Event buff_emtpy;
|
||||
|
||||
#if CONFIG_DAC_TIMER == DACC_TRGSEL_TIO_CH0 /* Select Timer counter TIO Channel 0 */
|
||||
#define DAC_TC_ID TC0_ID
|
||||
#define DAC_TC_CCR TC0_CCR0
|
||||
#define DAC_TC_IDR TC0_IDR0
|
||||
#define DAC_TC_CMR TC0_CMR0
|
||||
#define DAC_TC_SR TC0_SR0
|
||||
#define DAC_TC_RA TC0_RA0
|
||||
#define DAC_TC_RC TC0_RC0
|
||||
#elif CONFIG_DAC_TIMER == DACC_TRGSEL_TIO_CH1 /* Select Timer counter TIO Channel 1 */
|
||||
#define DAC_TC_ID TC1_ID
|
||||
#define DAC_TC_CCR TC0_CCR1
|
||||
#define DAC_TC_IDR TC0_IDR1
|
||||
#define DAC_TC_CMR TC0_CMR1
|
||||
#define DAC_TC_SR TC0_SR1
|
||||
#define DAC_TC_RA TC0_RA1
|
||||
#define DAC_TC_RC TC0_RC1
|
||||
#elif CONFIG_DAC_TIMER == DACC_TRGSEL_TIO_CH2 /* Select Timer counter TIO Channel 2 */
|
||||
#define DAC_TC_ID TC2_ID
|
||||
#define DAC_TC_CCR TC0_CCR2
|
||||
#define DAC_TC_IDR TC0_IDR2
|
||||
#define DAC_TC_CMR TC0_CMR2
|
||||
#define DAC_TC_SR TC0_SR2
|
||||
#define DAC_TC_RA TC0_RA2
|
||||
#define DAC_TC_RC TC0_RC2
|
||||
#elif CONFIG_DAC_TIMER == DACC_TRGSEL_PWM0 || CONFIG_DAC_TIMER == DACC_TRGSEL_PWM1
|
||||
#error unimplemented pwm triger select.
|
||||
#endif
|
||||
|
||||
|
||||
INLINE void tc_init(void)
|
||||
{
|
||||
pmc_periphEnable(DAC_TC_ID);
|
||||
|
||||
/* Disable TC clock */
|
||||
DAC_TC_CCR = BV(TC_CCR_CLKDIS);
|
||||
/* Disable interrupts */
|
||||
DAC_TC_IDR = 0xFFFFFFFF;
|
||||
/* Clear status register */
|
||||
volatile uint32_t dummy = DAC_TC_SR;
|
||||
(void)dummy;
|
||||
|
||||
/*
|
||||
* Setup the timer counter:
|
||||
* - select clock TCLK1 (MCK/2)
|
||||
* - enable wave form mode
|
||||
* - RA compare effect SET
|
||||
* - RC compare effect CLEAR
|
||||
* - UP mode with automatic trigger on RC Compare
|
||||
*/
|
||||
DAC_TC_CMR = TC_TIMER_CLOCK1 | BV(TC_CMR_WAVE) | TC_CMR_ACPA_SET | TC_CMR_ACPC_CLEAR | BV(TC_CMR_CPCTRG);
|
||||
|
||||
|
||||
/* Setup the pio: TODO: fix for more generic */
|
||||
PIOB_PDR = BV(25);
|
||||
PIO_PERIPH_SEL(PIOB_BASE, BV(25), PIO_PERIPH_B);
|
||||
}
|
||||
|
||||
INLINE void tc_setup(uint32_t freq, size_t n_sample)
|
||||
{
|
||||
/*
|
||||
* Compute the sample frequency
|
||||
* the RC counter will update every MCK/2 (see above)
|
||||
* so to convert one sample at the user freq we generate
|
||||
* the trigger every TC_CLK / (numer_of_sample * user_freq)
|
||||
* where TC_CLK = MCK / 2.
|
||||
*/
|
||||
uint32_t rc = DIV_ROUND((CPU_FREQ / 2), n_sample * freq);
|
||||
DAC_TC_RC = rc;
|
||||
/* generate the square wave with duty = 50% */
|
||||
DAC_TC_RA = DIV_ROUND(50 * rc, 100);
|
||||
}
|
||||
|
||||
INLINE void tc_start(void)
|
||||
{
|
||||
DAC_TC_CCR = BV(TC_CCR_CLKEN)| BV(TC_CCR_SWTRG);
|
||||
}
|
||||
|
||||
INLINE void tc_stop(void)
|
||||
{
|
||||
DAC_TC_CCR = BV(TC_CCR_CLKDIS);
|
||||
}
|
||||
|
||||
static int sam3x_dac_write(struct Dac *dac, unsigned channel, uint16_t sample)
|
||||
{
|
||||
(void)dac;
|
||||
|
||||
ASSERT(channel <= DAC_MAXCH);
|
||||
|
||||
DACC_MR |= (channel << DACC_USER_SEL_SHIFT) & DACC_USER_SEL_MASK;
|
||||
DACC_CHER |= BV(channel);
|
||||
|
||||
DACC_CDR = sample ;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sam3x_dac_setCh(struct Dac *dac, uint32_t mask)
|
||||
{
|
||||
/* we have only the ch0 and ch1 */
|
||||
ASSERT(mask < BV(3));
|
||||
dac->hw->channels = mask;
|
||||
|
||||
if (mask & BV(DACC_CH0))
|
||||
DACC_MR |= (DACC_CH0 << DACC_USER_SEL_SHIFT) & DACC_USER_SEL_MASK;
|
||||
|
||||
if (mask & BV(DACC_CH1))
|
||||
DACC_MR |= (DACC_CH1 << DACC_USER_SEL_SHIFT) & DACC_USER_SEL_MASK;
|
||||
|
||||
DACC_CHER |= mask;
|
||||
|
||||
}
|
||||
|
||||
static void sam3x_dac_setSampleRate(struct Dac *dac, uint32_t rate)
|
||||
{
|
||||
/* Eneble hw trigger */
|
||||
DACC_MR |= BV(DACC_TRGEN) | (CONFIG_DAC_TIMER << DACC_TRGSEL_SHIFT);
|
||||
dac->hw->rate = rate;
|
||||
}
|
||||
|
||||
static void sam3x_dac_conversion(struct Dac *dac, void *buf, size_t len)
|
||||
{
|
||||
/* setup timer and start it */
|
||||
tc_setup(dac->hw->rate, len);
|
||||
tc_start();
|
||||
|
||||
/* Setup dma and start it */
|
||||
DACC_TPR = (uint32_t)buf;
|
||||
DACC_TCR = len;
|
||||
DACC_PTCR |= BV(DACC_PTCR_TXTEN);
|
||||
}
|
||||
|
||||
static uint16_t *sample_buff;
|
||||
static size_t next_idx = 0;
|
||||
static size_t chunk_size = 0;
|
||||
static size_t remaing_size = 0;
|
||||
|
||||
static DECLARE_ISR(irq_dac)
|
||||
{
|
||||
if (DACC_ISR & BV(DACC_ENDTX))
|
||||
{
|
||||
if (remaing_size > 0)
|
||||
{
|
||||
DACC_TNPR = (uint32_t)&sample_buff[next_idx];
|
||||
DACC_TNCR = chunk_size;
|
||||
|
||||
remaing_size -= chunk_size;
|
||||
next_idx += chunk_size;
|
||||
}
|
||||
else
|
||||
/* Clear the pending irq when the dma ends the conversion */
|
||||
DACC_TCR = 1;
|
||||
}
|
||||
event_do(&buff_emtpy);
|
||||
}
|
||||
|
||||
|
||||
static bool sam3x_dac_isFinished(struct Dac *dac)
|
||||
{
|
||||
return dac->hw->end;
|
||||
}
|
||||
|
||||
static void sam3x_dac_start(struct Dac *dac, void *_buf, size_t len, size_t slice_len)
|
||||
{
|
||||
ASSERT(dac);
|
||||
ASSERT(len >= slice_len);
|
||||
|
||||
/* Reset the previous status. */
|
||||
dac->hw->end = false;
|
||||
|
||||
sample_buff = (uint16_t *)_buf;
|
||||
next_idx = 0;
|
||||
chunk_size = slice_len;
|
||||
remaing_size = len;
|
||||
|
||||
|
||||
/* Program the dma with the first and second chunk of samples and update counter */
|
||||
dac->ctx.callback(dac, &sample_buff[0], chunk_size);
|
||||
DACC_TPR = (uint32_t)&sample_buff[0];
|
||||
DACC_TCR = chunk_size;
|
||||
remaing_size -= chunk_size;
|
||||
next_idx += chunk_size;
|
||||
|
||||
if (chunk_size <= remaing_size)
|
||||
{
|
||||
dac->ctx.callback(dac, &sample_buff[next_idx], chunk_size);
|
||||
|
||||
DACC_TNPR = (uint32_t)&sample_buff[next_idx];
|
||||
DACC_TNCR = chunk_size;
|
||||
|
||||
remaing_size -= chunk_size;
|
||||
next_idx += chunk_size;
|
||||
|
||||
}
|
||||
|
||||
DACC_PTCR |= BV(DACC_PTCR_TXTEN);
|
||||
DACC_IER = BV(DACC_ENDTX);
|
||||
|
||||
/* Set up timer and trig the conversions */
|
||||
tc_setup(dac->hw->rate, len);
|
||||
tc_start();
|
||||
|
||||
while (1)
|
||||
{
|
||||
event_wait(&buff_emtpy);
|
||||
if (remaing_size <= 0)
|
||||
{
|
||||
DAC_TC_CCR = BV(TC_CCR_CLKDIS);
|
||||
dac->hw->end = true;
|
||||
next_idx = 0;
|
||||
chunk_size = 0;
|
||||
remaing_size = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
dac->ctx.callback(dac, &sample_buff[next_idx], chunk_size);
|
||||
}
|
||||
}
|
||||
|
||||
static void sam3x_dac_stop(struct Dac *dac)
|
||||
{
|
||||
dac->hw->end = false;
|
||||
/* Disable the irq, timer and channel */
|
||||
DACC_IDR = BV(DACC_ENDTX);
|
||||
DACC_PTCR |= BV(DACC_PTCR_TXTDIS);
|
||||
DAC_TC_CCR = BV(TC_CCR_CLKDIS);
|
||||
}
|
||||
|
||||
|
||||
void dac_init(struct Dac *dac)
|
||||
{
|
||||
/* Initialize the dataready event */
|
||||
event_initGeneric(&buff_emtpy);
|
||||
|
||||
/* Fill the virtual table */
|
||||
dac->ctx.write = sam3x_dac_write;
|
||||
dac->ctx.setCh = sam3x_dac_setCh;
|
||||
dac->ctx.setSampleRate = sam3x_dac_setSampleRate;
|
||||
dac->ctx.conversion = sam3x_dac_conversion;
|
||||
dac->ctx.isFinished = sam3x_dac_isFinished;
|
||||
dac->ctx.start = sam3x_dac_start;
|
||||
dac->ctx.stop = sam3x_dac_stop;
|
||||
DB(dac->ctx._type = DAC_SAM3X;)
|
||||
dac->hw = &dac_hw;
|
||||
|
||||
/* Clock DAC peripheral */
|
||||
pmc_periphEnable(DACC_ID);
|
||||
|
||||
/* Reset hw */
|
||||
DACC_CR |= BV(DACC_SWRST);
|
||||
DACC_MR = 0;
|
||||
|
||||
/* Configure the dac */
|
||||
DACC_MR |= (CONFIG_DAC_REFRESH << DACC_REFRESH_SHIFT) & DACC_REFRESH_MASK;
|
||||
DACC_MR |= (CONFIG_DAC_STARTUP << DACC_STARTUP_SHIFT) & DACC_STARTUP_MASK;
|
||||
|
||||
DACC_IDR = 0xFFFFFFFF;
|
||||
sysirq_setHandler(INT_DACC, irq_dac);
|
||||
|
||||
tc_init();
|
||||
}
|
51
bertos/cpu/cortex-m3/drv/dac_sam3.h
Normal file
51
bertos/cpu/cortex-m3/drv/dac_sam3.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2011 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief DAC hardware-specific definition
|
||||
*
|
||||
* \author Daniele Basile <asterix@develer.com>
|
||||
*/
|
||||
|
||||
#ifndef DRV_DAC_SAM3_H
|
||||
#define DRV_DAC_SAM3_H
|
||||
|
||||
#include <drv/dac.h>
|
||||
|
||||
/**
|
||||
* DAC config define.
|
||||
*/
|
||||
#define DAC_MAXCH 2 //Max number of channel for ADC.
|
||||
#define DAC_BITS 12 //Bit resolution for ADC converter.
|
||||
|
||||
#define DAC_SAM3X MAKE_ID('S', 'D', '3', 'X')
|
||||
|
||||
#endif /* DRV_DAC_SAM3_H */
|
533
bertos/cpu/cortex-m3/drv/eth_sam3.c
Normal file
533
bertos/cpu/cortex-m3/drv/eth_sam3.c
Normal file
|
@ -0,0 +1,533 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010,2011 Develer S.r.l. (http://www.develer.com/)
|
||||
* All Rights Reserved.
|
||||
* -->
|
||||
*
|
||||
* \brief EMAC driver for AT91SAM family with Davicom 9161A phy.
|
||||
*
|
||||
* \author Daniele Basile <asterix@develer.com>
|
||||
* \author Andrea Righi <arighi@develer.com>
|
||||
* \author Stefano Fedrigo <aleph@develer.com>
|
||||
*/
|
||||
|
||||
#include "cfg/cfg_eth.h"
|
||||
|
||||
#define LOG_LEVEL ETH_LOG_LEVEL
|
||||
#define LOG_FORMAT ETH_LOG_FORMAT
|
||||
|
||||
#include <cfg/log.h>
|
||||
|
||||
#include <cfg/debug.h>
|
||||
#include <cfg/log.h>
|
||||
#include <cfg/macros.h>
|
||||
#include <cfg/compiler.h>
|
||||
|
||||
// TODO: unify includes
|
||||
//#include <io/at91sam7.h>
|
||||
//#include <io/arm.h>
|
||||
//#include <io/include.h>
|
||||
#include <io/sam3.h>
|
||||
#include <drv/irq_cm3.h>
|
||||
|
||||
#include <cpu/power.h>
|
||||
#include <cpu/types.h>
|
||||
#include <cpu/irq.h>
|
||||
|
||||
#include <drv/timer.h>
|
||||
#include <drv/eth.h>
|
||||
|
||||
#include <mware/event.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "eth_sam3.h"
|
||||
|
||||
#define EMAC_RX_INTS (BV(EMAC_RCOMP) | BV(EMAC_ROVR) | BV(EMAC_RXUBR))
|
||||
#define EMAC_TX_INTS (BV(EMAC_TCOMP) | BV(EMAC_TXUBR) | BV(EMAC_RLEX))
|
||||
|
||||
/* Silent Doxygen bug... */
|
||||
#ifndef __doxygen__
|
||||
/*
|
||||
* NOTE: this buffer should be declared as 'volatile' because it is read by the
|
||||
* hardware. However, this is accessed only via memcpy() that should guarantee
|
||||
* coherency when copying from/to buffers.
|
||||
*/
|
||||
static uint8_t tx_buf[EMAC_TX_BUFFERS * EMAC_TX_BUFSIZ] ALIGNED(8);
|
||||
static volatile BufDescriptor tx_buf_tab[EMAC_TX_DESCRIPTORS] ALIGNED(8);
|
||||
|
||||
/*
|
||||
* NOTE: this buffer should be declared as 'volatile' because it is wrote by
|
||||
* the hardware. However, this is accessed only via memcpy() that should
|
||||
* guarantee coherency when copying from/to buffers.
|
||||
*/
|
||||
static uint8_t rx_buf[EMAC_RX_BUFFERS * EMAC_RX_BUFSIZ] ALIGNED(8);
|
||||
static volatile BufDescriptor rx_buf_tab[EMAC_RX_DESCRIPTORS] ALIGNED(8);
|
||||
#endif
|
||||
|
||||
static int tx_buf_idx;
|
||||
static int tx_buf_offset;
|
||||
static int rx_buf_idx;
|
||||
|
||||
static Event recv_wait, send_wait;
|
||||
|
||||
static DECLARE_ISR(emac_irqHandler)
|
||||
{
|
||||
/* Read interrupt status and disable interrupts. */
|
||||
uint32_t isr = EMAC_ISR;
|
||||
|
||||
/* Receiver interrupt */
|
||||
if ((isr & EMAC_RX_INTS))
|
||||
{
|
||||
if (isr & BV(EMAC_RCOMP))
|
||||
event_do(&recv_wait);
|
||||
EMAC_RSR = EMAC_RX_INTS;
|
||||
}
|
||||
/* Transmitter interrupt */
|
||||
if (isr & EMAC_TX_INTS)
|
||||
{
|
||||
if (isr & BV(EMAC_TCOMP))
|
||||
event_do(&send_wait);
|
||||
EMAC_TSR = EMAC_TX_INTS;
|
||||
}
|
||||
//AIC_EOICR = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* \brief Read contents of PHY register.
|
||||
*
|
||||
* \param reg PHY register number.
|
||||
*
|
||||
* \return Contents of the specified register.
|
||||
*/
|
||||
static uint16_t phy_hw_read(uint8_t phy_addr, reg8_t reg)
|
||||
{
|
||||
// PHY read command.
|
||||
EMAC_MAN = EMAC_SOF | EMAC_RW_READ
|
||||
| ((phy_addr << EMAC_PHYA_SHIFT) & EMAC_PHYA)
|
||||
| ((reg << EMAC_REGA_SHIFT) & EMAC_REGA)
|
||||
| EMAC_CODE;
|
||||
|
||||
// Wait until PHY logic completed.
|
||||
while (!(EMAC_NSR & BV(EMAC_IDLE)))
|
||||
cpu_relax();
|
||||
|
||||
// Get data from PHY maintenance register.
|
||||
return (uint16_t)(EMAC_MAN & EMAC_DATA);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* \brief Write value to PHY register.
|
||||
*
|
||||
* \param reg PHY register number.
|
||||
* \param val Value to write.
|
||||
*/
|
||||
static void phy_hw_write(uint8_t phy_addr, reg8_t reg, uint16_t val)
|
||||
{
|
||||
// PHY write command.
|
||||
EMAC_MAN = EMAC_SOF | EMAC_RW_WRITE
|
||||
| ((phy_addr << EMAC_PHYA_SHIFT) & EMAC_PHYA)
|
||||
| ((reg << EMAC_REGA_SHIFT) & EMAC_REGA)
|
||||
| EMAC_CODE | val;
|
||||
|
||||
// Wait until PHY logic completed.
|
||||
while (!(EMAC_NSR & BV(EMAC_IDLE)))
|
||||
cpu_relax();
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Check link speed and duplex as negotiated by the PHY
|
||||
* and configure CPU EMAC accordingly.
|
||||
* Requires active PHY maintenance mode.
|
||||
*/
|
||||
static void emac_autoNegotiation(void)
|
||||
{
|
||||
uint16_t reg;
|
||||
time_t start;
|
||||
|
||||
// Wait for auto-negotation to complete
|
||||
start = timer_clock();
|
||||
do {
|
||||
reg = phy_hw_read(NIC_PHY_ADDR, NIC_PHY_BMSR);
|
||||
if (timer_clock() - start > 2000)
|
||||
{
|
||||
kprintf("eth error: auto-negotiation timeout\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
while (!(reg & NIC_PHY_BMSR_ANCOMPL));
|
||||
|
||||
reg = phy_hw_read(NIC_PHY_ADDR, NIC_PHY_ANLPAR);
|
||||
|
||||
if ((reg & NIC_PHY_ANLPAR_TX_FDX) || (reg & NIC_PHY_ANLPAR_TX_HDX))
|
||||
{
|
||||
LOG_INFO("eth: 100BASE-TX\n");
|
||||
EMAC_NCFGR |= BV(EMAC_SPD);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_INFO("eth: 10BASE-T\n");
|
||||
EMAC_NCFGR &= ~BV(EMAC_SPD);
|
||||
}
|
||||
|
||||
if ((reg & NIC_PHY_ANLPAR_TX_FDX) || (reg & NIC_PHY_ANLPAR_10_FDX))
|
||||
{
|
||||
LOG_INFO("eth: full duplex\n");
|
||||
EMAC_NCFGR |= BV(EMAC_FD);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_INFO("eth: half duplex\n");
|
||||
EMAC_NCFGR &= ~BV(EMAC_FD);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int emac_reset(void)
|
||||
{
|
||||
#if CPU_ARM_AT91
|
||||
// Enable devices
|
||||
PMC_PCER = BV(PIOA_ID);
|
||||
PMC_PCER = BV(PIOB_ID);
|
||||
PMC_PCER = BV(EMAC_ID);
|
||||
|
||||
// Disable TESTMODE and RMII
|
||||
PIOB_PUDR = BV(PHY_RXDV_TESTMODE_BIT);
|
||||
PIOB_PUDR = BV(PHY_COL_RMII_BIT);
|
||||
|
||||
// Disable PHY power down.
|
||||
PIOB_PER = BV(PHY_PWRDN_BIT);
|
||||
PIOB_OER = BV(PHY_PWRDN_BIT);
|
||||
PIOB_CODR = BV(PHY_PWRDN_BIT);
|
||||
#else
|
||||
pmc_periphEnable(PIOA_ID);
|
||||
pmc_periphEnable(PIOB_ID);
|
||||
pmc_periphEnable(PIOC_ID);
|
||||
pmc_periphEnable(PIOD_ID);
|
||||
pmc_periphEnable(EMAC_ID);
|
||||
|
||||
// Disable TESTMODE
|
||||
PIOB_PUDR = BV(PHY_RXDV_TESTMODE_BIT);
|
||||
#endif
|
||||
|
||||
// Toggle external hardware reset pin.
|
||||
RSTC_MR = RSTC_KEY | (1 << RSTC_ERSTL_SHIFT) | BV(RSTC_URSTEN);
|
||||
RSTC_CR = RSTC_KEY | BV(RSTC_EXTRST);
|
||||
|
||||
while ((RSTC_SR & BV(RSTC_NRSTL)) == 0)
|
||||
cpu_relax();
|
||||
|
||||
// Configure MII ports.
|
||||
#if CPU_ARM_AT91
|
||||
PIOB_ASR = PHY_MII_PINS;
|
||||
PIOB_BSR = 0;
|
||||
PIOB_PDR = PHY_MII_PINS;
|
||||
|
||||
// Enable receive and transmit clocks.
|
||||
EMAC_USRIO = BV(EMAC_CLKEN);
|
||||
#else
|
||||
PIO_PERIPH_SEL(PIOB_BASE, PHY_MII_PINS_PORTB, PIO_PERIPH_A);
|
||||
PIOB_PDR = PHY_MII_PINS_PORTB;
|
||||
|
||||
// Enable receive, transmit clocks and RMII mode.
|
||||
EMAC_USRIO = BV(EMAC_CLKEN) | BV(EMAC_RMII);
|
||||
#endif
|
||||
|
||||
// Enable management port.
|
||||
EMAC_NCR |= BV(EMAC_MPE);
|
||||
EMAC_NCFGR |= EMAC_CLK_HCLK_64;
|
||||
|
||||
// Set local MAC address.
|
||||
EMAC_SA1L = (mac_addr[3] << 24) | (mac_addr[2] << 16) |
|
||||
(mac_addr[1] << 8) | mac_addr[0];
|
||||
EMAC_SA1H = (mac_addr[5] << 8) | mac_addr[4];
|
||||
|
||||
emac_autoNegotiation();
|
||||
|
||||
// Disable management port.
|
||||
EMAC_NCR &= ~BV(EMAC_MPE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int emac_start(void)
|
||||
{
|
||||
uint32_t addr;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < EMAC_RX_DESCRIPTORS; i++)
|
||||
{
|
||||
addr = (uint32_t)(rx_buf + (i * EMAC_RX_BUFSIZ));
|
||||
rx_buf_tab[i].addr = addr & BUF_ADDRMASK;
|
||||
}
|
||||
rx_buf_tab[EMAC_RX_DESCRIPTORS - 1].addr |= RXBUF_WRAP;
|
||||
|
||||
for (i = 0; i < EMAC_TX_DESCRIPTORS; i++)
|
||||
{
|
||||
addr = (uint32_t)(tx_buf + (i * EMAC_TX_BUFSIZ));
|
||||
tx_buf_tab[i].addr = addr & BUF_ADDRMASK;
|
||||
tx_buf_tab[i].stat = TXS_USED;
|
||||
}
|
||||
tx_buf_tab[EMAC_TX_DESCRIPTORS - 1].stat = TXS_USED | TXS_WRAP;
|
||||
|
||||
/* Tell the EMAC where to find the descriptors. */
|
||||
EMAC_RBQP = (uint32_t)rx_buf_tab;
|
||||
EMAC_TBQP = (uint32_t)tx_buf_tab;
|
||||
|
||||
/* Clear receiver status. */
|
||||
EMAC_RSR = BV(EMAC_OVR) | BV(EMAC_REC) | BV(EMAC_BNA);
|
||||
|
||||
/* Copy all frames and discard FCS. */
|
||||
EMAC_NCFGR |= BV(EMAC_CAF) | BV(EMAC_DRFCS);
|
||||
|
||||
/* Enable receiver, transmitter and statistics. */
|
||||
EMAC_NCR |= BV(EMAC_TE) | BV(EMAC_RE) | BV(EMAC_WESTAT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t eth_putFrame(const uint8_t *buf, size_t len)
|
||||
{
|
||||
size_t wr_len;
|
||||
|
||||
if (UNLIKELY(!len))
|
||||
return -1;
|
||||
ASSERT(len <= sizeof(tx_buf));
|
||||
|
||||
/* Check if the transmit buffer is available */
|
||||
while (!(tx_buf_tab[tx_buf_idx].stat & TXS_USED))
|
||||
event_wait(&send_wait);
|
||||
|
||||
/* Copy the data into the buffer and prepare descriptor */
|
||||
wr_len = MIN(len, (size_t)EMAC_TX_BUFSIZ - tx_buf_offset);
|
||||
memcpy((uint8_t *)tx_buf_tab[tx_buf_idx].addr + tx_buf_offset,
|
||||
buf, wr_len);
|
||||
tx_buf_offset += wr_len;
|
||||
|
||||
return wr_len;
|
||||
}
|
||||
|
||||
void eth_sendFrame(void)
|
||||
{
|
||||
tx_buf_tab[tx_buf_idx].stat = (tx_buf_offset & TXS_LENGTH_FRAME) |
|
||||
TXS_LAST_BUFF |
|
||||
((tx_buf_idx == EMAC_TX_DESCRIPTORS - 1) ? TXS_WRAP : 0);
|
||||
EMAC_NCR |= BV(EMAC_TSTART);
|
||||
|
||||
tx_buf_offset = 0;
|
||||
if (++tx_buf_idx >= EMAC_TX_DESCRIPTORS)
|
||||
tx_buf_idx = 0;
|
||||
}
|
||||
|
||||
ssize_t eth_send(const uint8_t *buf, size_t len)
|
||||
{
|
||||
if (UNLIKELY(!len))
|
||||
return -1;
|
||||
|
||||
len = eth_putFrame(buf, len);
|
||||
eth_sendFrame();
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static void eth_buf_realign(int idx)
|
||||
{
|
||||
/* Empty buffer found. Realign. */
|
||||
do {
|
||||
rx_buf_tab[rx_buf_idx].addr &= ~RXBUF_OWNERSHIP;
|
||||
if (++rx_buf_idx >= EMAC_RX_BUFFERS)
|
||||
rx_buf_idx = 0;
|
||||
} while (idx != rx_buf_idx);
|
||||
}
|
||||
|
||||
static size_t __eth_getFrameLen(void)
|
||||
{
|
||||
int idx, n = EMAC_RX_BUFFERS;
|
||||
|
||||
skip:
|
||||
/* Skip empty buffers */
|
||||
while ((n > 0) && !(rx_buf_tab[rx_buf_idx].addr & RXBUF_OWNERSHIP))
|
||||
{
|
||||
if (++rx_buf_idx >= EMAC_RX_BUFFERS)
|
||||
rx_buf_idx = 0;
|
||||
n--;
|
||||
}
|
||||
if (UNLIKELY(!n))
|
||||
{
|
||||
LOG_INFO("no frame found\n");
|
||||
return 0;
|
||||
}
|
||||
/* Search the start of frame and cleanup fragments */
|
||||
while ((n > 0) && (rx_buf_tab[rx_buf_idx].addr & RXBUF_OWNERSHIP) &&
|
||||
!(rx_buf_tab[rx_buf_idx].stat & RXS_SOF))
|
||||
{
|
||||
rx_buf_tab[rx_buf_idx].addr &= ~RXBUF_OWNERSHIP;
|
||||
if (++rx_buf_idx >= EMAC_RX_BUFFERS)
|
||||
rx_buf_idx = 0;
|
||||
n--;
|
||||
}
|
||||
if (UNLIKELY(!n))
|
||||
{
|
||||
LOG_INFO("no SOF found\n");
|
||||
return 0;
|
||||
}
|
||||
/* Search end of frame to evaluate the total frame size */
|
||||
idx = rx_buf_idx;
|
||||
restart:
|
||||
while (n > 0)
|
||||
{
|
||||
if (UNLIKELY(!(rx_buf_tab[idx].addr & RXBUF_OWNERSHIP)))
|
||||
{
|
||||
/* Empty buffer found. Realign. */
|
||||
eth_buf_realign(idx);
|
||||
goto skip;
|
||||
}
|
||||
if (rx_buf_tab[idx].stat & RXS_EOF)
|
||||
return rx_buf_tab[idx].stat & RXS_LENGTH_FRAME;
|
||||
if (UNLIKELY((idx != rx_buf_idx) &&
|
||||
(rx_buf_tab[idx].stat & RXS_SOF)))
|
||||
{
|
||||
/* Another start of frame found. Realign. */
|
||||
eth_buf_realign(idx);
|
||||
goto restart;
|
||||
}
|
||||
if (++idx >= EMAC_RX_BUFFERS)
|
||||
idx = 0;
|
||||
n--;
|
||||
}
|
||||
LOG_INFO("no EOF found\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t eth_getFrameLen(void)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
/* Check if there is at least one available frame in the buffer */
|
||||
while (1)
|
||||
{
|
||||
len = __eth_getFrameLen();
|
||||
if (LIKELY(len))
|
||||
break;
|
||||
/* Wait for RX interrupt */
|
||||
event_wait(&recv_wait);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
ssize_t eth_getFrame(uint8_t *buf, size_t len)
|
||||
{
|
||||
uint8_t *addr;
|
||||
size_t rd_len = 0;
|
||||
|
||||
if (UNLIKELY(!len))
|
||||
return -1;
|
||||
ASSERT(len <= sizeof(rx_buf));
|
||||
|
||||
/* Copy data from the RX buffer */
|
||||
addr = (uint8_t *)(rx_buf_tab[rx_buf_idx].addr & BUF_ADDRMASK);
|
||||
if (addr + len > &rx_buf[countof(rx_buf)])
|
||||
{
|
||||
size_t count = &rx_buf[countof(rx_buf)] - addr;
|
||||
|
||||
memcpy(buf, addr, count);
|
||||
memcpy(buf + count, rx_buf, len - count);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(buf, addr, len);
|
||||
}
|
||||
/* Update descriptors */
|
||||
while (rd_len < len)
|
||||
{
|
||||
if (len - rd_len >= EMAC_RX_BUFSIZ)
|
||||
rd_len += EMAC_RX_BUFSIZ;
|
||||
else
|
||||
rd_len += len - rd_len;
|
||||
if (UNLIKELY(!(rx_buf_tab[rx_buf_idx].addr & RXBUF_OWNERSHIP)))
|
||||
{
|
||||
LOG_INFO("bad frame found\n");
|
||||
return 0;
|
||||
}
|
||||
rx_buf_tab[rx_buf_idx].addr &= ~RXBUF_OWNERSHIP;
|
||||
if (++rx_buf_idx >= EMAC_RX_DESCRIPTORS)
|
||||
rx_buf_idx = 0;
|
||||
}
|
||||
|
||||
return rd_len;
|
||||
}
|
||||
|
||||
ssize_t eth_recv(uint8_t *buf, size_t len)
|
||||
{
|
||||
if (UNLIKELY(!len))
|
||||
return -1;
|
||||
len = MIN(len, eth_getFrameLen());
|
||||
return len ? eth_getFrame(buf, len) : 0;
|
||||
}
|
||||
|
||||
int eth_init()
|
||||
{
|
||||
cpu_flags_t flags;
|
||||
|
||||
emac_reset();
|
||||
emac_start();
|
||||
|
||||
event_initGeneric(&recv_wait);
|
||||
event_initGeneric(&send_wait);
|
||||
|
||||
// Register interrupt vector
|
||||
IRQ_SAVE_DISABLE(flags);
|
||||
|
||||
/* Disable all emac interrupts */
|
||||
EMAC_IDR = 0xFFFFFFFF;
|
||||
|
||||
#if CPU_ARM_AT91
|
||||
// TODO: define sysirq_set...
|
||||
/* Set the vector. */
|
||||
AIC_SVR(EMAC_ID) = emac_irqHandler;
|
||||
/* Initialize to edge triggered with defined priority. */
|
||||
AIC_SMR(EMAC_ID) = AIC_SRCTYPE_INT_EDGE_TRIGGERED;
|
||||
/* Clear pending interrupt */
|
||||
AIC_ICCR = BV(EMAC_ID);
|
||||
/* Enable the system IRQ */
|
||||
AIC_IECR = BV(EMAC_ID);
|
||||
#else
|
||||
sysirq_setHandler(INT_EMAC, emac_irqHandler);
|
||||
#endif
|
||||
|
||||
/* Enable interrupts */
|
||||
EMAC_IER = EMAC_RX_INTS | EMAC_TX_INTS;
|
||||
|
||||
IRQ_RESTORE(flags);
|
||||
|
||||
return 0;
|
||||
}
|
210
bertos/cpu/cortex-m3/drv/eth_sam3.h
Normal file
210
bertos/cpu/cortex-m3/drv/eth_sam3.h
Normal file
|
@ -0,0 +1,210 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2009,2010,2011 Develer S.r.l. (http://www.develer.com/)
|
||||
* All Rights Reserved.
|
||||
* -->
|
||||
*
|
||||
* \brief EMAC driver for AT91SAM family with Davicom 9161A phy, interface.
|
||||
*
|
||||
* \author Daniele Basile <asterix@develer.com>
|
||||
* \author Andrea Righi <arighi@develer.com>
|
||||
* \author Stefano Fedrigo <aleph@develer.com>
|
||||
*/
|
||||
|
||||
#ifndef ETH_SAM3_H
|
||||
#define ETH_SAM3_H
|
||||
|
||||
// Settings and definition for DAVICOM 9161A
|
||||
// \{
|
||||
#define NIC_PHY_ADDR 0
|
||||
|
||||
// Register bits definition
|
||||
#define NIC_PHY_BMCR 0x00 // Basic mode control register.
|
||||
#define NIC_PHY_BMCR_COLTEST 0x0080 // Collision test.
|
||||
#define NIC_PHY_BMCR_FDUPLEX 0x0100 // Full duplex mode.
|
||||
#define NIC_PHY_BMCR_ANEGSTART 0x0200 // Restart auto negotiation.
|
||||
#define NIC_PHY_BMCR_ISOLATE 0x0400 // Isolate from MII.
|
||||
#define NIC_PHY_BMCR_PWRDN 0x0800 // Power-down.
|
||||
#define NIC_PHY_BMCR_ANEGENA 0x1000 // Enable auto negotiation.
|
||||
#define NIC_PHY_BMCR_100MBPS 0x2000 // Select 100 Mbps.
|
||||
#define NIC_PHY_BMCR_LOOPBACK 0x4000 // Enable loopback mode.
|
||||
#define NIC_PHY_BMCR_RESET 0x8000 // Software reset.
|
||||
|
||||
#define NIC_PHY_BMSR 0x01 // Basic mode status register.
|
||||
#define NIC_PHY_BMSR_ANCOMPL 0x0020 // Auto negotiation complete.
|
||||
#define NIC_PHY_BMSR_ANEGCAPABLE 0x0008 // Able to do auto-negotiation
|
||||
#define NIC_PHY_BMSR_LINKSTAT 0x0004 // Link status.
|
||||
|
||||
#define NIC_PHY_ANLPAR_10_HDX BV(5) // 10BASE-T half duplex
|
||||
#define NIC_PHY_ANLPAR_10_FDX BV(6) // 10BASE-T full duplex
|
||||
#define NIC_PHY_ANLPAR_TX_HDX BV(7) // 100BASE-TX half duplex
|
||||
#define NIC_PHY_ANLPAR_TX_FDX BV(8) // 100BASE-TX full duplex
|
||||
|
||||
#define NIC_PHY_ID1 0x02 // PHY identifier register 1.
|
||||
#define NIC_PHY_ID2 0x03 // PHY identifier register 2.
|
||||
#define NIC_PHY_ANAR 0x04 // Auto negotiation advertisement register.
|
||||
#define NIC_PHY_ANLPAR 0x05 // Auto negotiation link partner availability register.
|
||||
#define NIC_PHY_ANER 0x06 // Auto negotiation expansion register.
|
||||
|
||||
#if CPU_ARM_AT91
|
||||
|
||||
/*
|
||||
* Pin definition for DAVICOM 9161A.
|
||||
* See schematics for AT91SAM7X-EK evalution board.
|
||||
*/
|
||||
// All pins in port B
|
||||
#define PHY_REFCLK_XT2_BIT 0
|
||||
#define PHY_TXEN_BIT 1
|
||||
#define PHY_TXD0_BIT 2
|
||||
#define PHY_TXD1_BIT 3
|
||||
#define PHY_CRS_AD4_BIT 4
|
||||
#define PHY_RXD0_AD0_BIT 5
|
||||
#define PHY_RXD1_AD1_BIT 6
|
||||
#define PHY_RXER_RXD4_RPTR_BIT 7
|
||||
#define PHY_MDC_BIT 8
|
||||
#define PHY_MDIO_BIT 9
|
||||
#define PHY_TXD2_BIT 10
|
||||
#define PHY_TXD3_BIT 11
|
||||
#define PHY_TXER_TXD4_BIT 12
|
||||
#define PHY_RXD2_AD2_BIT 13
|
||||
#define PHY_RXD3_AD3_BIT 14
|
||||
#define PHY_RXDV_TESTMODE_BIT 15
|
||||
#define PHY_COL_RMII_BIT 16
|
||||
#define PHY_RXCLK_10BTSER_BIT 17
|
||||
#define PHY_PWRDN_BIT 18
|
||||
#define PHY_MDINTR_BIT 26
|
||||
|
||||
#define PHY_MII_PINS \
|
||||
BV(PHY_REFCLK_XT2_BIT) \
|
||||
| BV(PHY_TXEN_BIT) \
|
||||
| BV(PHY_TXD0_BIT) \
|
||||
| BV(PHY_TXD1_BIT) \
|
||||
| BV(PHY_CRS_AD4_BIT) \
|
||||
| BV(PHY_RXD0_AD0_BIT) \
|
||||
| BV(PHY_RXD1_AD1_BIT) \
|
||||
| BV(PHY_RXER_RXD4_RPTR_BIT) \
|
||||
| BV(PHY_MDC_BIT) \
|
||||
| BV(PHY_MDIO_BIT) \
|
||||
| BV(PHY_TXD2_BIT) \
|
||||
| BV(PHY_TXD3_BIT) \
|
||||
| BV(PHY_TXER_TXD4_BIT) \
|
||||
| BV(PHY_RXD2_AD2_BIT) \
|
||||
| BV(PHY_RXD3_AD3_BIT) \
|
||||
| BV(PHY_RXDV_TESTMODE_BIT) \
|
||||
| BV(PHY_COL_RMII_BIT) \
|
||||
| BV(PHY_RXCLK_10BTSER_BIT)
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
* Pin definition for DAVICOM 9161A.
|
||||
* See schematics for SAM3X-EK evalution board.
|
||||
*/
|
||||
// Port B
|
||||
#define PHY_REFCLK_XT2_BIT 0
|
||||
#define PHY_TXEN_BIT 1
|
||||
#define PHY_TXD0_BIT 2
|
||||
#define PHY_TXD1_BIT 3
|
||||
#define PHY_RXDV_TESTMODE_BIT 4
|
||||
#define PHY_RXD0_AD0_BIT 5
|
||||
#define PHY_RXD1_AD1_BIT 6
|
||||
#define PHY_RXER_RXD4_RPTR_BIT 7
|
||||
#define PHY_MDC_BIT 8
|
||||
#define PHY_MDIO_BIT 9
|
||||
// Port A
|
||||
#define PHY_MDINTR_BIT 5
|
||||
|
||||
#define PHY_MII_PINS_PORTB \
|
||||
BV(PHY_REFCLK_XT2_BIT) \
|
||||
| BV(PHY_TXEN_BIT) \
|
||||
| BV(PHY_TXD0_BIT) \
|
||||
| BV(PHY_TXD1_BIT) \
|
||||
| BV(PHY_RXD0_AD0_BIT) \
|
||||
| BV(PHY_RXD1_AD1_BIT) \
|
||||
| BV(PHY_RXER_RXD4_RPTR_BIT) \
|
||||
| BV(PHY_MDC_BIT) \
|
||||
| BV(PHY_MDIO_BIT)
|
||||
|
||||
#endif /* CPU_ARM_AT91 */
|
||||
// \}
|
||||
|
||||
|
||||
#define EMAC_TX_BUFSIZ 1518 //!!! Don't change this
|
||||
#define EMAC_TX_BUFFERS 1 //!!! Don't change this
|
||||
#define EMAC_TX_DESCRIPTORS EMAC_TX_BUFFERS
|
||||
|
||||
#define EMAC_RX_BUFFERS 32 //!!! Don't change this
|
||||
#define EMAC_RX_BUFSIZ 128 //!!! Don't change this
|
||||
#define EMAC_RX_DESCRIPTORS EMAC_RX_BUFFERS
|
||||
|
||||
// Flag to manage local tx buffer
|
||||
#define TXS_USED 0x80000000 //Used buffer.
|
||||
#define TXS_WRAP 0x40000000 //Last descriptor.
|
||||
#define TXS_ERROR 0x20000000 //Retry limit exceeded.
|
||||
#define TXS_UNDERRUN 0x10000000 //Transmit underrun.
|
||||
#define TXS_NO_BUFFER 0x08000000 //Buffer exhausted.
|
||||
#define TXS_NO_CRC 0x00010000 //CRC not appended.
|
||||
#define TXS_LAST_BUFF 0x00008000 //Last buffer of frame.
|
||||
#define TXS_LENGTH_FRAME 0x000007FF // Length of frame including FCS.
|
||||
|
||||
// Flag to manage local rx buffer
|
||||
#define RXBUF_OWNERSHIP 0x00000001
|
||||
#define RXBUF_WRAP 0x00000002
|
||||
|
||||
#define BUF_ADDRMASK 0xFFFFFFFC
|
||||
|
||||
#define RXS_BROADCAST_ADDR 0x80000000 // Broadcast address detected.
|
||||
#define RXS_MULTICAST_HASH 0x40000000 // Multicast hash match.
|
||||
#define RXS_UNICAST_HASH 0x20000000 // Unicast hash match.
|
||||
#define RXS_EXTERNAL_ADDR 0x10000000 // External address match.
|
||||
#define RXS_SA1_ADDR 0x04000000 // Specific address register 1 match.
|
||||
#define RXS_SA2_ADDR 0x02000000 // Specific address register 2 match.
|
||||
#define RXS_SA3_ADDR 0x01000000 // Specific address register 3 match.
|
||||
#define RXS_SA4_ADDR 0x00800000 // Specific address register 4 match.
|
||||
#define RXS_TYPE_ID 0x00400000 // Type ID match.
|
||||
#define RXS_VLAN_TAG 0x00200000 // VLAN tag detected.
|
||||
#define RXS_PRIORITY_TAG 0x00100000 // Priority tag detected.
|
||||
#define RXS_VLAN_PRIORITY 0x000E0000 // VLAN priority.
|
||||
#define RXS_CFI_IND 0x00010000 // Concatenation format indicator.
|
||||
#define RXS_EOF 0x00008000 // End of frame.
|
||||
#define RXS_SOF 0x00004000 // Start of frame.
|
||||
#define RXS_RBF_OFFSET 0x00003000 // Receive buffer offset mask.
|
||||
#define RXS_LENGTH_FRAME 0x000007FF // Length of frame including FCS.
|
||||
|
||||
#define EMAC_RSR_BITS (BV(EMAC_BNA) | BV(EMAC_REC) | BV(EMAC_OVR))
|
||||
#define EMAC_TSR_BITS (BV(EMAC_UBR) | BV(EMAC_COL) | BV(EMAC_RLES) | \
|
||||
BV(EMAC_BEX) | BV(EMAC_COMP) | BV(EMAC_UND))
|
||||
|
||||
typedef struct BufDescriptor
|
||||
{
|
||||
volatile uint32_t addr;
|
||||
volatile uint32_t stat;
|
||||
} BufDescriptor;
|
||||
|
||||
#endif /* ETH_SAM3_H */
|
47
bertos/cpu/cortex-m3/drv/flash_cm3.h
Normal file
47
bertos/cpu/cortex-m3/drv/flash_cm3.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief Low-level flash module for Cortex-M3 (interface).
|
||||
*
|
||||
* \author Daniele Basile <asterix@develer.com>
|
||||
*/
|
||||
|
||||
#include <cpu/detect.h>
|
||||
|
||||
#if CPU_CM3_LM3S
|
||||
#include "flash_lm3s.h"
|
||||
#elif CPU_CM3_STM32
|
||||
#include "flash_stm32.h"
|
||||
/*#elif Add other Cortex-M3 CPUs here */
|
||||
#else
|
||||
#error Unknown CPU
|
||||
#endif
|
223
bertos/cpu/cortex-m3/drv/flash_lm3s.c
Normal file
223
bertos/cpu/cortex-m3/drv/flash_lm3s.c
Normal file
|
@ -0,0 +1,223 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief LM3S1968 internal flash memory driver.
|
||||
*
|
||||
* \author Andrea Righi <arighi@develer.com>
|
||||
*/
|
||||
|
||||
#include "flash_lm3s.h"
|
||||
#include "cfg/cfg_emb_flash.h"
|
||||
|
||||
// Define log settings for cfg/log.h
|
||||
#define LOG_LEVEL CONFIG_FLASH_EMB_LOG_LEVEL
|
||||
#define LOG_FORMAT CONFIG_FLASH_EMB_LOG_FORMAT
|
||||
#include <cfg/log.h>
|
||||
#include <cfg/macros.h>
|
||||
|
||||
#include <io/kblock.h>
|
||||
|
||||
#include <drv/timer.h>
|
||||
#include <drv/flash.h>
|
||||
|
||||
#include <cpu/power.h> /* cpu_relax() */
|
||||
#include <cpu/types.h>
|
||||
|
||||
#include <string.h> /* memcpy() */
|
||||
|
||||
struct FlashHardware
|
||||
{
|
||||
int status;
|
||||
};
|
||||
|
||||
static bool flash_wait(struct KBlock *blk, uint32_t event)
|
||||
{
|
||||
Flash *fls = FLASH_CAST(blk);
|
||||
ticks_t start = timer_clock();
|
||||
while (true)
|
||||
{
|
||||
if (!(FLASH_FMC_R & event))
|
||||
break;
|
||||
|
||||
if (FLASH_FCRIS_R & FLASH_FCRIS_ARIS)
|
||||
{
|
||||
fls->hw->status |= FLASH_WR_PROTECT;
|
||||
LOG_ERR("wr protect..\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (timer_clock() - start > ms_to_ticks(CONFIG_FLASH_WR_TIMEOUT))
|
||||
{
|
||||
fls->hw->status |= FLASH_WR_TIMEOUT;
|
||||
LOG_ERR("Timeout..\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int lm3s_erasePage(struct KBlock *blk, uint32_t addr)
|
||||
{
|
||||
FLASH_FCMISC_R = FLASH_FCMISC_AMISC;
|
||||
|
||||
FLASH_FMA_R = (volatile uint32_t)addr;
|
||||
FLASH_FMC_R = FLASH_FMC_WRKEY | FLASH_FMC_ERASE;
|
||||
|
||||
return flash_wait(blk, FLASH_FMC_ERASE);
|
||||
}
|
||||
|
||||
static int lm3s_writeWord(struct KBlock *blk, uint32_t addr, uint32_t data)
|
||||
{
|
||||
FLASH_FCMISC_R = FLASH_FCMISC_AMISC;
|
||||
|
||||
FLASH_FMA_R = (volatile uint32_t)addr;
|
||||
FLASH_FMD_R = (volatile uint32_t)data;
|
||||
FLASH_FMC_R = FLASH_FMC_WRKEY | FLASH_FMC_WRITE;
|
||||
|
||||
return flash_wait(blk, FLASH_FMC_WRITE);
|
||||
}
|
||||
|
||||
static size_t lm3s_flash_readDirect(struct KBlock *blk, block_idx_t idx, void *buf, size_t offset, size_t size)
|
||||
{
|
||||
memcpy(buf, (void *)(idx * blk->blk_size + offset), size);
|
||||
return size;
|
||||
}
|
||||
|
||||
static size_t lm3s_flash_writeDirect(struct KBlock *blk, block_idx_t idx, const void *_buf, size_t offset, size_t size)
|
||||
{
|
||||
(void)offset;
|
||||
ASSERT(offset == 0);
|
||||
ASSERT(size == blk->blk_size);
|
||||
|
||||
if (!lm3s_erasePage(blk, (idx * blk->blk_size)))
|
||||
return 0;
|
||||
|
||||
uint32_t addr = idx * blk->blk_size;
|
||||
const uint8_t *buf = (const uint8_t *)_buf;
|
||||
|
||||
while (size)
|
||||
{
|
||||
uint32_t data = (*(buf + 3) << 24) |
|
||||
(*(buf + 2) << 16) |
|
||||
(*(buf + 1) << 8) |
|
||||
*buf;
|
||||
|
||||
if (!lm3s_writeWord(blk, addr, data))
|
||||
return 0;
|
||||
|
||||
size -= 4;
|
||||
buf += 4;
|
||||
addr += 4;
|
||||
}
|
||||
|
||||
return blk->blk_size;
|
||||
}
|
||||
|
||||
static int lm3s_flash_error(struct KBlock *blk)
|
||||
{
|
||||
Flash *fls = FLASH_CAST(blk);
|
||||
return fls->hw->status;
|
||||
}
|
||||
|
||||
static void lm3s_flash_clearerror(struct KBlock *blk)
|
||||
{
|
||||
Flash *fls = FLASH_CAST(blk);
|
||||
fls->hw->status = 0;
|
||||
}
|
||||
|
||||
static const KBlockVTable flash_lm3s_buffered_vt =
|
||||
{
|
||||
.readDirect = lm3s_flash_readDirect,
|
||||
.writeDirect = lm3s_flash_writeDirect,
|
||||
|
||||
.readBuf = kblock_swReadBuf,
|
||||
.writeBuf = kblock_swWriteBuf,
|
||||
.load = kblock_swLoad,
|
||||
.store = kblock_swStore,
|
||||
|
||||
.close = kblock_swClose,
|
||||
|
||||
.error = lm3s_flash_error,
|
||||
.clearerr = lm3s_flash_clearerror,
|
||||
};
|
||||
|
||||
static const KBlockVTable flash_lm3s_unbuffered_vt =
|
||||
{
|
||||
.readDirect = lm3s_flash_readDirect,
|
||||
.writeDirect = lm3s_flash_writeDirect,
|
||||
|
||||
.close = kblock_swClose,
|
||||
|
||||
.error = lm3s_flash_error,
|
||||
.clearerr = lm3s_flash_clearerror,
|
||||
};
|
||||
|
||||
static struct FlashHardware flash_lm3s_hw;
|
||||
static uint8_t flash_buf[FLASH_PAGE_SIZE];
|
||||
|
||||
static void common_init(Flash *fls)
|
||||
{
|
||||
memset(fls, 0, sizeof(*fls));
|
||||
DB(fls->blk.priv.type = KBT_FLASH);
|
||||
|
||||
FLASH_USECRL_R = CPU_FREQ / 1000000 - 1;
|
||||
|
||||
fls->hw = &flash_lm3s_hw;
|
||||
|
||||
fls->blk.blk_size = FLASH_PAGE_SIZE;
|
||||
fls->blk.blk_cnt = FLASH_SIZE / FLASH_PAGE_SIZE;
|
||||
}
|
||||
|
||||
|
||||
void flash_hw_init(Flash *fls, UNUSED_ARG(int, flags))
|
||||
{
|
||||
common_init(fls);
|
||||
fls->blk.priv.vt = &flash_lm3s_buffered_vt;
|
||||
fls->blk.priv.flags |= KB_BUFFERED | KB_PARTIAL_WRITE;
|
||||
fls->blk.priv.buf = flash_buf;
|
||||
|
||||
/* Load the first block in the cache */
|
||||
void *flash_start = 0x0;
|
||||
memcpy(fls->blk.priv.buf, flash_start, fls->blk.blk_size);
|
||||
}
|
||||
|
||||
void flash_hw_initUnbuffered(Flash *fls, UNUSED_ARG(int, flags))
|
||||
{
|
||||
common_init(fls);
|
||||
fls->blk.priv.vt = &flash_lm3s_unbuffered_vt;
|
||||
}
|
||||
|
||||
|
||||
|
41
bertos/cpu/cortex-m3/drv/flash_lm3s.h
Normal file
41
bertos/cpu/cortex-m3/drv/flash_lm3s.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief LM3S1968 internal flash memory driver.
|
||||
*
|
||||
* \author Andrea Righi <arighi@develer.com>
|
||||
*/
|
||||
|
||||
#ifndef FLASH_LM3S_H
|
||||
#define FLASH_LM3S_H
|
||||
|
||||
#endif /* FLASH_LM3S_H */
|
252
bertos/cpu/cortex-m3/drv/flash_stm32.c
Normal file
252
bertos/cpu/cortex-m3/drv/flash_stm32.c
Normal file
|
@ -0,0 +1,252 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief STM32F103xx internal flash memory driver.
|
||||
*
|
||||
* \author Daniele Basile <asterix@develer.com>
|
||||
*/
|
||||
|
||||
#include "flash_stm32.h"
|
||||
|
||||
#include "cfg/cfg_emb_flash.h"
|
||||
|
||||
// Define log settings for cfg/log.h
|
||||
#define LOG_LEVEL CONFIG_FLASH_EMB_LOG_LEVEL
|
||||
#define LOG_FORMAT CONFIG_FLASH_EMB_LOG_FORMAT
|
||||
#include <cfg/log.h>
|
||||
|
||||
#include <drv/timer.h>
|
||||
#include <drv/flash.h>
|
||||
|
||||
#include <cpu/power.h>
|
||||
#include <cpu/detect.h>
|
||||
|
||||
#include <io/stm32.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define EMB_FLASH ((struct stm32_flash*)FLASH_R_BASE)
|
||||
|
||||
struct FlashHardware
|
||||
{
|
||||
uint8_t status;
|
||||
};
|
||||
|
||||
static bool flash_wait(struct KBlock *blk)
|
||||
{
|
||||
Flash *fls = FLASH_CAST(blk);
|
||||
ticks_t start = timer_clock();
|
||||
while (true)
|
||||
{
|
||||
cpu_relax();
|
||||
if (!(EMB_FLASH->SR & FLASH_FLAG_BSY))
|
||||
break;
|
||||
|
||||
if (EMB_FLASH->SR & FLASH_FLAG_PGERR)
|
||||
{
|
||||
fls->hw->status |= FLASH_NOT_ERASED;
|
||||
LOG_ERR("flash not erased..\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (EMB_FLASH->SR & FLASH_FLAG_WRPRTERR)
|
||||
{
|
||||
fls->hw->status |= FLASH_WR_PROTECT;
|
||||
LOG_ERR("wr protect..\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (timer_clock() - start > ms_to_ticks(CONFIG_FLASH_WR_TIMEOUT))
|
||||
{
|
||||
fls->hw->status |= FLASH_WR_TIMEOUT;
|
||||
LOG_ERR("Timeout..\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool stm32_erasePage(struct KBlock *blk, uint32_t page_add)
|
||||
{
|
||||
|
||||
EMB_FLASH->CR |= CR_PER_SET;
|
||||
EMB_FLASH->AR = page_add;
|
||||
EMB_FLASH->CR |= CR_STRT_SET;
|
||||
|
||||
if (!flash_wait(blk))
|
||||
return false;
|
||||
|
||||
EMB_FLASH->CR &= CR_PER_RESET;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if 0
|
||||
// not used for now
|
||||
static bool stm32_eraseAll(struct KBlock *blk)
|
||||
{
|
||||
EMB_FLASH->CR |= CR_MER_SET;
|
||||
EMB_FLASH->CR |= CR_STRT_SET;
|
||||
|
||||
if (!flash_wait(blk))
|
||||
return false;
|
||||
|
||||
EMB_FLASH->CR &= CR_MER_RESET;
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int stm32_flash_error(struct KBlock *blk)
|
||||
{
|
||||
Flash *fls = FLASH_CAST(blk);
|
||||
return fls->hw->status;
|
||||
}
|
||||
|
||||
static void stm32_flash_clearerror(struct KBlock *blk)
|
||||
{
|
||||
Flash *fls = FLASH_CAST(blk);
|
||||
fls->hw->status = 0;
|
||||
}
|
||||
|
||||
static size_t stm32_flash_readDirect(struct KBlock *blk, block_idx_t idx, void *buf, size_t offset, size_t size)
|
||||
{
|
||||
memcpy(buf, (void *)(idx * blk->blk_size + offset), size);
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
INLINE bool stm32_writeWord(struct KBlock *blk, uint32_t addr, uint16_t data)
|
||||
{
|
||||
ASSERT(!(addr % 2));
|
||||
|
||||
EMB_FLASH->CR |= CR_PG_SET;
|
||||
|
||||
*(reg16_t *)addr = data;
|
||||
|
||||
if (!flash_wait(blk))
|
||||
return false;
|
||||
|
||||
EMB_FLASH->CR &= CR_PG_RESET;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static size_t stm32_flash_writeDirect(struct KBlock *blk, block_idx_t idx, const void *_buf, size_t offset, size_t size)
|
||||
{
|
||||
ASSERT(offset == 0);
|
||||
ASSERT(size == blk->blk_size);
|
||||
|
||||
if (!stm32_erasePage(blk, (idx * blk->blk_size)))
|
||||
return 0;
|
||||
|
||||
uint32_t addr = idx * blk->blk_size;
|
||||
const uint8_t *buf = (const uint8_t *)_buf;
|
||||
|
||||
while (size)
|
||||
{
|
||||
uint16_t data = (*(buf + 1) << 8) | *buf;
|
||||
if (!stm32_writeWord(blk, addr, data))
|
||||
return 0;
|
||||
|
||||
buf += 2;
|
||||
size -= 2;
|
||||
addr += 2;
|
||||
}
|
||||
|
||||
return blk->blk_size;
|
||||
}
|
||||
|
||||
static const KBlockVTable flash_stm32_buffered_vt =
|
||||
{
|
||||
.readDirect = stm32_flash_readDirect,
|
||||
.writeDirect = stm32_flash_writeDirect,
|
||||
|
||||
.readBuf = kblock_swReadBuf,
|
||||
.writeBuf = kblock_swWriteBuf,
|
||||
.load = kblock_swLoad,
|
||||
.store = kblock_swStore,
|
||||
|
||||
.close = kblock_swClose,
|
||||
|
||||
.error = stm32_flash_error,
|
||||
.clearerr = stm32_flash_clearerror,
|
||||
};
|
||||
|
||||
static const KBlockVTable flash_stm32_unbuffered_vt =
|
||||
{
|
||||
.readDirect = stm32_flash_readDirect,
|
||||
.writeDirect = stm32_flash_writeDirect,
|
||||
|
||||
.close = kblock_swClose,
|
||||
|
||||
.error = stm32_flash_error,
|
||||
.clearerr = stm32_flash_clearerror,
|
||||
};
|
||||
|
||||
static struct FlashHardware flash_stm32_hw;
|
||||
static uint8_t flash_buf[FLASH_PAGE_SIZE];
|
||||
|
||||
static void common_init(Flash *fls)
|
||||
{
|
||||
memset(fls, 0, sizeof(*fls));
|
||||
DB(fls->blk.priv.type = KBT_FLASH);
|
||||
|
||||
fls->hw = &flash_stm32_hw;
|
||||
|
||||
fls->blk.blk_size = FLASH_PAGE_SIZE;
|
||||
fls->blk.blk_cnt = (F_SIZE * 1024) / FLASH_PAGE_SIZE;
|
||||
|
||||
/* Unlock flash memory for the FPEC Access */
|
||||
EMB_FLASH->KEYR = FLASH_KEY1;
|
||||
EMB_FLASH->KEYR = FLASH_KEY2;
|
||||
}
|
||||
|
||||
|
||||
void flash_hw_init(Flash *fls, UNUSED_ARG(int, flags))
|
||||
{
|
||||
common_init(fls);
|
||||
fls->blk.priv.vt = &flash_stm32_buffered_vt;
|
||||
fls->blk.priv.flags |= KB_BUFFERED | KB_PARTIAL_WRITE;
|
||||
fls->blk.priv.buf = flash_buf;
|
||||
|
||||
/* Load the first block in the cache */
|
||||
void *flash_start = 0x0;
|
||||
memcpy(fls->blk.priv.buf, flash_start, fls->blk.blk_size);
|
||||
}
|
||||
|
||||
void flash_hw_initUnbuffered(Flash *fls, UNUSED_ARG(int, flags))
|
||||
{
|
||||
common_init(fls);
|
||||
fls->blk.priv.vt = &flash_stm32_unbuffered_vt;
|
||||
}
|
41
bertos/cpu/cortex-m3/drv/flash_stm32.h
Normal file
41
bertos/cpu/cortex-m3/drv/flash_stm32.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief STM32F103xx internal flash memory driver.
|
||||
*
|
||||
* \author Daniele Basile <asterix@develer.com>
|
||||
*/
|
||||
|
||||
#ifndef FLASH_STM32_H
|
||||
#define FLASH_STM32_H
|
||||
|
||||
#endif /* FLASH_STM32_H */
|
206
bertos/cpu/cortex-m3/drv/gpio_lm3s.c
Normal file
206
bertos/cpu/cortex-m3/drv/gpio_lm3s.c
Normal file
|
@ -0,0 +1,206 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief LM3S1968 GPIO control interface.
|
||||
*
|
||||
* \author Andrea Righi <arighi@develer.com>
|
||||
*/
|
||||
|
||||
|
||||
#include "gpio_lm3s.h"
|
||||
|
||||
#include <cfg/compiler.h>
|
||||
#include <cfg/debug.h>
|
||||
|
||||
#include <io/lm3s.h>
|
||||
|
||||
|
||||
/* Set the pin(s) direction and mode */
|
||||
INLINE int lm3s_gpioPinConfigMode(uint32_t port, uint8_t pins, uint32_t mode)
|
||||
{
|
||||
if (mode == GPIO_DIR_MODE_IN)
|
||||
{
|
||||
HWREG(port + GPIO_O_DIR) &= ~pins;
|
||||
HWREG(port + GPIO_O_AFSEL) &= ~pins;
|
||||
}
|
||||
else if (mode == GPIO_DIR_MODE_OUT)
|
||||
{
|
||||
HWREG(port + GPIO_O_DIR) |= pins;
|
||||
HWREG(port + GPIO_O_AFSEL) &= ~pins;
|
||||
}
|
||||
else if (mode == GPIO_DIR_MODE_HW)
|
||||
{
|
||||
HWREG(port + GPIO_O_DIR) &= ~pins;
|
||||
HWREG(port + GPIO_O_AFSEL) |= pins;
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT(0);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set the pin(s) output strength */
|
||||
INLINE int
|
||||
lm3s_gpioPinConfigStrength(uint32_t port, uint8_t pins, uint32_t strength)
|
||||
{
|
||||
if (strength == GPIO_STRENGTH_2MA)
|
||||
{
|
||||
HWREG(port + GPIO_O_DR2R) |= pins;
|
||||
HWREG(port + GPIO_O_DR4R) &= ~pins;
|
||||
HWREG(port + GPIO_O_DR8R) &= ~pins;
|
||||
HWREG(port + GPIO_O_SLR) &= ~pins;
|
||||
}
|
||||
else if (strength == GPIO_STRENGTH_4MA)
|
||||
{
|
||||
HWREG(port + GPIO_O_DR2R) &= ~pins;
|
||||
HWREG(port + GPIO_O_DR4R) |= pins;
|
||||
HWREG(port + GPIO_O_DR8R) &= ~pins;
|
||||
HWREG(port + GPIO_O_SLR) &= ~pins;
|
||||
}
|
||||
else if (strength == GPIO_STRENGTH_8MA)
|
||||
{
|
||||
HWREG(port + GPIO_O_DR2R) &= ~pins;
|
||||
HWREG(port + GPIO_O_DR4R) &= ~pins;
|
||||
HWREG(port + GPIO_O_DR8R) |= pins;
|
||||
HWREG(port + GPIO_O_SLR) &= ~pins;
|
||||
}
|
||||
else if (strength == GPIO_STRENGTH_8MA_SC)
|
||||
{
|
||||
HWREG(port + GPIO_O_DR2R) &= ~pins;
|
||||
HWREG(port + GPIO_O_DR4R) &= ~pins;
|
||||
HWREG(port + GPIO_O_DR8R) |= pins;
|
||||
HWREG(port + GPIO_O_SLR) |= pins;
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT(0);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set the pin(s) type */
|
||||
INLINE int lm3s_gpioPinConfigType(uint32_t port, uint8_t pins, uint32_t type)
|
||||
{
|
||||
if (type == GPIO_PIN_TYPE_STD)
|
||||
{
|
||||
HWREG(port + GPIO_O_ODR) &= ~pins;
|
||||
HWREG(port + GPIO_O_PUR) &= ~pins;
|
||||
HWREG(port + GPIO_O_PDR) &= ~pins;
|
||||
HWREG(port + GPIO_O_DEN) |= pins;
|
||||
HWREG(port + GPIO_O_AMSEL) &= ~pins;
|
||||
}
|
||||
else if (type == GPIO_PIN_TYPE_STD_WPU)
|
||||
{
|
||||
HWREG(port + GPIO_O_ODR) &= ~pins;
|
||||
HWREG(port + GPIO_O_PUR) |= pins;
|
||||
HWREG(port + GPIO_O_PDR) &= ~pins;
|
||||
HWREG(port + GPIO_O_DEN) |= pins;
|
||||
HWREG(port + GPIO_O_AMSEL) &= ~pins;
|
||||
}
|
||||
else if (type == GPIO_PIN_TYPE_STD_WPD)
|
||||
{
|
||||
HWREG(port + GPIO_O_ODR) &= ~pins;
|
||||
HWREG(port + GPIO_O_PUR) &= ~pins;
|
||||
HWREG(port + GPIO_O_PDR) |= pins;
|
||||
HWREG(port + GPIO_O_DEN) |= pins;
|
||||
HWREG(port + GPIO_O_AMSEL) &= ~pins;
|
||||
}
|
||||
else if (type == GPIO_PIN_TYPE_OD)
|
||||
{
|
||||
HWREG(port + GPIO_O_ODR) |= pins;
|
||||
HWREG(port + GPIO_O_PUR) &= ~pins;
|
||||
HWREG(port + GPIO_O_PDR) &= ~pins;
|
||||
HWREG(port + GPIO_O_DEN) |= pins;
|
||||
HWREG(port + GPIO_O_AMSEL) &= ~pins;
|
||||
}
|
||||
else if (type == GPIO_PIN_TYPE_OD_WPU)
|
||||
{
|
||||
HWREG(port + GPIO_O_ODR) |= pins;
|
||||
HWREG(port + GPIO_O_PUR) |= pins;
|
||||
HWREG(port + GPIO_O_PDR) &= ~pins;
|
||||
HWREG(port + GPIO_O_DEN) |= pins;
|
||||
HWREG(port + GPIO_O_AMSEL) &= ~pins;
|
||||
}
|
||||
else if (type == GPIO_PIN_TYPE_OD_WPD)
|
||||
{
|
||||
HWREG(port + GPIO_O_ODR) |= pins;
|
||||
HWREG(port + GPIO_O_PUR) &= pins;
|
||||
HWREG(port + GPIO_O_PDR) |= pins;
|
||||
HWREG(port + GPIO_O_DEN) |= pins;
|
||||
HWREG(port + GPIO_O_AMSEL) &= ~pins;
|
||||
}
|
||||
else if (type == GPIO_PIN_TYPE_ANALOG)
|
||||
{
|
||||
HWREG(port + GPIO_O_ODR) &= ~pins;
|
||||
HWREG(port + GPIO_O_PUR) &= ~pins;
|
||||
HWREG(port + GPIO_O_PDR) &= ~pins;
|
||||
HWREG(port + GPIO_O_DEN) &= ~pins;
|
||||
HWREG(port + GPIO_O_AMSEL) |= pins;
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT(0);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure a GPIO pin
|
||||
*
|
||||
* \param port Base address of the GPIO port
|
||||
* \param pins Bit-packed representation of the pin(s)
|
||||
* \param mode Pin(s) configuration mode
|
||||
* \param strength Output drive strength
|
||||
* \param type Pin(s) type
|
||||
*
|
||||
* Return 0 on success, otherwise a negative value.
|
||||
*/
|
||||
int lm3s_gpioPinConfig(uint32_t port, uint8_t pins,
|
||||
uint32_t mode, uint32_t strength, uint32_t type)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = lm3s_gpioPinConfigMode(port, pins, mode);
|
||||
if (UNLIKELY(ret < 0))
|
||||
return ret;
|
||||
ret = lm3s_gpioPinConfigStrength(port, pins, strength);
|
||||
if (UNLIKELY(ret < 0))
|
||||
return ret;
|
||||
ret = lm3s_gpioPinConfigType(port, pins, type);
|
||||
if (UNLIKELY(ret < 0))
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
97
bertos/cpu/cortex-m3/drv/gpio_lm3s.h
Normal file
97
bertos/cpu/cortex-m3/drv/gpio_lm3s.h
Normal file
|
@ -0,0 +1,97 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief LM3S1968 GPIO control interface.
|
||||
*/
|
||||
|
||||
#ifndef GPIO_LM3S_H
|
||||
#define GPIO_LM3S_H
|
||||
|
||||
#include <io/lm3s.h>
|
||||
|
||||
/**
|
||||
* GPIO mode
|
||||
*/
|
||||
/*\{*/
|
||||
enum
|
||||
{
|
||||
GPIO_DIR_MODE_IN = 0, //< Pin is a GPIO input
|
||||
GPIO_DIR_MODE_OUT, //< Pin is a GPIO output
|
||||
GPIO_DIR_MODE_HW, //< Pin is a peripheral function
|
||||
};
|
||||
/*\}*/
|
||||
|
||||
/**
|
||||
* GPIO strenght
|
||||
*/
|
||||
/*\{*/
|
||||
enum
|
||||
{
|
||||
GPIO_STRENGTH_2MA = 0, //< 2mA drive strength
|
||||
GPIO_STRENGTH_4MA, //< 4mA drive strength
|
||||
GPIO_STRENGTH_8MA, //< 8mA drive strength
|
||||
GPIO_STRENGTH_8MA_SC, //< 8mA drive with slew rate control
|
||||
};
|
||||
/*\}*/
|
||||
|
||||
/**
|
||||
* GPIO type
|
||||
*/
|
||||
/*\{*/
|
||||
enum
|
||||
{
|
||||
GPIO_PIN_TYPE_ANALOG = 0, //< Analog comparator
|
||||
GPIO_PIN_TYPE_STD, //< Push-pull
|
||||
GPIO_PIN_TYPE_STD_WPU, //< Push-pull with weak pull-up
|
||||
GPIO_PIN_TYPE_STD_WPD, //< Push-pull with weak pull-down
|
||||
GPIO_PIN_TYPE_OD, //< Open-drain
|
||||
GPIO_PIN_TYPE_OD_WPU, //< Open-drain with weak pull-up
|
||||
GPIO_PIN_TYPE_OD_WPD, //< Open-drain with weak pull-down
|
||||
};
|
||||
/*\}*/
|
||||
|
||||
/* Write a value to the specified pin(s) */
|
||||
INLINE void lm3s_gpioPinWrite(uint32_t port, uint8_t pins, uint8_t val)
|
||||
{
|
||||
HWREG(port + GPIO_O_DATA + (pins << 2)) = val;
|
||||
}
|
||||
|
||||
/* Read a value from the specified pin(s) */
|
||||
INLINE uint32_t lm3s_gpioPinRead(uint32_t port, uint8_t pins)
|
||||
{
|
||||
return HWREG(port + GPIO_O_DATA + (pins << 2));
|
||||
}
|
||||
|
||||
int lm3s_gpioPinConfig(uint32_t port, uint8_t pins,
|
||||
uint32_t mode, uint32_t strength, uint32_t type);
|
||||
|
||||
#endif /* GPIO_LM3S_H */
|
110
bertos/cpu/cortex-m3/drv/gpio_stm32.c
Normal file
110
bertos/cpu/cortex-m3/drv/gpio_stm32.c
Normal file
|
@ -0,0 +1,110 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief STM32 GPIO control interface.
|
||||
*
|
||||
* \author Andrea Righi <arighi@develer.com>
|
||||
*/
|
||||
|
||||
#include "gpio_stm32.h"
|
||||
|
||||
#include <cfg/compiler.h>
|
||||
#include <cfg/debug.h>
|
||||
|
||||
#include <io/stm32.h>
|
||||
|
||||
|
||||
/**
|
||||
* Configure a GPIO pin
|
||||
*
|
||||
* \param base Base address of the GPIO port
|
||||
* \param pins Bit-packed representation of the pin(s)
|
||||
* \param mode Pin(s) configuration mode
|
||||
* \param speed Output drive speed
|
||||
*
|
||||
* Return 0 on success, otherwise a negative value.
|
||||
*/
|
||||
int stm32_gpioPinConfig(struct stm32_gpio *base,
|
||||
uint16_t pins, uint8_t mode, uint8_t speed)
|
||||
{
|
||||
uint32_t reg_mode = mode & 0x0f;
|
||||
int i;
|
||||
|
||||
if (mode & 0x10)
|
||||
reg_mode |= speed;
|
||||
|
||||
if (pins & 0xff)
|
||||
{
|
||||
uint32_t reg = base->CRL;
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
uint32_t pos = 1 << i;
|
||||
|
||||
if (pins & pos)
|
||||
{
|
||||
pos = i << 2;
|
||||
reg &= ~(0x0f << pos);
|
||||
reg |= reg_mode << pos;
|
||||
|
||||
if (mode == GPIO_MODE_IPD)
|
||||
base->BRR = 0x01 << i;
|
||||
if (mode == GPIO_MODE_IPU)
|
||||
base->BSRR = 0x01 << i;
|
||||
}
|
||||
}
|
||||
base->CRL = reg;
|
||||
}
|
||||
if (pins > 0xff)
|
||||
{
|
||||
uint32_t reg = base->CRH;
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
uint32_t pos = 1 << (i + 8);
|
||||
|
||||
if (pins & pos)
|
||||
{
|
||||
pos = i << 2;
|
||||
reg &= ~(0x0f << pos);
|
||||
reg |= reg_mode << pos;
|
||||
|
||||
if (mode == GPIO_MODE_IPD)
|
||||
base->BRR = 0x01 << (i + 8);
|
||||
if (mode == GPIO_MODE_IPU)
|
||||
base->BSRR = 0x01 << (i + 8);
|
||||
}
|
||||
}
|
||||
base->CRH = reg;
|
||||
}
|
||||
return 0;
|
||||
}
|
106
bertos/cpu/cortex-m3/drv/gpio_stm32.h
Normal file
106
bertos/cpu/cortex-m3/drv/gpio_stm32.h
Normal file
|
@ -0,0 +1,106 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief STM32 GPIO control interface.
|
||||
*/
|
||||
|
||||
#ifndef GPIO_STM32_H
|
||||
#define GPIO_STM32_H
|
||||
|
||||
#include <io/stm32.h>
|
||||
|
||||
/**
|
||||
* GPIO mode
|
||||
* \{
|
||||
*/
|
||||
enum
|
||||
{
|
||||
GPIO_MODE_AIN = 0x0,
|
||||
GPIO_MODE_IN_FLOATING = 0x04,
|
||||
GPIO_MODE_IPD = 0x28,
|
||||
GPIO_MODE_IPU = 0x48,
|
||||
GPIO_MODE_OUT_OD = 0x14,
|
||||
GPIO_MODE_OUT_PP = 0x10,
|
||||
GPIO_MODE_AF_OD = 0x1C,
|
||||
GPIO_MODE_AF_PP = 0x18,
|
||||
};
|
||||
/*\}*/
|
||||
|
||||
/**
|
||||
* GPIO speed
|
||||
*\{
|
||||
*/
|
||||
enum
|
||||
{
|
||||
GPIO_SPEED_10MHZ = 1,
|
||||
GPIO_SPEED_2MHZ,
|
||||
GPIO_SPEED_50MHZ,
|
||||
};
|
||||
/*\}*/
|
||||
|
||||
/**
|
||||
* Write a value to the specified pin(s)
|
||||
*
|
||||
* \param base gpio register address
|
||||
* \param pins mask of pins that we want set or clear
|
||||
* \param val true to set selected pins of false to clear they.
|
||||
*/
|
||||
INLINE void stm32_gpioPinWrite(struct stm32_gpio *base, uint16_t pins, bool val)
|
||||
{
|
||||
if (val)
|
||||
base->BSRR |= pins;
|
||||
else
|
||||
base->BRR |= pins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a value from the specified pin(s)
|
||||
*
|
||||
* \param base gpio register address
|
||||
* \param pins mask of pins that we want read
|
||||
*/
|
||||
INLINE uint16_t stm32_gpioPinRead(struct stm32_gpio *base, uint16_t pins)
|
||||
{
|
||||
return (base->IDR & pins);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a GPIO peripheral configuration
|
||||
*
|
||||
* \param base gpio register address
|
||||
* \param pins mask of pins that we want to configure
|
||||
* \param mode select the behaviour of selected pins
|
||||
* \param speed clock frequency for selected gpio ports
|
||||
*/
|
||||
int stm32_gpioPinConfig(struct stm32_gpio *base, uint16_t pins, uint8_t mode, uint8_t speed);
|
||||
|
||||
#endif /* GPIO_STM32_H */
|
51
bertos/cpu/cortex-m3/drv/i2c_cm3.h
Normal file
51
bertos/cpu/cortex-m3/drv/i2c_cm3.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief Low-level I2C module for ARM Cortex-m3 (interface).
|
||||
*
|
||||
* \author Daniele Basile <asterix@develer.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <cpu/detect.h>
|
||||
|
||||
#if CPU_CM3_LM3S
|
||||
#include "i2c_lm3s.h"
|
||||
#elif CPU_CM3_STM32
|
||||
#include "i2c_stm32.h"
|
||||
#elif CPU_CM3_SAM3
|
||||
#include "i2c_sam3.h"
|
||||
/*#elif Add other Cortex-M3 CPUs here */
|
||||
#else
|
||||
#error Unknown CPU
|
||||
#endif
|
||||
|
256
bertos/cpu/cortex-m3/drv/i2c_lm3s.c
Normal file
256
bertos/cpu/cortex-m3/drv/i2c_lm3s.c
Normal file
|
@ -0,0 +1,256 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief Driver for the LM3S I2C (implementation)
|
||||
*
|
||||
* \author Daniele Basile <asterix@develer.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "cfg/cfg_i2c.h"
|
||||
|
||||
#define LOG_LEVEL I2C_LOG_LEVEL
|
||||
#define LOG_FORMAT I2C_LOG_FORMAT
|
||||
|
||||
#include <cfg/log.h>
|
||||
|
||||
#include <cfg/debug.h>
|
||||
#include <cfg/macros.h> // BV()
|
||||
|
||||
#include <cpu/detect.h>
|
||||
#include <cpu/irq.h>
|
||||
#include <cpu/types.h>
|
||||
#include <cpu/power.h>
|
||||
|
||||
#include <io/lm3s.h>
|
||||
|
||||
#include <drv/timer.h>
|
||||
#include <drv/i2c.h>
|
||||
#include <drv/gpio_lm3s.h>
|
||||
#include <drv/clock_lm3s.h>
|
||||
|
||||
|
||||
struct I2cHardware
|
||||
{
|
||||
uint32_t base;
|
||||
uint32_t sys_cntl;
|
||||
uint32_t sys_gpio;
|
||||
uint32_t pin_mask;
|
||||
uint32_t gpio_base;
|
||||
bool first_xtranf;
|
||||
};
|
||||
|
||||
#define WAIT_BUSY(base) \
|
||||
do { \
|
||||
while (HWREG(base + I2C_O_MCS) & I2C_MCS_BUSY ) \
|
||||
cpu_relax(); \
|
||||
} while (0);
|
||||
|
||||
|
||||
/*
|
||||
* The start is not performed when we call the start function
|
||||
* because the hardware should know the first data byte to send.
|
||||
* Generally to perform a byte send we should write the slave address
|
||||
* in slave address register and the first byte to send in data registry.
|
||||
* After then we can perform the start write procedure, and send really
|
||||
* the our data. To use common bertos i2c api the really start will be
|
||||
* performed when the user "put" or "send" its data. These tricks are hide
|
||||
* from the driver implementation.
|
||||
*/
|
||||
static void i2c_lm3s_start(struct I2c *i2c, uint16_t slave_addr)
|
||||
{
|
||||
i2c->hw->first_xtranf = true;
|
||||
|
||||
if (I2C_TEST_START(i2c->flags) == I2C_START_W)
|
||||
HWREG(i2c->hw->base + I2C_O_MSA) = slave_addr & ~BV(0);
|
||||
else /* (I2C_TEST_START(i2c->flags) == I2C_START_R) */
|
||||
HWREG(i2c->hw->base + I2C_O_MSA) = slave_addr | BV(0);
|
||||
}
|
||||
|
||||
INLINE bool wait_addrAck(I2c *i2c, uint32_t mode_mask)
|
||||
{
|
||||
ticks_t start = timer_clock();
|
||||
while (1)
|
||||
{
|
||||
uint32_t status = HWREG(i2c->hw->base + I2C_O_MCS);
|
||||
|
||||
if (timer_clock() - start > ms_to_ticks(CONFIG_I2C_START_TIMEOUT))
|
||||
return false;
|
||||
|
||||
if(status & I2C_MCS_ADRACK)
|
||||
{
|
||||
HWREG(i2c->hw->base + I2C_O_MCS) = mode_mask;
|
||||
WAIT_BUSY(i2c->hw->base);
|
||||
}
|
||||
else
|
||||
break;
|
||||
|
||||
cpu_relax();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void i2c_lm3s_putc(I2c *i2c, const uint8_t data)
|
||||
{
|
||||
HWREG(i2c->hw->base + I2C_O_MDR) = data;
|
||||
|
||||
if (i2c->hw->first_xtranf)
|
||||
{
|
||||
HWREG(i2c->hw->base + I2C_O_MCS) = I2C_MCS_RUN | I2C_MCS_START;
|
||||
while( HWREG(i2c->hw->base + I2C_O_MCS) & I2C_MCS_BUSY );
|
||||
|
||||
if (!wait_addrAck(i2c, I2C_MCS_RUN | I2C_MCS_START))
|
||||
{
|
||||
LOG_ERR("Start timeout\n");
|
||||
i2c->errors |= I2C_START_TIMEOUT;
|
||||
HWREG(i2c->hw->base + I2C_O_MCS) = I2C_MCS_STOP;
|
||||
WAIT_BUSY(i2c->hw->base);
|
||||
return;
|
||||
}
|
||||
|
||||
i2c->hw->first_xtranf = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
HWREG(i2c->hw->base + I2C_O_MCS) = I2C_MCS_RUN;
|
||||
WAIT_BUSY(i2c->hw->base);
|
||||
}
|
||||
|
||||
if ((i2c->xfer_size == 1) && (I2C_TEST_STOP(i2c->flags) == I2C_STOP))
|
||||
{
|
||||
HWREG(i2c->hw->base + I2C_O_MCS) = I2C_MCS_STOP;
|
||||
WAIT_BUSY(i2c->hw->base);
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t i2c_lm3s_getc(I2c *i2c)
|
||||
{
|
||||
uint8_t data;
|
||||
if (i2c->hw->first_xtranf)
|
||||
{
|
||||
uint32_t start_mode;
|
||||
if (i2c->xfer_size == 1)
|
||||
start_mode = I2C_MCS_RUN | I2C_MCS_START;
|
||||
else
|
||||
start_mode = I2C_MCS_ACK | I2C_MCS_RUN | I2C_MCS_START;
|
||||
|
||||
HWREG(i2c->hw->base + I2C_O_MCS) = start_mode;
|
||||
WAIT_BUSY(i2c->hw->base);
|
||||
if (!wait_addrAck(i2c, start_mode))
|
||||
{
|
||||
LOG_ERR("Start timeout\n");
|
||||
i2c->errors |= I2C_START_TIMEOUT;
|
||||
HWREG(i2c->hw->base + I2C_O_MCS) = I2C_MCS_STOP;
|
||||
WAIT_BUSY(i2c->hw->base);
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
data = HWREG(i2c->hw->base + I2C_O_MDR);
|
||||
i2c->hw->first_xtranf = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i2c->xfer_size > 1)
|
||||
HWREG(i2c->hw->base + I2C_O_MCS) = I2C_MCS_ACK | I2C_MCS_RUN;
|
||||
else
|
||||
HWREG(i2c->hw->base + I2C_O_MCS) = I2C_MCS_RUN;
|
||||
|
||||
WAIT_BUSY(i2c->hw->base);
|
||||
data = HWREG(i2c->hw->base + I2C_O_MDR);
|
||||
}
|
||||
|
||||
if ((i2c->xfer_size == 1) && (I2C_TEST_STOP(i2c->flags) == I2C_STOP))
|
||||
{
|
||||
HWREG(i2c->hw->base + I2C_O_MCS) = I2C_MCS_STOP;
|
||||
WAIT_BUSY(i2c->hw->base);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
static const I2cVT i2c_lm3s_vt =
|
||||
{
|
||||
.start = i2c_lm3s_start,
|
||||
.getc = i2c_lm3s_getc,
|
||||
.putc = i2c_lm3s_putc,
|
||||
.write = i2c_genericWrite,
|
||||
.read = i2c_genericRead,
|
||||
};
|
||||
|
||||
static struct I2cHardware i2c_lm3s_hw[] =
|
||||
{
|
||||
{ /* I2C0 */
|
||||
.base = I2C0_MASTER_BASE,
|
||||
.sys_cntl = SYSCTL_RCGC1_I2C0,
|
||||
.sys_gpio = SYSCTL_RCGC2_GPIOB,
|
||||
.pin_mask = (GPIO_I2C0_SCL_PIN | GPIO_I2C0_SDA_PIN),
|
||||
.gpio_base = GPIO_PORTB_BASE,
|
||||
},
|
||||
{ /* I2C1 */
|
||||
.base = I2C1_MASTER_BASE,
|
||||
.sys_cntl = SYSCTL_RCGC1_I2C1,
|
||||
.sys_gpio = SYSCTL_RCGC2_GPIOA,
|
||||
.pin_mask = (GPIO_I2C1_SCL_PIN | GPIO_I2C1_SDA_PIN),
|
||||
.gpio_base = GPIO_PORTA_BASE,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize I2C module.
|
||||
*/
|
||||
void i2c_hw_init(I2c *i2c, int dev, uint32_t clock)
|
||||
{
|
||||
i2c->hw = &i2c_lm3s_hw[dev];
|
||||
i2c->vt = &i2c_lm3s_vt;
|
||||
|
||||
/* Enable the peripheral clock */
|
||||
SYSCTL_RCGC1_R |= i2c->hw->sys_cntl;
|
||||
SYSCTL_RCGC2_R |= i2c->hw->sys_gpio;
|
||||
|
||||
/* Configure GPIO pins to work as I2C pins */
|
||||
lm3s_gpioPinConfig(i2c->hw->gpio_base, i2c->hw->pin_mask,
|
||||
GPIO_DIR_MODE_HW, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_OD_WPU);
|
||||
/*
|
||||
* Note: to set correctly the i2c speed we shold before enable the i2c
|
||||
* device and then set in master time period the correct value
|
||||
*/
|
||||
|
||||
/* Enable i2c device */
|
||||
HWREG(i2c->hw->base + I2C_O_MCR) |= I2C_MCR_MFE;
|
||||
|
||||
/*
|
||||
* Compute the clock divider that achieves the fastest speed less than or
|
||||
* equal to the desired speed. The numerator is biased to favor a larger
|
||||
* clock divider so that the resulting clock is always less than or equal
|
||||
* to the desired clock, never greater.
|
||||
*/
|
||||
HWREG(i2c->hw->base + I2C_O_MTPR) = ((CPU_FREQ + (2 * 10 * clock) - 1) / (2 * 10 * clock)) - 1;
|
||||
}
|
57
bertos/cpu/cortex-m3/drv/i2c_lm3s.h
Normal file
57
bertos/cpu/cortex-m3/drv/i2c_lm3s.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief Driver for the LM3S I2C (interface)
|
||||
*
|
||||
* \author Daniele Basile <asterix@develer.com>
|
||||
*/
|
||||
|
||||
#ifndef I2C_LM3S_H
|
||||
#define I2C_LM3S_H
|
||||
|
||||
#include <drv/i2c.h>
|
||||
|
||||
/**
|
||||
* \name I2C devices enum
|
||||
*/
|
||||
enum
|
||||
{
|
||||
#if CPU_CM3_LM3S1968
|
||||
I2C0,
|
||||
I2C1,
|
||||
#elif CPU_CM3_LM3S8962
|
||||
I2C0,
|
||||
#enif
|
||||
I2C_CNT /**< Number of serial ports */
|
||||
};
|
||||
|
||||
#endif /* I2C_LM3S_H */
|
253
bertos/cpu/cortex-m3/drv/i2c_sam3.c
Normal file
253
bertos/cpu/cortex-m3/drv/i2c_sam3.c
Normal file
|
@ -0,0 +1,253 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2011 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief TWI driver for SAM3 (implementation)
|
||||
*
|
||||
* Only master mode is supported.
|
||||
*
|
||||
* \author Stefano Fedrigo <aleph@develer.com>
|
||||
*/
|
||||
|
||||
|
||||
#include "cfg/cfg_i2c.h"
|
||||
|
||||
#define LOG_LEVEL I2C_LOG_LEVEL
|
||||
#define LOG_FORMAT I2C_LOG_FORMAT
|
||||
|
||||
#include <cfg/log.h>
|
||||
|
||||
#include <hw/hw_cpufreq.h> // CPU_FREQ
|
||||
#include <cfg/debug.h>
|
||||
#include <cfg/macros.h> // BV()
|
||||
#include <cfg/module.h>
|
||||
#include <cpu/detect.h>
|
||||
#include <cpu/irq.h>
|
||||
#include <cpu/power.h>
|
||||
#include <drv/timer.h>
|
||||
#include <drv/i2c.h>
|
||||
#include <io/sam3.h>
|
||||
|
||||
|
||||
struct I2cHardware
|
||||
{
|
||||
uint32_t base;
|
||||
bool first_xtranf;
|
||||
};
|
||||
|
||||
|
||||
INLINE bool waitTxRdy(I2c *i2c, time_t ms_timeout)
|
||||
{
|
||||
ticks_t start = timer_clock();
|
||||
|
||||
while (!(HWREG(i2c->hw->base + TWI_SR_OFF) & TWI_SR_TXRDY))
|
||||
{
|
||||
if (timer_clock() - start > ms_to_ticks(ms_timeout))
|
||||
return false;
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
INLINE bool waitRxRdy(I2c *i2c, time_t ms_timeout)
|
||||
{
|
||||
ticks_t start = timer_clock();
|
||||
|
||||
while (!(HWREG(i2c->hw->base + TWI_SR_OFF) & TWI_SR_RXRDY))
|
||||
{
|
||||
if (timer_clock() - start > ms_to_ticks(ms_timeout))
|
||||
return false;
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
INLINE void waitXferComplete(I2c *i2c)
|
||||
{
|
||||
while (!(HWREG(i2c->hw->base + TWI_SR_OFF) & TWI_SR_TXCOMP))
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* The start is not performed when we call the start function
|
||||
* because the hardware should know the first data byte to send.
|
||||
* Generally to perform a byte send we should write the slave address
|
||||
* in slave address register and the first byte to send in data registry.
|
||||
* After then we can perform the start write procedure, and send really
|
||||
* the our data. To use common bertos i2c api the really start will be
|
||||
* performed when the user "put" or "send" its data. These tricks are hide
|
||||
* from the driver implementation.
|
||||
*/
|
||||
static void i2c_sam3_start(struct I2c *i2c, uint16_t slave_addr)
|
||||
{
|
||||
i2c->hw->first_xtranf = true;
|
||||
|
||||
if (I2C_TEST_START(i2c->flags) == I2C_START_R)
|
||||
HWREG(i2c->hw->base + TWI_MMR_OFF) = TWI_MMR_DADR(slave_addr >> 1) | TWI_MMR_MREAD;
|
||||
else
|
||||
HWREG(i2c->hw->base + TWI_MMR_OFF) = TWI_MMR_DADR(slave_addr >> 1);
|
||||
}
|
||||
|
||||
static void i2c_sam3_putc(I2c *i2c, const uint8_t data)
|
||||
{
|
||||
if (!waitTxRdy(i2c, CONFIG_I2C_START_TIMEOUT))
|
||||
{
|
||||
LOG_ERR("i2c: txready timeout\n");
|
||||
i2c->errors |= I2C_START_TIMEOUT;
|
||||
return;
|
||||
}
|
||||
|
||||
HWREG(i2c->hw->base + TWI_THR_OFF) = data;
|
||||
|
||||
if ((i2c->xfer_size == 1) && (I2C_TEST_STOP(i2c->flags) == I2C_STOP))
|
||||
HWREG(i2c->hw->base + TWI_CR_OFF) = TWI_CR_STOP;
|
||||
|
||||
// On first byte sent wait for start timeout
|
||||
if (i2c->hw->first_xtranf && !waitTxRdy(i2c, CONFIG_I2C_START_TIMEOUT))
|
||||
{
|
||||
LOG_ERR("i2c: write start timeout\n");
|
||||
i2c->errors |= I2C_START_TIMEOUT;
|
||||
HWREG(i2c->hw->base + TWI_CR_OFF) = TWI_CR_STOP;
|
||||
waitXferComplete(i2c);
|
||||
return;
|
||||
}
|
||||
i2c->hw->first_xtranf = false;
|
||||
|
||||
if ((i2c->xfer_size == 1) && (I2C_TEST_STOP(i2c->flags) == I2C_STOP))
|
||||
waitXferComplete(i2c);
|
||||
}
|
||||
|
||||
static uint8_t i2c_sam3_getc(I2c *i2c)
|
||||
{
|
||||
uint8_t data;
|
||||
uint32_t cr = 0;
|
||||
|
||||
if (i2c->hw->first_xtranf)
|
||||
{
|
||||
cr |= TWI_CR_START;
|
||||
i2c->hw->first_xtranf = false;
|
||||
}
|
||||
if ((i2c->xfer_size == 1) && (I2C_TEST_STOP(i2c->flags) == I2C_STOP))
|
||||
cr |= TWI_CR_STOP;
|
||||
|
||||
HWREG(i2c->hw->base + TWI_CR_OFF) = cr;
|
||||
|
||||
if (!waitRxRdy(i2c, CONFIG_I2C_START_TIMEOUT))
|
||||
{
|
||||
LOG_ERR("i2c: read start timeout\n");
|
||||
i2c->errors |= I2C_START_TIMEOUT;
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
data = HWREG(i2c->hw->base + TWI_RHR_OFF);
|
||||
|
||||
if ((i2c->xfer_size == 1) && (I2C_TEST_STOP(i2c->flags) == I2C_STOP))
|
||||
waitXferComplete(i2c);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static void i2c_setClock(I2c *i2c, int clock)
|
||||
{
|
||||
uint32_t ck_div = 0;
|
||||
uint32_t cl_div;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
cl_div = ((CPU_FREQ / (2 * clock)) - 4) / (1 << ck_div);
|
||||
|
||||
if (cl_div <= 255)
|
||||
break;
|
||||
|
||||
ck_div++;
|
||||
}
|
||||
|
||||
ASSERT(ck_div < 8);
|
||||
LOG_INFO("i2c: using CKDIV = %lu and CLDIV/CHDIV = %lu\n\n", ck_div, cl_div);
|
||||
|
||||
HWREG(i2c->hw->base + TWI_CWGR_OFF) = 0;
|
||||
HWREG(i2c->hw->base + TWI_CWGR_OFF) = (ck_div << 16) | (cl_div << 8) | cl_div;
|
||||
}
|
||||
|
||||
|
||||
static const I2cVT i2c_sam3_vt =
|
||||
{
|
||||
.start = i2c_sam3_start,
|
||||
.getc = i2c_sam3_getc,
|
||||
.putc = i2c_sam3_putc,
|
||||
.write = i2c_genericWrite,
|
||||
.read = i2c_genericRead,
|
||||
};
|
||||
|
||||
struct I2cHardware i2c_sam3_hw[I2C_CNT];
|
||||
|
||||
|
||||
/**
|
||||
* Initialize I2C module.
|
||||
*/
|
||||
void i2c_hw_init(I2c *i2c, int dev, uint32_t clock)
|
||||
{
|
||||
ASSERT(dev < I2C_CNT);
|
||||
|
||||
i2c->hw = &i2c_sam3_hw[dev];
|
||||
i2c->vt = &i2c_sam3_vt;
|
||||
|
||||
pmc_periphEnable(PIOA_ID);
|
||||
|
||||
switch (dev)
|
||||
{
|
||||
case I2C0:
|
||||
i2c->hw->base = TWI0_BASE;
|
||||
PIO_PERIPH_SEL(TWI0_PORT, BV(TWI0_TWD) | BV(TWI0_TWCK), TWI0_PERIPH);
|
||||
HWREG(TWI0_PORT + PIO_PDR_OFF) = BV(TWI0_TWD) | BV(TWI0_TWCK);
|
||||
pmc_periphEnable(TWI0_ID);
|
||||
break;
|
||||
case I2C1:
|
||||
i2c->hw->base = TWI1_BASE;
|
||||
PIO_PERIPH_SEL(TWI1_PORT, BV(TWI1_TWD) | BV(TWI1_TWCK), TWI1_PERIPH);
|
||||
HWREG(TWI1_PORT + PIO_PDR_OFF) = BV(TWI1_TWD) | BV(TWI1_TWCK);
|
||||
pmc_periphEnable(TWI1_ID);
|
||||
break;
|
||||
default:
|
||||
ASSERT(!"i2c: invalid dev number");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Reset and set master mode
|
||||
HWREG(i2c->hw->base + TWI_CR_OFF) = TWI_CR_SWRST;
|
||||
HWREG(i2c->hw->base + TWI_CR_OFF) = TWI_CR_MSEN | TWI_CR_SVDIS;
|
||||
|
||||
i2c_setClock(i2c, clock);
|
||||
}
|
53
bertos/cpu/cortex-m3/drv/i2c_sam3.h
Normal file
53
bertos/cpu/cortex-m3/drv/i2c_sam3.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2011 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief TWI driver for SAM3 (interface)
|
||||
*
|
||||
* \author Stefano Fedrigo <aleph@develer.com>
|
||||
*/
|
||||
|
||||
#ifndef DRV_I2C_SAM3_H
|
||||
#define DRV_I2C_SAM3_H
|
||||
|
||||
/**
|
||||
* \name I2C devices enum
|
||||
*/
|
||||
enum
|
||||
{
|
||||
I2C0,
|
||||
I2C1,
|
||||
|
||||
I2C_CNT /**< Number of ports */
|
||||
};
|
||||
|
||||
#endif /* DRV_I2C_SAM3_H */
|
||||
|
353
bertos/cpu/cortex-m3/drv/i2c_stm32.c
Normal file
353
bertos/cpu/cortex-m3/drv/i2c_stm32.c
Normal file
|
@ -0,0 +1,353 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief STM32F103xx I2C driver.
|
||||
*
|
||||
* \author Daniele Basile <asterix@develer.com>
|
||||
*/
|
||||
|
||||
#include "cfg/cfg_i2c.h"
|
||||
|
||||
#define LOG_LEVEL I2C_LOG_LEVEL
|
||||
#define LOG_FORMAT I2C_LOG_FORMAT
|
||||
#include <cfg/log.h>
|
||||
|
||||
#include <cfg/debug.h>
|
||||
#include <cfg/macros.h> // BV()
|
||||
#include <cfg/module.h>
|
||||
|
||||
#include <cpu/power.h>
|
||||
#include <drv/gpio_stm32.h>
|
||||
#include <drv/irq_cm3.h>
|
||||
#include <drv/clock_stm32.h>
|
||||
#include <drv/i2c.h>
|
||||
#include <drv/timer.h>
|
||||
|
||||
#include <io/stm32.h>
|
||||
|
||||
|
||||
struct I2cHardware
|
||||
{
|
||||
struct stm32_i2c *base;
|
||||
uint32_t clk_i2c_en;
|
||||
uint32_t pin_mask;
|
||||
uint8_t cache[2];
|
||||
bool cached;
|
||||
};
|
||||
|
||||
#define WAIT_BTF(base) \
|
||||
do { \
|
||||
while (!(base->SR1 & BV(SR1_BTF))) \
|
||||
cpu_relax(); \
|
||||
} while (0)
|
||||
|
||||
#define WAIT_RXNE(base) \
|
||||
do { \
|
||||
while (!(base->SR1 & BV(SR1_RXNE))) \
|
||||
cpu_relax(); \
|
||||
} while (0)
|
||||
|
||||
INLINE uint32_t get_status(struct stm32_i2c *base)
|
||||
{
|
||||
return ((base->SR1 | (base->SR2 << 16)) & 0x00FFFFFF);
|
||||
}
|
||||
|
||||
/*
|
||||
* This fuction read the status registers of the i2c device
|
||||
* and waint until the selec event happen. If occur one error
|
||||
* the funtions return false.
|
||||
*/
|
||||
INLINE bool wait_event(I2c *i2c, uint32_t event)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
uint32_t stat = get_status(i2c->hw->base);
|
||||
|
||||
if (stat == event)
|
||||
break;
|
||||
|
||||
if (stat & SR1_ERR_MASK)
|
||||
{
|
||||
i2c->hw->base->SR1 &= ~SR1_ERR_MASK;
|
||||
return false;
|
||||
}
|
||||
cpu_relax();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
INLINE void start_w(struct I2c *i2c, uint16_t slave_addr)
|
||||
{
|
||||
/*
|
||||
* Loop on the select write sequence: when the eeprom is busy
|
||||
* writing previously sent data it will reply to the SLA_W
|
||||
* control byte with a NACK. In this case, we must
|
||||
* keep trying until the eeprom responds with an ACK.
|
||||
*/
|
||||
ticks_t start = timer_clock();
|
||||
while (true)
|
||||
{
|
||||
i2c->hw->base->CR1 |= CR1_ACK_SET | CR1_START_SET;
|
||||
|
||||
if(!wait_event(i2c, I2C_EVENT_MASTER_MODE_SELECT))
|
||||
{
|
||||
LOG_ERR("ARBIT lost\n");
|
||||
i2c->errors |= I2C_ARB_LOST;
|
||||
break;
|
||||
}
|
||||
|
||||
i2c->hw->base->DR = slave_addr & OAR1_ADD0_RESET;
|
||||
|
||||
if(wait_event(i2c, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
|
||||
break;
|
||||
|
||||
if (timer_clock() - start > ms_to_ticks(CONFIG_I2C_START_TIMEOUT))
|
||||
{
|
||||
LOG_ERR("Timeout on I2C START\n");
|
||||
i2c->errors |= I2C_START_TIMEOUT;
|
||||
i2c->hw->base->CR1 |= CR1_STOP_SET;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
INLINE bool start_and_addr(struct I2c *i2c, uint16_t slave_addr)
|
||||
{
|
||||
i2c->hw->base->CR1 |= CR1_START_SET;
|
||||
if(!wait_event(i2c, I2C_EVENT_MASTER_MODE_SELECT))
|
||||
{
|
||||
LOG_ERR("ARBIT lost\n");
|
||||
i2c->errors |= I2C_ARB_LOST;
|
||||
i2c->hw->base->CR1 |= CR1_STOP_SET;
|
||||
return false;
|
||||
}
|
||||
|
||||
i2c->hw->base->DR = (slave_addr | OAR1_ADD0_SET);
|
||||
|
||||
if (i2c->xfer_size == 2)
|
||||
i2c->hw->base->CR1 |= CR1_ACK_SET | CR1_POS_SET;
|
||||
|
||||
if(!wait_event(i2c, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED))
|
||||
{
|
||||
LOG_ERR("SLAR NACK:%08lx\n", get_status(i2c->hw->base));
|
||||
i2c->errors |= I2C_NO_ACK;
|
||||
i2c->hw->base->CR1 |= CR1_STOP_SET;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
INLINE void start_r(struct I2c *i2c, uint16_t slave_addr)
|
||||
{
|
||||
if (!start_and_addr(i2c, slave_addr))
|
||||
return;
|
||||
/*
|
||||
* Due to the hardware receive bytes from slave in automatically mode
|
||||
* we should manage contextually all cases that we want to read one, two or more
|
||||
* than two bytes. To comply this behaviour to our api we shoul bufferd some byte
|
||||
* to hide all special case that needs to use this device.
|
||||
*/
|
||||
if (i2c->xfer_size == 1)
|
||||
{
|
||||
i2c->hw->base->CR1 &= CR1_ACK_RESET;
|
||||
|
||||
cpu_flags_t irq;
|
||||
|
||||
IRQ_SAVE_DISABLE(irq);
|
||||
(void)i2c->hw->base->SR2;
|
||||
if (I2C_TEST_STOP(i2c->flags) == I2C_STOP)
|
||||
i2c->hw->base->CR1 |= CR1_STOP_SET;
|
||||
IRQ_RESTORE(irq);
|
||||
|
||||
WAIT_RXNE(i2c->hw->base);
|
||||
|
||||
i2c->hw->cache[0] = i2c->hw->base->DR;
|
||||
i2c->hw->cached = true;
|
||||
|
||||
if (I2C_TEST_STOP(i2c->flags) == I2C_STOP)
|
||||
while (i2c->hw->base->CR1 & CR1_STOP_SET);
|
||||
|
||||
i2c->hw->base->CR1 |= CR1_ACK_SET;
|
||||
}
|
||||
else if (i2c->xfer_size == 2)
|
||||
{
|
||||
cpu_flags_t irq;
|
||||
|
||||
IRQ_SAVE_DISABLE(irq);
|
||||
(void)i2c->hw->base->SR2;
|
||||
i2c->hw->base->CR1 &= CR1_ACK_RESET;
|
||||
IRQ_RESTORE(irq);
|
||||
|
||||
WAIT_BTF(i2c->hw->base);
|
||||
|
||||
IRQ_SAVE_DISABLE(irq);
|
||||
if (I2C_TEST_STOP(i2c->flags) == I2C_STOP)
|
||||
i2c->hw->base->CR1 |= CR1_STOP_SET;
|
||||
/*
|
||||
* We store read bytes like a fifo..
|
||||
*/
|
||||
i2c->hw->cache[1] = i2c->hw->base->DR;
|
||||
i2c->hw->cache[0] = i2c->hw->base->DR;
|
||||
i2c->hw->cached = true;
|
||||
IRQ_RESTORE(irq);
|
||||
|
||||
i2c->hw->base->CR1 &= CR1_POS_RESET;
|
||||
i2c->hw->base->CR1 |= CR1_ACK_SET;
|
||||
}
|
||||
}
|
||||
|
||||
static void i2c_stm32_start(struct I2c *i2c, uint16_t slave_addr)
|
||||
{
|
||||
i2c->hw->cached = false;
|
||||
|
||||
if (I2C_TEST_START(i2c->flags) == I2C_START_W)
|
||||
start_w(i2c, slave_addr);
|
||||
else /* (I2C_TEST_START(i2c->flags) == I2C_START_R) */
|
||||
start_r(i2c, slave_addr);
|
||||
}
|
||||
|
||||
static void i2c_stm32_putc(I2c *i2c, const uint8_t data)
|
||||
{
|
||||
i2c->hw->base->DR = data;
|
||||
|
||||
WAIT_BTF(i2c->hw->base);
|
||||
|
||||
/* Generate the stop if we finish to send all programmed bytes */
|
||||
if ((i2c->xfer_size == 1) && (I2C_TEST_STOP(i2c->flags) == I2C_STOP))
|
||||
{
|
||||
wait_event(i2c, I2C_EVENT_MASTER_BYTE_TRANSMITTED);
|
||||
i2c->hw->base->CR1 |= CR1_STOP_SET;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t i2c_stm32_getc(I2c *i2c)
|
||||
{
|
||||
if (i2c->hw->cached)
|
||||
{
|
||||
ASSERT(i2c->xfer_size <= 2);
|
||||
return i2c->hw->cache[i2c->xfer_size - 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
WAIT_BTF(i2c->hw->base);
|
||||
|
||||
if (i2c->xfer_size == 3)
|
||||
{
|
||||
i2c->hw->base->CR1 &= CR1_ACK_RESET;
|
||||
|
||||
cpu_flags_t irq;
|
||||
IRQ_SAVE_DISABLE(irq);
|
||||
|
||||
uint8_t data = i2c->hw->base->DR;
|
||||
|
||||
if (I2C_TEST_STOP(i2c->flags) == I2C_STOP)
|
||||
i2c->hw->base->CR1 |= CR1_STOP_SET;
|
||||
|
||||
i2c->hw->cache[1] = i2c->hw->base->DR;
|
||||
|
||||
IRQ_RESTORE(irq);
|
||||
|
||||
WAIT_RXNE(i2c->hw->base);
|
||||
|
||||
i2c->hw->cache[0] = i2c->hw->base->DR;
|
||||
i2c->hw->cached = true;
|
||||
|
||||
if (I2C_TEST_STOP(i2c->flags) == I2C_STOP)
|
||||
while (i2c->hw->base->CR1 & CR1_STOP_SET);
|
||||
|
||||
return data;
|
||||
}
|
||||
else
|
||||
return i2c->hw->base->DR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const I2cVT i2c_stm32_vt =
|
||||
{
|
||||
.start = i2c_stm32_start,
|
||||
.getc = i2c_stm32_getc,
|
||||
.putc = i2c_stm32_putc,
|
||||
.write = i2c_genericWrite,
|
||||
.read = i2c_genericRead,
|
||||
};
|
||||
|
||||
static struct I2cHardware i2c_stm32_hw[] =
|
||||
{
|
||||
{ /* I2C1 */
|
||||
.base = (struct stm32_i2c *)I2C1_BASE,
|
||||
.clk_i2c_en = RCC_APB1_I2C1,
|
||||
.pin_mask = (GPIO_I2C1_SCL_PIN | GPIO_I2C1_SDA_PIN),
|
||||
},
|
||||
{ /* I2C2 */
|
||||
.base = (struct stm32_i2c *)I2C2_BASE,
|
||||
.clk_i2c_en = RCC_APB1_I2C2,
|
||||
.pin_mask = (GPIO_I2C2_SCL_PIN | GPIO_I2C2_SDA_PIN),
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize I2C module.
|
||||
*/
|
||||
void i2c_hw_init(I2c *i2c, int dev, uint32_t clock)
|
||||
{
|
||||
|
||||
i2c->hw = &i2c_stm32_hw[dev];
|
||||
i2c->vt = &i2c_stm32_vt;
|
||||
|
||||
RCC->APB2ENR |= RCC_APB2_GPIOB;
|
||||
RCC->APB1ENR |= i2c->hw->clk_i2c_en;
|
||||
|
||||
/* Set gpio to use I2C driver */
|
||||
stm32_gpioPinConfig((struct stm32_gpio *)GPIOB_BASE, i2c->hw->pin_mask,
|
||||
GPIO_MODE_AF_OD, GPIO_SPEED_50MHZ);
|
||||
|
||||
/* Clear all needed registers */
|
||||
i2c->hw->base->CR1 = 0;
|
||||
i2c->hw->base->CR2 = 0;
|
||||
i2c->hw->base->CCR = 0;
|
||||
i2c->hw->base->TRISE = 0;
|
||||
i2c->hw->base->OAR1 = 0;
|
||||
|
||||
/* Set PCLK1 frequency accornding to the master clock settings. See stm32_clock.c */
|
||||
i2c->hw->base->CR2 |= CR2_FREQ_36MHZ;
|
||||
|
||||
/* Configure spi in standard mode */
|
||||
ASSERT2(clock >= 100000, "fast mode not supported");
|
||||
|
||||
i2c->hw->base->CCR |= (uint16_t)((CR2_FREQ_36MHZ * 1000000) / (clock << 1));
|
||||
i2c->hw->base->TRISE |= (CR2_FREQ_36MHZ + 1);
|
||||
|
||||
i2c->hw->base->CR1 |= CR1_PE_SET;
|
||||
}
|
53
bertos/cpu/cortex-m3/drv/i2c_stm32.h
Normal file
53
bertos/cpu/cortex-m3/drv/i2c_stm32.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief Driver for the STM32F103xx I2C (interface)
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef I2C_STM32_H
|
||||
#define I2C_STM32_H
|
||||
|
||||
#include <drv/i2c.h>
|
||||
|
||||
/**
|
||||
* \name I2C devices enum
|
||||
*/
|
||||
enum
|
||||
{
|
||||
I2C1,
|
||||
I2C2,
|
||||
|
||||
I2C_CNT /**< Number of serial ports */
|
||||
};
|
||||
|
||||
#endif /* I2C_STM32_H */
|
138
bertos/cpu/cortex-m3/drv/irq_cm3.c
Normal file
138
bertos/cpu/cortex-m3/drv/irq_cm3.c
Normal file
|
@ -0,0 +1,138 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief Cortex-M3 IRQ management.
|
||||
*
|
||||
* \author Andrea Righi <arighi@develer.com>
|
||||
*/
|
||||
|
||||
#include "irq_cm3.h"
|
||||
|
||||
#include <cfg/debug.h> /* ASSERT() */
|
||||
#include <cfg/log.h> /* LOG_ERR() */
|
||||
#include <cpu/irq.h>
|
||||
|
||||
|
||||
#ifdef __IAR_SYSTEMS_ICC__
|
||||
#pragma data_alignment=0x400
|
||||
static void (*irq_table[NUM_INTERRUPTS])(void);
|
||||
#else
|
||||
static void (*irq_table[NUM_INTERRUPTS])(void)
|
||||
__attribute__((section("vtable")));
|
||||
#endif
|
||||
|
||||
/* Priority register / IRQ number table */
|
||||
static const uint32_t nvic_prio_reg[] =
|
||||
{
|
||||
/* System exception registers */
|
||||
0, NVIC_SYS_PRI1, NVIC_SYS_PRI2, NVIC_SYS_PRI3,
|
||||
|
||||
/* External interrupts registers */
|
||||
NVIC_PRI0, NVIC_PRI1, NVIC_PRI2, NVIC_PRI3,
|
||||
NVIC_PRI4, NVIC_PRI5, NVIC_PRI6, NVIC_PRI7,
|
||||
NVIC_PRI8, NVIC_PRI9, NVIC_PRI10, NVIC_PRI11,
|
||||
NVIC_PRI12, NVIC_PRI13
|
||||
};
|
||||
|
||||
/* Unhandled IRQ */
|
||||
static NAKED NORETURN void unhandled_isr(void)
|
||||
{
|
||||
register uint32_t reg;
|
||||
|
||||
#ifdef __IAR_SYSTEMS_ICC__
|
||||
reg = CPU_READ_IPSR();
|
||||
#else
|
||||
asm volatile ("mrs %0, ipsr" : "=r"(reg));
|
||||
#endif
|
||||
LOG_ERR("unhandled IRQ %lu\n", reg);
|
||||
while (1)
|
||||
PAUSE;
|
||||
}
|
||||
|
||||
void sysirq_setPriority(sysirq_t irq, int prio)
|
||||
{
|
||||
uint32_t pos = (irq & 3) * 8;
|
||||
reg32_t reg = nvic_prio_reg[irq >> 2];
|
||||
uint32_t val;
|
||||
|
||||
val = HWREG(reg);
|
||||
val &= ~(0xff << pos);
|
||||
val |= prio << pos;
|
||||
HWREG(reg) = val;
|
||||
}
|
||||
|
||||
static void sysirq_enable(sysirq_t irq)
|
||||
{
|
||||
/* Enable the IRQ line (only for generic IRQs) */
|
||||
if (irq >= 16 && irq < 48)
|
||||
NVIC_EN0_R = 1 << (irq - 16);
|
||||
else if (irq >= 48)
|
||||
NVIC_EN1_R = 1 << (irq - 48);
|
||||
}
|
||||
|
||||
void sysirq_setHandler(sysirq_t irq, sysirq_handler_t handler)
|
||||
{
|
||||
cpu_flags_t flags;
|
||||
|
||||
ASSERT(irq < NUM_INTERRUPTS);
|
||||
|
||||
IRQ_SAVE_DISABLE(flags);
|
||||
irq_table[irq] = handler;
|
||||
sysirq_setPriority(irq, IRQ_PRIO);
|
||||
sysirq_enable(irq);
|
||||
IRQ_RESTORE(flags);
|
||||
}
|
||||
|
||||
void sysirq_freeHandler(sysirq_t irq)
|
||||
{
|
||||
cpu_flags_t flags;
|
||||
|
||||
ASSERT(irq < NUM_INTERRUPTS);
|
||||
|
||||
IRQ_SAVE_DISABLE(flags);
|
||||
irq_table[irq] = unhandled_isr;
|
||||
IRQ_RESTORE(flags);
|
||||
}
|
||||
|
||||
void sysirq_init(void)
|
||||
{
|
||||
cpu_flags_t flags;
|
||||
int i;
|
||||
|
||||
IRQ_SAVE_DISABLE(flags);
|
||||
for (i = 0; i < NUM_INTERRUPTS; i++)
|
||||
irq_table[i] = unhandled_isr;
|
||||
|
||||
/* Update NVIC to point to the new vector table */
|
||||
NVIC_VTABLE_R = (size_t)irq_table;
|
||||
IRQ_RESTORE(flags);
|
||||
}
|
63
bertos/cpu/cortex-m3/drv/irq_cm3.h
Normal file
63
bertos/cpu/cortex-m3/drv/irq_cm3.h
Normal file
|
@ -0,0 +1,63 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief IRQ management for the Cortex-M3 processor.
|
||||
*
|
||||
* \author Andrea Righi <arighi@develer.com>
|
||||
*/
|
||||
|
||||
#ifndef DRV_CORTEX_M3_SYSIRQ_H
|
||||
#define DRV_CORTEX_M3_SYSIRQ_H
|
||||
|
||||
#include <cfg/compiler.h>
|
||||
|
||||
#if CPU_CM3_LM3S
|
||||
#include <io/lm3s.h>
|
||||
#elif CPU_CM3_STM32
|
||||
#include <io/stm32.h>
|
||||
#elif CPU_CM3_SAM3
|
||||
#include <io/sam3.h>
|
||||
/*#elif Add other families here */
|
||||
#else
|
||||
#error Unknown CPU
|
||||
#endif
|
||||
|
||||
typedef void (*sysirq_handler_t)(void);
|
||||
typedef unsigned int sysirq_t;
|
||||
|
||||
void sysirq_setHandler(sysirq_t irq, sysirq_handler_t handler);
|
||||
void sysirq_setPriority(sysirq_t irq, int prio);
|
||||
void sysirq_freeHandler(sysirq_t irq);
|
||||
|
||||
void sysirq_init(void);
|
||||
|
||||
#endif /* DRV_CORTEX_M3_SYSIRQ_H */
|
49
bertos/cpu/cortex-m3/drv/kdebug_cm3.c
Normal file
49
bertos/cpu/cortex-m3/drv/kdebug_cm3.c
Normal file
|
@ -0,0 +1,49 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \author Francesco Sacchi <batt@develer.com>
|
||||
*
|
||||
* \brief Low-level kdebug module for Cortex-M3 (inplementation).
|
||||
*/
|
||||
|
||||
#include <cpu/detect.h>
|
||||
|
||||
#if CPU_CM3_LM3S
|
||||
#include "kdebug_lm3s.c"
|
||||
#elif CPU_CM3_STM32
|
||||
#include "kdebug_stm32.c"
|
||||
#elif CPU_CM3_SAM3
|
||||
#include "kdebug_sam3.c"
|
||||
/*#elif Add other families here */
|
||||
#else
|
||||
#error Unknown CPU
|
||||
#endif
|
118
bertos/cpu/cortex-m3/drv/kdebug_lm3s.c
Normal file
118
bertos/cpu/cortex-m3/drv/kdebug_lm3s.c
Normal file
|
@ -0,0 +1,118 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief LM3S debug support (implementation).
|
||||
*
|
||||
* \author Andrea Righi <arighi@develer.com>
|
||||
*/
|
||||
|
||||
#include <cfg/cfg_debug.h>
|
||||
#include <cfg/macros.h> /* for BV() */
|
||||
#include <drv/clock_lm3s.h> /* lm3s_busyWait() */
|
||||
#include <drv/gpio_lm3s.h>
|
||||
#include <drv/ser_lm3s.h>
|
||||
#include "kdebug_lm3s.h"
|
||||
|
||||
#if CONFIG_KDEBUG_PORT == 0
|
||||
#define UART_BASE UART0_BASE
|
||||
#define UART_GPIO_BASE GPIO_PORTA_BASE
|
||||
#define UART_PINS (BV(1) | BV(0))
|
||||
#define UART_REG_SYSCTL SYSCTL_RCGC2_GPIOA
|
||||
#elif CONFIG_KDEBUG_PORT == 1
|
||||
#define UART_BASE UART1_BASE
|
||||
#define UART_GPIO_BASE GPIO_PORTD_BASE
|
||||
#define UART_PINS (BV(3) | BV(2))
|
||||
#define UART_REG_SYSCTL SYSCTL_RCGC2_GPIOD
|
||||
#elif CONFIG_KDEBUG_PORT == 2
|
||||
#define UART_BASE UART2_BASE
|
||||
#define UART_GPIO_BASE GPIO_PORTG_BASE
|
||||
#define UART_PINS (BV(1) | BV(0))
|
||||
#define UART_REG_SYSCTL SYSCTL_RCGC2_GPIOG
|
||||
#else
|
||||
#error "UART port not supported in this board"
|
||||
#endif
|
||||
|
||||
#define KDBG_WAIT_READY() while (!lm3s_uartReady(UART_BASE)) {}
|
||||
#define KDBG_WAIT_TXDONE() while (!lm3s_uartTxDone(UART_BASE)) {}
|
||||
|
||||
#define KDBG_WRITE_CHAR(c) do { lm3s_uartPutCharNonBlocking(UART_BASE, c); } while(0)
|
||||
|
||||
/* Debug unit is used only for debug purposes so does not generate interrupts. */
|
||||
#define KDBG_MASK_IRQ(old) do { (void)old; } while(0)
|
||||
|
||||
/* Debug unit is used only for debug purposes so does not generate interrupts. */
|
||||
#define KDBG_RESTORE_IRQ(old) do { (void)old; } while(0)
|
||||
|
||||
typedef uint32_t kdbg_irqsave_t;
|
||||
|
||||
INLINE void uart_hw_config(void)
|
||||
{
|
||||
unsigned long div, baud = CONFIG_KDEBUG_BAUDRATE;
|
||||
bool hi_speed = false;
|
||||
|
||||
if (baud * 16 > CPU_FREQ)
|
||||
{
|
||||
hi_speed = true;
|
||||
baud /= 2;
|
||||
}
|
||||
div = (CPU_FREQ * 8 / baud + 1) / 2;
|
||||
|
||||
lm3s_uartDisable(UART_BASE);
|
||||
if (hi_speed)
|
||||
HWREG(UART_BASE + UART_O_CTL) |= UART_CTL_HSE;
|
||||
else
|
||||
HWREG(UART_BASE + UART_O_CTL) &= ~UART_CTL_HSE;
|
||||
/* Set the baud rate */
|
||||
HWREG(UART_BASE + UART_O_IBRD) = div / 64;
|
||||
HWREG(UART_BASE + UART_O_FBRD) = div % 64;
|
||||
/* Set word lenght and parity */
|
||||
HWREG(UART_BASE + UART_O_LCRH) = UART_LCRH_WLEN_8;
|
||||
lm3s_uartClear(UART_BASE);
|
||||
lm3s_uartEnable(UART_BASE);
|
||||
}
|
||||
|
||||
INLINE void kdbg_hw_init(void)
|
||||
{
|
||||
uint32_t reg_clock = 1 << CONFIG_KDEBUG_PORT;
|
||||
|
||||
/* Enable the peripheral clock */
|
||||
SYSCTL_RCGC1_R |= reg_clock;
|
||||
SYSCTL_RCGC2_R |= UART_REG_SYSCTL;
|
||||
lm3s_busyWait(512);
|
||||
|
||||
/* Configure GPIO pins to work as UART pins */
|
||||
lm3s_gpioPinConfig(UART_GPIO_BASE, UART_PINS,
|
||||
GPIO_DIR_MODE_HW, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD);
|
||||
|
||||
/* Low-level UART configuration */
|
||||
uart_hw_config();
|
||||
}
|
44
bertos/cpu/cortex-m3/drv/kdebug_lm3s.h
Normal file
44
bertos/cpu/cortex-m3/drv/kdebug_lm3s.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief LM3S debug support (interface).
|
||||
*
|
||||
* \author Andrea Righi <arighi@develer.com>
|
||||
*/
|
||||
|
||||
#ifndef DRV_KDEBUG_LM3S_H
|
||||
#define DRV_KDEBUG_LM3S_H
|
||||
|
||||
#define KDEBUG_PORT_DEFAULT 0 ///< Default debug port.
|
||||
/* \} */
|
||||
|
||||
#endif /* DRV_KDEBUG_LM3S_H */
|
100
bertos/cpu/cortex-m3/drv/kdebug_sam3.c
Normal file
100
bertos/cpu/cortex-m3/drv/kdebug_sam3.c
Normal file
|
@ -0,0 +1,100 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief SAM3 debug support (implementation).
|
||||
*
|
||||
* \author Stefano Fedrigo <aleph@develer.com>
|
||||
*/
|
||||
|
||||
#include <cfg/cfg_debug.h>
|
||||
#include <cfg/macros.h> /* for BV() */
|
||||
|
||||
#include <cpu/types.h>
|
||||
|
||||
#include <io/sam3.h>
|
||||
|
||||
|
||||
#if (CONFIG_KDEBUG_PORT == 0)
|
||||
#define UART_BASE UART0_BASE
|
||||
#define UART_ID UART0_ID
|
||||
#define UART_PIO_BASE UART0_PORT
|
||||
#define UART_PERIPH UART0_PERIPH
|
||||
#define UART_PINS (BV(URXD0) | BV(UTXD0))
|
||||
#elif (CONFIG_KDEBUG_PORT == 1) && UART_PORTS > 1
|
||||
#define UART_BASE UART1_BASE
|
||||
#define UART_ID UART1_ID
|
||||
#define UART_PIO_BASE UART1_PORT
|
||||
#define UART_PERIPH UART1_PERIPH
|
||||
#define UART_PINS (BV(URXD1) | BV(UTXD1))
|
||||
#else
|
||||
#error "UART port not supported in this board"
|
||||
#endif
|
||||
|
||||
// TODO: refactor serial simple functions and use them, see lm3s kdebug
|
||||
#define KDBG_WAIT_READY() while (!(HWREG(UART_BASE + UART_SR_OFF) & BV(UART_SR_TXRDY))) {}
|
||||
#define KDBG_WAIT_TXDONE() while (!(HWREG(UART_BASE + UART_SR_OFF) & BV(UART_SR_TXEMPTY))) {}
|
||||
|
||||
#define KDBG_WRITE_CHAR(c) do { HWREG(UART_BASE + UART_THR_OFF) = (c); } while(0)
|
||||
|
||||
/* Debug unit is used only for debug purposes so does not generate interrupts. */
|
||||
#define KDBG_MASK_IRQ(old) do { (void)old; } while(0)
|
||||
|
||||
/* Debug unit is used only for debug purposes so does not generate interrupts. */
|
||||
#define KDBG_RESTORE_IRQ(old) do { (void)old; } while(0)
|
||||
|
||||
typedef uint32_t kdbg_irqsave_t;
|
||||
|
||||
|
||||
INLINE void kdbg_hw_init(void)
|
||||
{
|
||||
/*
|
||||
* Disable PIO mode and set appropriate UART pins peripheral mode.
|
||||
* SAM3X,A,N,S,U: all of them has all UARTs on peripheral A.
|
||||
*/
|
||||
HWREG(UART_PIO_BASE + PIO_PDR_OFF) = UART_PINS;
|
||||
PIO_PERIPH_SEL(UART_PIO_BASE, UART_PINS, UART_PERIPH);
|
||||
|
||||
/* Enable the peripheral clock */
|
||||
pmc_periphEnable(UART_ID);
|
||||
|
||||
/* Reset and disable receiver & transmitter */
|
||||
HWREG(UART_BASE + UART_CR_OFF) = BV(UART_CR_RSTRX) | BV(UART_CR_RSTTX) | BV(UART_CR_RXDIS) | BV(UART_CR_TXDIS);
|
||||
|
||||
/* Set mode: normal, no parity */
|
||||
HWREG(UART_BASE + UART_MR_OFF) = UART_MR_PAR_NO;
|
||||
|
||||
/* Set baud rate */
|
||||
HWREG(UART_BASE + UART_BRGR_OFF) = CPU_FREQ / CONFIG_KDEBUG_BAUDRATE / 16;
|
||||
|
||||
/* Enable receiver & transmitter */
|
||||
HWREG(UART_BASE + UART_CR_OFF) = BV(UART_CR_RXEN) | BV(UART_CR_TXEN);
|
||||
}
|
103
bertos/cpu/cortex-m3/drv/kdebug_stm32.c
Normal file
103
bertos/cpu/cortex-m3/drv/kdebug_stm32.c
Normal file
|
@ -0,0 +1,103 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief STM32 debug support (implementation).
|
||||
*
|
||||
* \author Andrea Righi <arighi@develer.com>
|
||||
*/
|
||||
|
||||
#include "kdebug_stm32.h"
|
||||
|
||||
#include <cfg/cfg_debug.h>
|
||||
|
||||
#include <drv/gpio_stm32.h>
|
||||
#include <drv/clock_stm32.h> /* RCC */
|
||||
|
||||
#include <io/stm32.h>
|
||||
|
||||
|
||||
|
||||
#if CONFIG_KDEBUG_PORT == 0
|
||||
#define UART_BASE ((struct stm32_usart *)USART1_BASE)
|
||||
#elif CONFIG_KDEBUG_PORT == 1
|
||||
#define UART_BASE ((struct stm32_usart *)USART2_BASE)
|
||||
#elif CONFIG_KDEBUG_PORT == 2
|
||||
#define UART_BASE ((struct stm32_usart *)USART3_BASE)
|
||||
#else
|
||||
#error "UART port not supported in this board"
|
||||
#endif
|
||||
|
||||
#define KDBG_WAIT_READY() while (!(UART_BASE->SR & USART_FLAG_TXE))
|
||||
#define KDBG_WAIT_TXDONE() while (!(UART_BASE->SR & USART_FLAG_TC))
|
||||
|
||||
#define KDBG_WRITE_CHAR(c) do { UART_BASE->DR = (c) & 0x1ff; } while(0)
|
||||
|
||||
/* Debug unit is used only for debug purposes so does not generate interrupts. */
|
||||
#define KDBG_MASK_IRQ(old) do { (void)old; } while(0)
|
||||
|
||||
/* Debug unit is used only for debug purposes so does not generate interrupts. */
|
||||
#define KDBG_RESTORE_IRQ(old) do { (void)old; } while(0)
|
||||
|
||||
typedef uint32_t kdbg_irqsave_t;
|
||||
|
||||
|
||||
/* Initialize UART debug port */
|
||||
INLINE void kdbg_hw_init(void)
|
||||
{
|
||||
/* Enable clocking on AFIO */
|
||||
RCC->APB2ENR |= RCC_APB2_AFIO;
|
||||
/* Configure USART pins */
|
||||
#if CONFIG_KDEBUG_PORT == 0
|
||||
RCC->APB2ENR |= RCC_APB2_GPIOA;
|
||||
RCC->APB2ENR |= RCC_APB2_USART1;
|
||||
stm32_gpioPinConfig((struct stm32_gpio *)GPIOA_BASE, GPIO_USART1_TX_PIN,
|
||||
GPIO_MODE_AF_PP, GPIO_SPEED_50MHZ);
|
||||
#elif CONFIG_KDEBUG_PORT == 1
|
||||
RCC->APB2ENR |= RCC_APB2_GPIOA;
|
||||
RCC->APB1ENR |= RCC_APB1_USART2;
|
||||
stm32_gpioPinConfig((struct stm32_gpio *)GPIOA_BASE, GPIO_USART2_TX_PIN,
|
||||
GPIO_MODE_AF_PP, GPIO_SPEED_50MHZ);
|
||||
#elif CONFIG_KDEBUG_PORT == 2
|
||||
RCC->APB2ENR |= RCC_APB2_GPIOB;
|
||||
RCC->APB1ENR |= RCC_APB1_USART3;
|
||||
stm32_gpioPinConfig((struct stm32_gpio *)GPIOB_BASE, GPIO_USART3_TX_PIN,
|
||||
GPIO_MODE_AF_PP, GPIO_SPEED_50MHZ);
|
||||
#else
|
||||
#error "UART port not supported in this board"
|
||||
#endif
|
||||
/* Enable the USART by writing the UE bit */
|
||||
UART_BASE->CR1 |= CR1_RUN_SET;
|
||||
/* Configure the desired baud rate */
|
||||
UART_BASE->BRR = (uint16_t)evaluate_brr(UART_BASE, CPU_FREQ, CONFIG_KDEBUG_BAUDRATE);
|
||||
/* Set the Transmitter Enable bit in CR1 */
|
||||
UART_BASE->CR1 |= USART_MODE_TX;
|
||||
}
|
44
bertos/cpu/cortex-m3/drv/kdebug_stm32.h
Normal file
44
bertos/cpu/cortex-m3/drv/kdebug_stm32.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief STM32 debug support (interface).
|
||||
*
|
||||
* \author Andrea Righi <arighi@develer.com>
|
||||
*/
|
||||
|
||||
#ifndef DRV_KDEBUG_STM32_H
|
||||
#define DRV_KDEBUG_STM32_H
|
||||
|
||||
#define KDEBUG_PORT_DEFAULT 1 ///< Default debug port.
|
||||
/* \} */
|
||||
|
||||
#endif /* DRV_KDEBUG_STM32_H */
|
272
bertos/cpu/cortex-m3/drv/nand_sam3.c
Normal file
272
bertos/cpu/cortex-m3/drv/nand_sam3.c
Normal file
|
@ -0,0 +1,272 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2011 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief NAND driver hardware implementation for SAM3's static memory controller.
|
||||
*
|
||||
* \author Stefano Fedrigo <aleph@develer.com>
|
||||
*/
|
||||
|
||||
#include <drv/nand.h>
|
||||
#include <cfg/log.h>
|
||||
#include <io/sam3.h>
|
||||
#include <drv/timer.h>
|
||||
#include <cpu/power.h> // cpu_relax()
|
||||
|
||||
|
||||
/*
|
||||
* PIO definitions.
|
||||
*/
|
||||
#define NAND_PIN_CE BV(6)
|
||||
#define NAND_PIN_RB BV(2)
|
||||
#define NAND_PINS_PORTA (NAND_PIN_CE | NAND_PIN_RB)
|
||||
#define NAND_PERIPH_PORTA PIO_PERIPH_B
|
||||
|
||||
#define NAND_PIN_OE BV(19)
|
||||
#define NAND_PIN_WE BV(20)
|
||||
#define NAND_PIN_IO 0x0000FFFF
|
||||
#define NAND_PINS_PORTC (NAND_PIN_OE | NAND_PIN_WE | NAND_PIN_IO)
|
||||
#define NAND_PERIPH_PORTC PIO_PERIPH_A
|
||||
|
||||
#define NAND_PIN_CLE BV(9)
|
||||
#define NAND_PIN_ALE BV(8)
|
||||
#define NAND_PINS_PORTD (NAND_PIN_CLE | NAND_PIN_ALE)
|
||||
#define NAND_PERIPH_PORTD PIO_PERIPH_A
|
||||
|
||||
|
||||
/*
|
||||
* Wait for edge transition of READY/BUSY NAND
|
||||
* signal.
|
||||
* Return true for edge detection, false in case of timeout.
|
||||
*/
|
||||
bool nand_waitReadyBusy(UNUSED_ARG(Nand *, chip), time_t timeout)
|
||||
{
|
||||
time_t start = timer_clock();
|
||||
|
||||
while (!(SMC_SR & SMC_SR_RB_EDGE0))
|
||||
{
|
||||
cpu_relax();
|
||||
if (timer_clock() - start > timeout)
|
||||
{
|
||||
LOG_INFO("nand: R/B timeout\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Wait for transfer to complete until timeout.
|
||||
* If transfer completes return true, false in case of timeout.
|
||||
*/
|
||||
bool nand_waitTransferComplete(UNUSED_ARG(Nand *, chip), time_t timeout)
|
||||
{
|
||||
time_t start = timer_clock();
|
||||
|
||||
while (!(SMC_SR & SMC_SR_XFRDONE))
|
||||
{
|
||||
cpu_relax();
|
||||
if (timer_clock() - start > timeout)
|
||||
{
|
||||
LOG_INFO("nand: xfer complete timeout\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Send command to NAND and wait for completion.
|
||||
*/
|
||||
void nand_sendCommand(Nand *chip,
|
||||
uint32_t cmd1, uint32_t cmd2,
|
||||
int num_cycles, uint32_t cycle0, uint32_t cycle1234)
|
||||
{
|
||||
reg32_t *cmd_addr;
|
||||
uint32_t cmd_val;
|
||||
|
||||
while (HWREG(NFC_CMD_BASE_ADDR + NFC_CMD_NFCCMD) & 0x8000000);
|
||||
|
||||
if (num_cycles == 5)
|
||||
SMC_ADDR = cycle0;
|
||||
|
||||
cmd_val = NFC_CMD_NFCCMD
|
||||
| ((chip->chip_select << NFC_CMD_CSID_SHIFT) & NFC_CMD_CSID_MASK)
|
||||
| ((num_cycles << NFC_CMD_ACYCLE_SHIFT) & NFC_CMD_ACYCLE_MASK)
|
||||
| cmd1 << 2
|
||||
| cmd2 << 10;
|
||||
|
||||
// Check for commands transferring data
|
||||
if (cmd1 == NAND_CMD_WRITE_1 || cmd1 == NAND_CMD_READ_1 || cmd1 == NAND_CMD_READID)
|
||||
cmd_val |= NFC_CMD_NFCEN;
|
||||
|
||||
// Check for commands writing data
|
||||
if (cmd1 == NAND_CMD_WRITE_1)
|
||||
cmd_val |= NFC_CMD_NFCWR;
|
||||
|
||||
// Check for two command cycles
|
||||
if (cmd2)
|
||||
cmd_val |= NFC_CMD_VCMD2;
|
||||
|
||||
cmd_addr = (reg32_t *)(NFC_CMD_BASE_ADDR + cmd_val);
|
||||
*cmd_addr = cycle1234;
|
||||
|
||||
while (!(SMC_SR & SMC_SR_CMDDONE));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Get NAND chip status register.
|
||||
*
|
||||
* NOTE: this is global between different chip selects, so returns
|
||||
* the status register of the last used NAND chip.
|
||||
*/
|
||||
uint8_t nand_getChipStatus(UNUSED_ARG(Nand *, chip))
|
||||
{
|
||||
return (uint8_t)HWREG(NFC_CMD_BASE_ADDR);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return pointer to buffer where data are read to or written from
|
||||
* by nand_sendCommand().
|
||||
*/
|
||||
void *nand_dataBuffer(UNUSED_ARG(Nand *, chip))
|
||||
{
|
||||
return (void *)NFC_SRAM_BASE_ADDR;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Extract ECC data from ECC_PRx registers.
|
||||
*/
|
||||
bool nand_checkEcc(UNUSED_ARG(Nand *, chip))
|
||||
{
|
||||
uint32_t sr1 = SMC_ECC_SR1;
|
||||
if (sr1)
|
||||
{
|
||||
LOG_INFO("ECC error, ECC_SR1=0x%lx\n", sr1);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Compute ECC on data in a buffer.
|
||||
*
|
||||
* \param chip nand context
|
||||
* \param buf buffer containing data
|
||||
* \param size size of data buffer
|
||||
* \param ecc pointer to buffer where computed ECC is stored
|
||||
* \param ecc_size max size for ecc buffer
|
||||
*/
|
||||
void nand_computeEcc(UNUSED_ARG(Nand *, chip),
|
||||
UNUSED_ARG(const void *, buf), UNUSED_ARG(size_t, size), uint32_t *ecc, size_t ecc_size)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < ecc_size; i++)
|
||||
ecc[i] = *((reg32_t *)(SMC_BASE + SMC_ECC_PR0_OFF) + i);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Low-level hardware driver initialization.
|
||||
*/
|
||||
void nand_hwInit(UNUSED_ARG(Nand *, chip))
|
||||
{
|
||||
// FIXME: Parameters specific for MT29F8G08AAD
|
||||
|
||||
// PIO init
|
||||
pmc_periphEnable(PIOA_ID);
|
||||
pmc_periphEnable(PIOC_ID);
|
||||
pmc_periphEnable(PIOD_ID);
|
||||
|
||||
PIO_PERIPH_SEL(PIOA_BASE, NAND_PINS_PORTA, NAND_PERIPH_PORTA);
|
||||
PIOA_PDR = NAND_PINS_PORTA;
|
||||
PIOA_PUER = NAND_PINS_PORTA;
|
||||
|
||||
PIO_PERIPH_SEL(PIOC_BASE, NAND_PINS_PORTC, NAND_PERIPH_PORTC);
|
||||
PIOC_PDR = NAND_PINS_PORTC;
|
||||
PIOC_PUER = NAND_PINS_PORTC;
|
||||
|
||||
PIO_PERIPH_SEL(PIOD_BASE, NAND_PINS_PORTD, NAND_PERIPH_PORTD);
|
||||
PIOD_PDR = NAND_PINS_PORTD;
|
||||
PIOD_PUER = NAND_PINS_PORTD;
|
||||
|
||||
pmc_periphEnable(SMC_SDRAMC_ID);
|
||||
|
||||
// SMC init
|
||||
SMC_SETUP0 = SMC_SETUP_NWE_SETUP(0)
|
||||
| SMC_SETUP_NCS_WR_SETUP(0)
|
||||
| SMC_SETUP_NRD_SETUP(0)
|
||||
| SMC_SETUP_NCS_RD_SETUP(0);
|
||||
|
||||
SMC_PULSE0 = SMC_PULSE_NWE_PULSE(2)
|
||||
| SMC_PULSE_NCS_WR_PULSE(3)
|
||||
| SMC_PULSE_NRD_PULSE(2)
|
||||
| SMC_PULSE_NCS_RD_PULSE(3);
|
||||
|
||||
SMC_CYCLE0 = SMC_CYCLE_NWE_CYCLE(3)
|
||||
| SMC_CYCLE_NRD_CYCLE(3);
|
||||
|
||||
SMC_TIMINGS0 = SMC_TIMINGS_TCLR(1)
|
||||
| SMC_TIMINGS_TADL(6)
|
||||
| SMC_TIMINGS_TAR(4)
|
||||
| SMC_TIMINGS_TRR(2)
|
||||
| SMC_TIMINGS_TWB(9)
|
||||
| SMC_TIMINGS_RBNSEL(7)
|
||||
| SMC_TIMINGS_NFSEL;
|
||||
|
||||
SMC_MODE0 = SMC_MODE_READ_MODE
|
||||
| SMC_MODE_WRITE_MODE;
|
||||
|
||||
SMC_CFG = SMC_CFG_PAGESIZE_PS2048_64
|
||||
| SMC_CFG_EDGECTRL
|
||||
| SMC_CFG_DTOMUL_X1048576
|
||||
| SMC_CFG_DTOCYC(0xF)
|
||||
| SMC_CFG_WSPARE
|
||||
| SMC_CFG_RSPARE;
|
||||
|
||||
// Disable SMC interrupts, reset and enable NFC controller
|
||||
SMC_IDR = ~0;
|
||||
SMC_CTRL = 0;
|
||||
SMC_CTRL = SMC_CTRL_NFCEN;
|
||||
|
||||
// Enable ECC, 1 ECC per 256 bytes
|
||||
SMC_ECC_CTRL = SMC_ECC_CTRL_SWRST;
|
||||
SMC_ECC_MD = SMC_ECC_MD_ECC_PAGESIZE_PS2048_64 | SMC_ECC_MD_TYPCORREC_C256B;
|
||||
}
|
93
bertos/cpu/cortex-m3/drv/random_lm3s.c
Normal file
93
bertos/cpu/cortex-m3/drv/random_lm3s.c
Normal file
|
@ -0,0 +1,93 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief LM3 backend implementation entropy pulling.
|
||||
* \author Giovanni Bajo <rasky@develer.com>
|
||||
*/
|
||||
|
||||
#include "random_p.h"
|
||||
|
||||
#include <cpu/power.h>
|
||||
|
||||
#include <io/cm3.h>
|
||||
|
||||
#include <drv/clock_cm3.h>
|
||||
|
||||
/*
|
||||
* Return the cpu core temperature in raw format
|
||||
*/
|
||||
INLINE uint16_t hw_readRawTemp(void)
|
||||
{
|
||||
/* Trig the temperature sampling */
|
||||
HWREG(ADC0_BASE + ADC_O_PSSI) |= ADC_PSSI_SS3;
|
||||
|
||||
while (!(HWREG(ADC0_BASE + ADC_O_SSFSTAT3) & ADC_SSFSTAT3_FULL))
|
||||
cpu_relax();
|
||||
|
||||
return (uint16_t)HWREG(ADC0_BASE + ADC_O_SSFIFO3);
|
||||
}
|
||||
|
||||
INLINE void hw_initIntTemp(void)
|
||||
{
|
||||
SYSCTL_RCGC0_R |= SYSCTL_RCGC0_ADC0;
|
||||
|
||||
lm3s_busyWait(10);
|
||||
|
||||
/* Disable all sequence */
|
||||
HWREG(ADC0_BASE + ADC_O_ACTSS) = 0;
|
||||
/* Set trigger event to programmed (for all sequence) */
|
||||
HWREG(ADC0_BASE + ADC_O_EMUX) = 0;
|
||||
/* Enalbe read of temperature sensor */
|
||||
HWREG(ADC0_BASE + ADC_O_SSCTL3) |= ADC_SSCTL3_TS0;
|
||||
/* Enable sequence S03 (single sample on select channel) */
|
||||
HWREG(ADC0_BASE + ADC_O_ACTSS) |= ADC_ACTSS_ASEN3;
|
||||
}
|
||||
|
||||
|
||||
void random_pull_entropy(uint8_t *entropy, size_t len)
|
||||
{
|
||||
// We use the internal temperature sensor of LM3S as a source of entropy.
|
||||
// The last bit of the acquisition is very variable and with a decent distribution
|
||||
// to consider it "entropic". It does not really matter because it will
|
||||
// go through a randomness extractor anyway.
|
||||
hw_initIntTemp();
|
||||
|
||||
for (size_t j=0; j<len; j++)
|
||||
{
|
||||
uint8_t accum = 0;
|
||||
for (int b=0; b<8; ++b)
|
||||
if (hw_readRawTemp() & 1)
|
||||
accum |= 1<<b;
|
||||
|
||||
*entropy++ = accum;
|
||||
}
|
||||
}
|
109
bertos/cpu/cortex-m3/drv/random_stm32.c
Normal file
109
bertos/cpu/cortex-m3/drv/random_stm32.c
Normal file
|
@ -0,0 +1,109 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief STM32 backend implementation entropy pulling.
|
||||
* \author Daniele Basile <asterix@develer.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sec/random_p.h>
|
||||
|
||||
#include <cpu/power.h>
|
||||
|
||||
#include <drv/clock_cm3.h>
|
||||
|
||||
#include <io/cm3.h>
|
||||
|
||||
struct stm32_adc *adc = (struct stm32_adc *)ADC1_BASE;
|
||||
|
||||
/*
|
||||
* Return the cpu core temperature in raw format
|
||||
*/
|
||||
INLINE uint16_t hw_readRawTemp(void)
|
||||
{
|
||||
/* We sample only from one channel */
|
||||
adc->SQR1 |= BV(SQR1_SQ_LEN_SHIFT);
|
||||
adc->SQR3 = (ADC_TEMP_CH & SQR3_SQ_MASK);
|
||||
|
||||
/* Start convertion */
|
||||
adc->CR2 |= CR2_EXTTRIG_SWSTRT_SET;
|
||||
|
||||
/* Wait in polling until conversion is done */
|
||||
while (!(adc->SR & BV(SR_EOC)))
|
||||
cpu_relax();
|
||||
|
||||
/* Return the last converted data */
|
||||
return (uint16_t)adc->DR;
|
||||
}
|
||||
|
||||
INLINE void hw_initIntTemp(void)
|
||||
{
|
||||
RCC->APB2ENR |= RCC_APB2_ADC1;
|
||||
|
||||
/* Reset registry */
|
||||
adc->CR1 = 0;
|
||||
adc->CR2 = 0;
|
||||
adc->SQR1 = 0;
|
||||
adc->SQR2 = 0;
|
||||
adc->SQR3 = 0;
|
||||
|
||||
/*
|
||||
* Configure ADC
|
||||
* - Regular mode
|
||||
* - Wake up adc
|
||||
* - Wake up temperature and Vrefint
|
||||
*/
|
||||
adc->CR2 |= BV(CR2_ADON) | ADC_EXTERNALTRIGCONV_NONE | BV(CR2_TSVREFE);
|
||||
|
||||
/* Set 17.1usec sampling time*/
|
||||
adc->SMPR1 |= ((ADC_SAMPLETIME_239CYCLES5 << SMPR1_CH17) | (ADC_SAMPLETIME_239CYCLES5 << SMPR1_CH16));
|
||||
}
|
||||
|
||||
|
||||
void random_pull_entropy(uint8_t *entropy, size_t len)
|
||||
{
|
||||
// We use the internal temperature sensor of LM3S as a source of entropy.
|
||||
// The last bit of the acquisition is very variable and with a decent distribution
|
||||
// to consider it "entropic". It does not really matter because it will
|
||||
// go through a randomness extractor anyway.
|
||||
hw_initIntTemp();
|
||||
|
||||
for (size_t j=0; j<len; j++)
|
||||
{
|
||||
uint8_t accum = 0;
|
||||
for (int b=0; b<8; ++b)
|
||||
if (hw_readRawTemp() & 1)
|
||||
accum |= 1<<b;
|
||||
|
||||
*entropy++ = accum;
|
||||
}
|
||||
}
|
152
bertos/cpu/cortex-m3/drv/rtc_stm32.c
Normal file
152
bertos/cpu/cortex-m3/drv/rtc_stm32.c
Normal file
|
@ -0,0 +1,152 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2011 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief STM32 RTC driver.
|
||||
*
|
||||
* \author Andrea Righi <arighi@develer.com>
|
||||
*/
|
||||
|
||||
#include "clock_stm32.h"
|
||||
|
||||
#include <cfg/compiler.h>
|
||||
#include <cfg/module.h>
|
||||
#include <cfg/debug.h>
|
||||
|
||||
#include <io/stm32.h>
|
||||
#include <io/stm32_pwr.h>
|
||||
|
||||
#include <cpu/power.h> // cpu_relax()
|
||||
|
||||
#include <drv/rtc.h>
|
||||
|
||||
/* PWR registers base */
|
||||
static struct PWR *PWR = (struct PWR *)PWR_BASE;
|
||||
|
||||
/* RTC clock source: LSE */
|
||||
#define RTC_CLKSRC 0x00000100
|
||||
/* RTC clock: 32768 Hz */
|
||||
#define RTC_CLOCK 32768
|
||||
/* RTC clock period (in ms) */
|
||||
#define RTC_PERIOD 1000
|
||||
|
||||
/* RTC control register */
|
||||
#define RTC_CRH (*(reg16_t *)(RTC_BASE + 0x00))
|
||||
#define RTC_CRL (*(reg16_t *)(RTC_BASE + 0x04))
|
||||
|
||||
#define RTC_CRL_SECIE BV(0)
|
||||
#define RTC_CRL_ALRIE BV(1)
|
||||
#define RTC_CRL_OWIE BV(2)
|
||||
|
||||
#define RTC_CRL_SECF BV(0)
|
||||
#define RTC_CRL_ALRF BV(1)
|
||||
#define RTC_CRL_OWF BV(2)
|
||||
#define RTC_CRL_RSF BV(3)
|
||||
#define RTC_CRL_CNF BV(4)
|
||||
#define RTC_CRL_RTOFF BV(5)
|
||||
|
||||
/* RTC prescaler load register */
|
||||
#define RTC_PRLH (*(reg16_t *)(RTC_BASE + 0x08))
|
||||
#define RTC_PRLL (*(reg16_t *)(RTC_BASE + 0x0c))
|
||||
|
||||
/* RTC prescaler divider register */
|
||||
#define RTC_DIVH (*(reg16_t *)(RTC_BASE + 0x10))
|
||||
#define RTC_DIVL (*(reg16_t *)(RTC_BASE + 0x14))
|
||||
|
||||
/* RTC counter register */
|
||||
#define RTC_CNTH (*(reg16_t *)(RTC_BASE + 0x18))
|
||||
#define RTC_CNTL (*(reg16_t *)(RTC_BASE + 0x1c))
|
||||
|
||||
/* RTC alarm register */
|
||||
#define RTC_ALRH (*(reg16_t *)(RTC_BASE + 0x20))
|
||||
#define RTC_ALRL (*(reg16_t *)(RTC_BASE + 0x24))
|
||||
|
||||
static void rtc_enterConfig(void)
|
||||
{
|
||||
/* Enter configuration mode */
|
||||
RTC_CRL |= RTC_CRL_CNF;
|
||||
}
|
||||
|
||||
static void rtc_exitConfig(void)
|
||||
{
|
||||
/* Exit from configuration mode */
|
||||
RTC_CRL &= ~RTC_CRL_CNF;
|
||||
while (!(RTC_CRL & RTC_CRL_RTOFF))
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
uint32_t rtc_time(void)
|
||||
{
|
||||
return (RTC_CNTH << 16) | RTC_CNTL;
|
||||
}
|
||||
|
||||
void rtc_setTime(uint32_t val)
|
||||
{
|
||||
rtc_enterConfig();
|
||||
RTC_CNTH = (val >> 16) & 0xffff;
|
||||
RTC_CNTL = val & 0xffff;
|
||||
rtc_exitConfig();
|
||||
}
|
||||
|
||||
/* Initialize the RTC clock */
|
||||
int rtc_init(void)
|
||||
{
|
||||
#if CONFIG_KERN
|
||||
MOD_CHECK(proc);
|
||||
#endif
|
||||
/* Enable clock for Power interface */
|
||||
RCC->APB1ENR |= RCC_APB1_PWR;
|
||||
|
||||
/* Enable access to RTC registers */
|
||||
PWR->CR |= PWR_CR_DBP;
|
||||
|
||||
/* Enable LSE */
|
||||
RCC->BDCR |= RCC_BDCR_LSEON;
|
||||
/* Wait for LSE ready */
|
||||
while (!(RCC->BDCR & RCC_BDCR_LSERDY))
|
||||
cpu_relax();
|
||||
|
||||
/* Set clock source and enable RTC peripheral */
|
||||
RCC->BDCR |= RTC_CLKSRC | RCC_BDCR_RTCEN;
|
||||
|
||||
rtc_enterConfig();
|
||||
|
||||
/* Set prescaler */
|
||||
RTC_PRLH = ((RTC_PERIOD * RTC_CLOCK / 1000 - 1) >> 16) & 0xff;
|
||||
RTC_PRLL = ((RTC_PERIOD * RTC_CLOCK / 1000 - 1)) & 0xffff;
|
||||
|
||||
rtc_exitConfig();
|
||||
|
||||
/* Disable access to the RTC registers */
|
||||
PWR->CR &= ~PWR_CR_DBP;
|
||||
|
||||
return 0;
|
||||
}
|
49
bertos/cpu/cortex-m3/drv/ser_cm3.h
Normal file
49
bertos/cpu/cortex-m3/drv/ser_cm3.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief Low-level serial module for Cortex-M3 (interface).
|
||||
*
|
||||
* \author Andrea Righi <arighi@develer.com>
|
||||
*/
|
||||
|
||||
#include <cpu/detect.h>
|
||||
|
||||
#if CPU_CM3_LM3S
|
||||
#include "ser_lm3s.h"
|
||||
#elif CPU_CM3_STM32
|
||||
#include "ser_stm32.h"
|
||||
#elif CPU_CM3_SAM3
|
||||
#include "ser_sam3.h"
|
||||
/*#elif Add other Cortex-M3 CPUs here */
|
||||
#else
|
||||
#error Unknown CPU
|
||||
#endif
|
369
bertos/cpu/cortex-m3/drv/ser_lm3s.c
Normal file
369
bertos/cpu/cortex-m3/drv/ser_lm3s.c
Normal file
|
@ -0,0 +1,369 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief LM3S1968 UART interface driver.
|
||||
*
|
||||
* \author Andrea Righi <arighi@develer.com>
|
||||
*/
|
||||
|
||||
#include <cfg/macros.h> /* for BV() */
|
||||
#include <drv/gpio_lm3s.h>
|
||||
#include <drv/ser_p.h>
|
||||
#include <drv/ser.h>
|
||||
#include <drv/irq_cm3.h>
|
||||
#include "cfg/cfg_ser.h"
|
||||
#include "ser_lm3s.h"
|
||||
|
||||
/* From the high-level serial driver */
|
||||
extern struct Serial *ser_handles[SER_CNT];
|
||||
|
||||
struct CM3Serial
|
||||
{
|
||||
struct SerialHardware hw;
|
||||
bool sending;
|
||||
uint32_t base;
|
||||
sysirq_t irq;
|
||||
};
|
||||
|
||||
/* Forward declaration */
|
||||
static struct CM3Serial UARTDesc[SER_CNT];
|
||||
|
||||
/* GPIO descriptor for UART pins */
|
||||
struct gpio_uart_info
|
||||
{
|
||||
/* Sysctl */
|
||||
uint32_t sysctl;
|
||||
/* GPIO base address register */
|
||||
uint32_t base;
|
||||
/* Pin(s) bitmask */
|
||||
uint8_t pins;
|
||||
};
|
||||
|
||||
/* Table to retrieve GPIO pins configuration to work as UART pins */
|
||||
static const struct gpio_uart_info gpio_uart[SER_CNT] =
|
||||
{
|
||||
/* UART0 */
|
||||
{
|
||||
.base = GPIO_PORTA_BASE,
|
||||
.pins = BV(1) | BV(0),
|
||||
.sysctl = SYSCTL_RCGC2_GPIOA,
|
||||
},
|
||||
/* UART1 */
|
||||
{
|
||||
.base = GPIO_PORTD_BASE,
|
||||
.pins = BV(3) | BV(2),
|
||||
.sysctl = SYSCTL_RCGC2_GPIOD,
|
||||
},
|
||||
/* UART2 */
|
||||
{
|
||||
.base = GPIO_PORTG_BASE,
|
||||
.pins = BV(1) | BV(0),
|
||||
.sysctl = SYSCTL_RCGC2_GPIOG,
|
||||
},
|
||||
};
|
||||
|
||||
void lm3s_uartSetBaudRate(uint32_t base, unsigned long baud)
|
||||
{
|
||||
unsigned long div;
|
||||
bool hi_speed;
|
||||
|
||||
if (baud * 16 > CPU_FREQ)
|
||||
{
|
||||
hi_speed = true;
|
||||
baud /= 2;
|
||||
}
|
||||
div = (CPU_FREQ * 8 / baud + 1) / 2;
|
||||
|
||||
lm3s_uartDisable(base);
|
||||
if (hi_speed)
|
||||
HWREG(base + UART_O_CTL) |= UART_CTL_HSE;
|
||||
else
|
||||
HWREG(base + UART_O_CTL) &= ~UART_CTL_HSE;
|
||||
/* Set the baud rate */
|
||||
HWREG(base + UART_O_IBRD) = div / 64;
|
||||
HWREG(base + UART_O_FBRD) = div % 64;
|
||||
lm3s_uartClear(base);
|
||||
lm3s_uartEnable(base);
|
||||
}
|
||||
|
||||
void lm3s_uartSetParity(uint32_t base, int parity)
|
||||
{
|
||||
/* Set 8-bit word, one stop bit by default */
|
||||
uint32_t config = UART_LCRH_WLEN_8;
|
||||
|
||||
switch(parity)
|
||||
{
|
||||
case SER_PARITY_NONE:
|
||||
break;
|
||||
case SER_PARITY_ODD:
|
||||
config |= UART_LCRH_PEN;
|
||||
break;
|
||||
case SER_PARITY_EVEN:
|
||||
config |= UART_LCRH_EPS | UART_LCRH_PEN;
|
||||
break;
|
||||
default:
|
||||
ASSERT(0);
|
||||
return;
|
||||
}
|
||||
lm3s_uartDisable(base);
|
||||
HWREG(base + UART_O_LCRH) = config;
|
||||
lm3s_uartClear(base);
|
||||
lm3s_uartEnable(base);
|
||||
}
|
||||
|
||||
void lm3s_uartInit(int port)
|
||||
{
|
||||
uint32_t reg_clock, base;
|
||||
|
||||
ASSERT(port >= 0 && port < SER_CNT);
|
||||
|
||||
base = UARTDesc[port].base;
|
||||
reg_clock = 1 << port;
|
||||
|
||||
/* Enable the peripheral clock */
|
||||
SYSCTL_RCGC1_R |= reg_clock;
|
||||
SYSCTL_RCGC2_R |= gpio_uart[port].sysctl;
|
||||
lm3s_busyWait(512);
|
||||
|
||||
/* Configure GPIO pins to work as UART pins */
|
||||
lm3s_gpioPinConfig(gpio_uart[port].base, gpio_uart[port].pins,
|
||||
GPIO_DIR_MODE_HW, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD);
|
||||
|
||||
/* Set serial param: 115.200 bps, no parity */
|
||||
lm3s_uartSetBaudRate(base, 115200);
|
||||
lm3s_uartSetParity(base, SER_PARITY_NONE);
|
||||
}
|
||||
|
||||
static bool tx_sending(struct SerialHardware *_hw)
|
||||
{
|
||||
struct CM3Serial *hw = (struct CM3Serial *)_hw;
|
||||
return hw->sending;
|
||||
}
|
||||
|
||||
static void uart_irq_rx(int port)
|
||||
{
|
||||
struct FIFOBuffer *rxfifo = &ser_handles[port]->rxfifo;
|
||||
uint32_t base = UARTDesc[port].base;
|
||||
char c;
|
||||
|
||||
while (lm3s_uartRxReady(base))
|
||||
{
|
||||
c = HWREG(base + UART_O_DR);
|
||||
if (fifo_isfull(rxfifo))
|
||||
ser_handles[port]->status |= SERRF_RXFIFOOVERRUN;
|
||||
else
|
||||
fifo_push(rxfifo, c);
|
||||
}
|
||||
}
|
||||
|
||||
static void uart_irq_tx(int port)
|
||||
{
|
||||
struct FIFOBuffer *txfifo = &ser_handles[port]->txfifo;
|
||||
uint32_t base = UARTDesc[port].base;
|
||||
|
||||
while (lm3s_uartTxReady(base))
|
||||
{
|
||||
if (fifo_isempty(txfifo))
|
||||
{
|
||||
/*
|
||||
* Disable TX empty interrupts if there're no more
|
||||
* characters to transmit.
|
||||
*/
|
||||
HWREG(base + UART_O_IM) &= ~UART_IM_TXIM;
|
||||
UARTDesc[port].sending = false;
|
||||
break;
|
||||
}
|
||||
HWREG(base + UART_O_DR) = fifo_pop(txfifo);
|
||||
}
|
||||
}
|
||||
|
||||
static void uart_common_irq_handler(int port)
|
||||
{
|
||||
uint32_t base = UARTDesc[port].base;
|
||||
uint32_t status;
|
||||
|
||||
/* Read and clear the IRQ status */
|
||||
status = HWREG(base + UART_O_RIS);
|
||||
|
||||
/* Process the IRQ */
|
||||
if (status & (UART_RIS_RXRIS | UART_RIS_RTRIS))
|
||||
uart_irq_rx(port);
|
||||
if (status & UART_RIS_TXRIS)
|
||||
uart_irq_tx(port);
|
||||
}
|
||||
|
||||
static void lm3s_uartIRQEnable(int port, sysirq_handler_t handler)
|
||||
{
|
||||
uint32_t base = UARTDesc[port].base;
|
||||
sysirq_t irq = UARTDesc[port].irq;
|
||||
|
||||
/* Register the IRQ handler */
|
||||
sysirq_setHandler(irq, handler);
|
||||
/* Enable RX interrupt in the UART interrupt mask register */
|
||||
HWREG(base + UART_O_IM) |= UART_IM_RXIM | UART_IM_RTIM;
|
||||
}
|
||||
|
||||
static void lm3s_uartIRQDisable(int port)
|
||||
{
|
||||
uint32_t base = UARTDesc[port].base;
|
||||
|
||||
HWREG(base + UART_O_IM) &=
|
||||
~(UART_IM_TXIM | UART_IM_RXIM | UART_IM_RTIM);
|
||||
}
|
||||
|
||||
/* UART class definition */
|
||||
#define UART_PORT(port) \
|
||||
/* UART TX and RX buffers */ \
|
||||
static unsigned char \
|
||||
uart ## port ## _txbuffer[CONFIG_UART ## port ## _TXBUFSIZE]; \
|
||||
static unsigned char \
|
||||
uart ## port ## _rxbuffer[CONFIG_UART ## port ## _RXBUFSIZE]; \
|
||||
\
|
||||
/* UART interrupt handler */ \
|
||||
static DECLARE_ISR(uart ## port ## _irq_handler) \
|
||||
{ \
|
||||
uart_common_irq_handler(port); \
|
||||
} \
|
||||
\
|
||||
/* UART public methods */ \
|
||||
static void \
|
||||
uart ## port ## _txStart(struct SerialHardware *_hw) \
|
||||
{ \
|
||||
struct FIFOBuffer *txfifo = &ser_handles[port]->txfifo; \
|
||||
struct CM3Serial *hw = (struct CM3Serial *)_hw; \
|
||||
\
|
||||
if (hw->sending) \
|
||||
return; \
|
||||
lm3s_uartPutChar(UART ## port ## _BASE, fifo_pop(txfifo)); \
|
||||
if (!fifo_isempty(txfifo)) \
|
||||
{ \
|
||||
HWREG(UART ## port ## _BASE + UART_O_IM) |= \
|
||||
UART_IM_TXIM; \
|
||||
hw->sending = true; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
static void \
|
||||
uart ## port ## _setbaudrate(UNUSED_ARG(struct SerialHardware *, hw), \
|
||||
unsigned long baud) \
|
||||
{ \
|
||||
lm3s_uartSetBaudRate(UART ## port ## _BASE, baud); \
|
||||
} \
|
||||
\
|
||||
static void \
|
||||
uart ## port ## _setparity(UNUSED_ARG(struct SerialHardware *, hw), \
|
||||
int parity) \
|
||||
{ \
|
||||
lm3s_uartSetParity(UART ## port ## _BASE, parity); \
|
||||
} \
|
||||
\
|
||||
static void \
|
||||
uart ## port ## _cleanup(struct SerialHardware *_hw) \
|
||||
{ \
|
||||
struct CM3Serial *hw = (struct CM3Serial *)_hw; \
|
||||
\
|
||||
hw->sending = false; \
|
||||
lm3s_uartIRQDisable(port); \
|
||||
lm3s_uartClear(UART ## port ## _BASE); \
|
||||
lm3s_uartDisable(UART ## port ## _BASE); \
|
||||
} \
|
||||
\
|
||||
static void \
|
||||
uart ## port ## _init(UNUSED_ARG(struct SerialHardware *, hw), \
|
||||
UNUSED_ARG(struct Serial *, ser)) \
|
||||
{ \
|
||||
lm3s_uartInit(port); \
|
||||
lm3s_uartEnable(UART ## port ## _BASE); \
|
||||
lm3s_uartIRQEnable(port, uart ## port ## _irq_handler); \
|
||||
} \
|
||||
\
|
||||
/* UART operations */ \
|
||||
static const struct SerialHardwareVT UART ## port ## _VT = \
|
||||
{ \
|
||||
.init = uart ## port ## _init, \
|
||||
.cleanup = uart ## port ## _cleanup, \
|
||||
.setBaudrate = uart ## port ## _setbaudrate, \
|
||||
.setParity = uart ## port ## _setparity, \
|
||||
.txStart = uart ## port ## _txStart, \
|
||||
.txSending = tx_sending, \
|
||||
};
|
||||
|
||||
/* UART port instances */
|
||||
UART_PORT(0)
|
||||
UART_PORT(1)
|
||||
UART_PORT(2)
|
||||
|
||||
static struct CM3Serial UARTDesc[SER_CNT] =
|
||||
{
|
||||
{
|
||||
.hw = {
|
||||
.table = &UART0_VT,
|
||||
.txbuffer = uart0_txbuffer,
|
||||
.rxbuffer = uart0_rxbuffer,
|
||||
.txbuffer_size = sizeof(uart0_txbuffer),
|
||||
.rxbuffer_size = sizeof(uart0_rxbuffer),
|
||||
},
|
||||
.sending = false,
|
||||
.base = UART0_BASE,
|
||||
.irq = INT_UART0,
|
||||
},
|
||||
{
|
||||
.hw = {
|
||||
.table = &UART1_VT,
|
||||
.txbuffer = uart1_txbuffer,
|
||||
.rxbuffer = uart1_rxbuffer,
|
||||
.txbuffer_size = sizeof(uart1_txbuffer),
|
||||
.rxbuffer_size = sizeof(uart1_rxbuffer),
|
||||
},
|
||||
.sending = false,
|
||||
.base = UART1_BASE,
|
||||
.irq = INT_UART1,
|
||||
},
|
||||
{
|
||||
.hw = {
|
||||
.table = &UART2_VT,
|
||||
.txbuffer = uart2_txbuffer,
|
||||
.rxbuffer = uart2_rxbuffer,
|
||||
.txbuffer_size = sizeof(uart2_txbuffer),
|
||||
.rxbuffer_size = sizeof(uart2_rxbuffer),
|
||||
},
|
||||
.sending = false,
|
||||
.base = UART2_BASE,
|
||||
.irq = INT_UART2,
|
||||
},
|
||||
};
|
||||
|
||||
struct SerialHardware *ser_hw_getdesc(int port)
|
||||
{
|
||||
ASSERT(port >= 0 && port < SER_CNT);
|
||||
return &UARTDesc[port].hw;
|
||||
}
|
154
bertos/cpu/cortex-m3/drv/ser_lm3s.h
Normal file
154
bertos/cpu/cortex-m3/drv/ser_lm3s.h
Normal file
|
@ -0,0 +1,154 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief LM3S1968 UART interface driver.
|
||||
*
|
||||
* \author Andrea Righi <arighi@develer.com>
|
||||
*/
|
||||
|
||||
#ifndef SER_LM3S_H
|
||||
#define SER_LM3S_H
|
||||
|
||||
#include <cfg/cfg_debug.h>
|
||||
#include <cpu/power.h> /* cpu_relax() */
|
||||
#include <drv/clock_lm3s.h> /* lm3s_busyWait() */
|
||||
#include <io/lm3s.h>
|
||||
|
||||
/* Serial hardware numbers */
|
||||
enum
|
||||
{
|
||||
SER_UART0,
|
||||
SER_UART1,
|
||||
SER_UART2,
|
||||
|
||||
SER_CNT //< Number of serial ports
|
||||
};
|
||||
|
||||
/* Software errors */
|
||||
#define SERRF_RXFIFOOVERRUN BV(0) //< Rx FIFO buffer overrun
|
||||
#define SERRF_RXTIMEOUT BV(1) //< Receive timeout
|
||||
#define SERRF_TXTIMEOUT BV(2) //< Transmit timeout
|
||||
|
||||
/*
|
||||
* Hardware errors.
|
||||
*/
|
||||
#define SERRF_RXSROVERRUN 0 //< Input overrun
|
||||
#define SERRF_FRAMEERROR 0 //< Stop bit missing
|
||||
#define SERRF_PARITYERROR 0 //< Parity error
|
||||
#define SERRF_NOISEERROR 0 //< Noise error
|
||||
|
||||
/* Serial error/status flags */
|
||||
typedef uint32_t serstatus_t;
|
||||
|
||||
INLINE void lm3s_uartDisable(uint32_t base)
|
||||
{
|
||||
/* Disable the hardware FIFO */
|
||||
HWREG(base + UART_O_LCRH) &= ~UART_LCRH_FEN;
|
||||
|
||||
/* Disable the UART */
|
||||
HWREG(base + UART_O_CTL) &=
|
||||
~(UART_CTL_UARTEN | UART_CTL_TXE | UART_CTL_RXE);
|
||||
lm3s_busyWait(512);
|
||||
}
|
||||
|
||||
INLINE void lm3s_uartEnable(uint32_t base)
|
||||
{
|
||||
/* Enable the hardware FIFO */
|
||||
HWREG(base + UART_O_LCRH) |= UART_LCRH_FEN;
|
||||
|
||||
/* Enable RX, TX, and the UART */
|
||||
HWREG(base + UART_O_CTL) |=
|
||||
UART_CTL_UARTEN | UART_CTL_TXE | UART_CTL_RXE;
|
||||
lm3s_busyWait(512);
|
||||
}
|
||||
|
||||
/* Clear the flags register */
|
||||
INLINE void lm3s_uartClear(uint32_t base)
|
||||
{
|
||||
HWREG(base + UART_O_FR) = 0;
|
||||
}
|
||||
|
||||
INLINE bool lm3s_uartTxDone(uint32_t base)
|
||||
{
|
||||
return HWREG(base + UART_O_FR) & UART_FR_TXFE ? true : false;
|
||||
}
|
||||
|
||||
INLINE bool lm3s_uartTxReady(uint32_t base)
|
||||
{
|
||||
return HWREG(base + UART_O_FR) & UART_FR_TXFF ? false : true;
|
||||
}
|
||||
|
||||
INLINE bool lm3s_uartRxReady(uint32_t base)
|
||||
{
|
||||
return HWREG(base + UART_O_FR) & UART_FR_RXFE ? false : true;
|
||||
}
|
||||
|
||||
INLINE bool lm3s_uartReady(uint32_t base)
|
||||
{
|
||||
return HWREG(base + UART_O_FR) & UART_FR_BUSY ? false : true;
|
||||
}
|
||||
|
||||
INLINE int lm3s_uartPutCharNonBlocking(uint32_t base, unsigned char c)
|
||||
{
|
||||
if (!lm3s_uartTxReady(base))
|
||||
return EOF;
|
||||
HWREG(base + UART_O_DR) = c;
|
||||
return c;
|
||||
}
|
||||
|
||||
INLINE int lm3s_uartPutChar(uint32_t base, unsigned char c)
|
||||
{
|
||||
while (!lm3s_uartTxReady(base))
|
||||
cpu_relax();
|
||||
HWREG(base + UART_O_DR) = c;
|
||||
return c;
|
||||
}
|
||||
|
||||
INLINE int lm3s_uartGetCharNonBlocking(uint32_t base)
|
||||
{
|
||||
if (!lm3s_uartRxReady(base))
|
||||
return EOF;
|
||||
return HWREG(base + UART_O_DR);
|
||||
}
|
||||
|
||||
INLINE int lm3s_uartGetChar(uint32_t base)
|
||||
{
|
||||
while (!lm3s_uartRxReady(base))
|
||||
cpu_relax();
|
||||
return HWREG(base + UART_O_DR);
|
||||
}
|
||||
|
||||
void lm3s_uartSetBaudRate(uint32_t base, unsigned long baud);
|
||||
void lm3s_uartSetParity(uint32_t base, int parity);
|
||||
void lm3s_uartInit(int port);
|
||||
|
||||
#endif /* SER_LM3S_H */
|
996
bertos/cpu/cortex-m3/drv/ser_sam3.c
Normal file
996
bertos/cpu/cortex-m3/drv/ser_sam3.c
Normal file
|
@ -0,0 +1,996 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2003, 2004 Develer S.r.l. (http://www.develer.com/)
|
||||
* Copyright 2000 Bernie Innocenti <bernie@codewiz.org>
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief ARM UART and SPI I/O driver
|
||||
*
|
||||
*
|
||||
* \author Daniele Basile <asterix@develer.com>
|
||||
* \author Stefano Fedrigo <aleph@develer.com>
|
||||
*/
|
||||
|
||||
#include "hw/hw_ser.h" /* Required for bus macros overrides */
|
||||
#include <hw/hw_cpufreq.h> /* CPU_FREQ */
|
||||
|
||||
#include "cfg/cfg_ser.h"
|
||||
#include <cfg/debug.h>
|
||||
|
||||
|
||||
//#include <io/arm.h>
|
||||
#include <io/cm3.h>
|
||||
#include <drv/irq_cm3.h>
|
||||
|
||||
#include <cpu/attr.h>
|
||||
|
||||
#include <drv/ser.h>
|
||||
#include <drv/ser_p.h>
|
||||
|
||||
#include <struct/fifobuf.h>
|
||||
|
||||
|
||||
#define SERIRQ_PRIORITY 4 ///< default priority for serial irqs.
|
||||
|
||||
/**
|
||||
* \name Overridable serial bus hooks
|
||||
*
|
||||
* These can be redefined in hw.h to implement
|
||||
* special bus policies such as half-duplex, 485, etc.
|
||||
*
|
||||
*
|
||||
* \code
|
||||
* TXBEGIN TXCHAR TXEND TXOFF
|
||||
* | __________|__________ | |
|
||||
* | | | | | | | | |
|
||||
* v v v v v v v v v
|
||||
* ______ __ __ __ __ __ __ ________________
|
||||
* \/ \/ \/ \/ \/ \/ \/
|
||||
* ______/\__/\__/\__/\__/\__/\__/
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \{
|
||||
*/
|
||||
|
||||
#ifndef SER_UART0_BUS_TXINIT
|
||||
/**
|
||||
* Default TXINIT macro - invoked in uart0_init()
|
||||
*
|
||||
* - Disable GPIO on USART0 tx/rx pins
|
||||
*/
|
||||
#if CPU_ARM_AT91 && !CPU_ARM_SAM7S_LARGE && !CPU_ARM_SAM7X
|
||||
#warning Check USART0 pins!
|
||||
#endif
|
||||
#define SER_UART0_BUS_TXINIT do { \
|
||||
PIOA_PDR = BV(RXD0) | BV(TXD0); \
|
||||
PIO_PERIPH_SEL(PIOA_BASE, BV(RXD0) | BV(TXD0), USART0_PERIPH); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#ifndef SER_UART0_BUS_TXBEGIN
|
||||
/**
|
||||
* Invoked before starting a transmission
|
||||
*/
|
||||
#define SER_UART0_BUS_TXBEGIN
|
||||
#endif
|
||||
|
||||
#ifndef SER_UART0_BUS_TXCHAR
|
||||
/**
|
||||
* Invoked to send one character.
|
||||
*/
|
||||
#define SER_UART0_BUS_TXCHAR(c) do { \
|
||||
US0_THR = (c); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#ifndef SER_UART0_BUS_TXEND
|
||||
/**
|
||||
* Invoked as soon as the txfifo becomes empty
|
||||
*/
|
||||
#define SER_UART0_BUS_TXEND
|
||||
#endif
|
||||
|
||||
/* End USART0 macros */
|
||||
|
||||
#if USART_PORTS > 1
|
||||
|
||||
#ifndef SER_UART1_BUS_TXINIT
|
||||
/**
|
||||
* Default TXINIT macro - invoked in uart1_init()
|
||||
*
|
||||
* - Disable GPIO on USART1 tx/rx pins
|
||||
*/
|
||||
#if CPU_ARM_AT91 && !CPU_ARM_SAM7S_LARGE && !CPU_ARM_SAM7X
|
||||
#warning Check USART1 pins!
|
||||
#endif
|
||||
#define SER_UART1_BUS_TXINIT do { \
|
||||
PIOA_PDR = BV(RXD1) | BV(TXD1); \
|
||||
PIO_PERIPH_SEL(PIOA_BASE, BV(RXD1) | BV(TXD1), USART1_PERIPH); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#ifndef SER_UART1_BUS_TXBEGIN
|
||||
/**
|
||||
* Invoked before starting a transmission
|
||||
*/
|
||||
#define SER_UART1_BUS_TXBEGIN
|
||||
#endif
|
||||
|
||||
#ifndef SER_UART1_BUS_TXCHAR
|
||||
/**
|
||||
* Invoked to send one character.
|
||||
*/
|
||||
#define SER_UART1_BUS_TXCHAR(c) do { \
|
||||
US1_THR = (c); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#ifndef SER_UART1_BUS_TXEND
|
||||
/**
|
||||
* Invoked as soon as the txfifo becomes empty
|
||||
*/
|
||||
#define SER_UART1_BUS_TXEND
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \name Overridable SPI hooks
|
||||
*
|
||||
* These can be redefined in hw.h to implement
|
||||
* special bus policies such as slave select pin handling, etc.
|
||||
*
|
||||
* \{
|
||||
*/
|
||||
|
||||
#ifndef SER_SPI0_BUS_TXINIT
|
||||
/**
|
||||
* Default TXINIT macro - invoked in spi_init()
|
||||
* The default is no action.
|
||||
*/
|
||||
#if CPU_CM3_SAM3
|
||||
#define SER_SPI0_BUS_TXINIT do { \
|
||||
/* Disable PIO on SPI pins */ \
|
||||
PIOA_PDR = BV(SPI0_SPCK) | BV(SPI0_MOSI) | BV(SPI0_MISO); \
|
||||
/* SPI is peripheral A on SAM3X,A,N,S,U */ \
|
||||
PIO_PERIPH_SEL(PIOA_BASE, BV(SPI0_SPCK) | BV(SPI0_MOSI) | BV(SPI0_MISO), PIO_PERIPH_A); \
|
||||
} while (0)
|
||||
#else
|
||||
#define SER_SPI0_BUS_TXINIT do { \
|
||||
/* Disable PIO on SPI pins */ \
|
||||
PIOA_PDR = BV(SPI0_SPCK) | BV(SPI0_MOSI) | BV(SPI0_MISO); \
|
||||
} while (0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef SER_SPI0_BUS_TXCLOSE
|
||||
/**
|
||||
* Invoked after the last character has been transmitted.
|
||||
* The default is no action.
|
||||
*/
|
||||
#define SER_SPI0_BUS_TXCLOSE do { \
|
||||
/* Enable PIO on SPI pins */ \
|
||||
PIOA_PER = BV(SPI0_SPCK) | BV(SPI0_MOSI) | BV(SPI0_MISO); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#if CPU_ARM_SAM7X
|
||||
|
||||
#ifndef SER_SPI1_BUS_TXINIT
|
||||
/**
|
||||
* Default TXINIT macro - invoked in spi_init()
|
||||
* The default is no action.
|
||||
*/
|
||||
#define SER_SPI1_BUS_TXINIT do { \
|
||||
/* Disable PIO on SPI pins */ \
|
||||
PIOA_PDR = BV(SPI1_SPCK) | BV(SPI1_MOSI) | BV(SPI1_MISO); \
|
||||
/* SPI1 pins are on B peripheral function! */ \
|
||||
PIOA_BSR = BV(SPI1_SPCK) | BV(SPI1_MOSI) | BV(SPI1_MISO); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#ifndef SER_SPI1_BUS_TXCLOSE
|
||||
/**
|
||||
* Invoked after the last character has been transmitted.
|
||||
* The default is no action.
|
||||
*/
|
||||
#define SER_SPI1_BUS_TXCLOSE do { \
|
||||
/* Enable PIO on SPI pins */ \
|
||||
PIOA_PER = BV(SPI1_SPCK) | BV(SPI1_MOSI) | BV(SPI1_MISO); \
|
||||
} while (0)
|
||||
#endif
|
||||
#endif
|
||||
/*\}*/
|
||||
|
||||
|
||||
/**
|
||||
* \name Core dependent interrupt handling macros
|
||||
*
|
||||
* Atmel serial hardware is used on different CPU cores,
|
||||
* i.e. SAM3 and SAM7. The user interface of the serial
|
||||
* subsystem is identical but core interrupt controllers
|
||||
* are different.
|
||||
*
|
||||
* \{
|
||||
*/
|
||||
#if CPU_ARM_AT91
|
||||
|
||||
INLINE void sysirq_setHandler(sysirq_t irq, sysirq_handler_t handler)
|
||||
{
|
||||
/* Set the vector. */
|
||||
AIC_SVR(irq) = uart0_irq_dispatcher;
|
||||
|
||||
/* Initialize to level/edge sensitive with defined priority. */
|
||||
#if CPU_ARM_SAM7X
|
||||
if (irq == SPI0_ID || irq == SPI1_ID)
|
||||
#else
|
||||
if (irq == SPI0_ID)
|
||||
#endif
|
||||
AIC_SMR(irq) = (AIC_SMR(irq) & ~AIC_SRCTYPE_MASK) | AIC_SRCTYPE_INT_EDGE_TRIGGERED;
|
||||
else // USART/UART
|
||||
AIC_SMR(irq) = (AIC_SMR(irq) & ~AIC_SRCTYPE_MASK) | AIC_SRCTYPE_INT_LEVEL_SENSITIVE;
|
||||
|
||||
/* Enable IRQ */
|
||||
AIC_IECR = BV(irq);
|
||||
}
|
||||
|
||||
INLINE void sysirq_setPriority(sysirq_t irq, int prio)
|
||||
{
|
||||
AIC_SMR(irq) = (AIC_SMR(irq) & ~AIC_PRIOR_MASK) | SERIRQ_PRIORITY;
|
||||
}
|
||||
|
||||
/** Inform hw that we have served the IRQ */
|
||||
#define SER_INT_ACK do { \
|
||||
AIC_EOICR = 0; \
|
||||
} while (0)
|
||||
|
||||
#elif CPU_CM3_SAM3
|
||||
|
||||
/** Inform hw that we have served the IRQ */
|
||||
#define SER_INT_ACK do { /* nop */ } while (0)
|
||||
|
||||
#else
|
||||
#error No interrupt handling macros defined for current architecture
|
||||
#endif
|
||||
|
||||
/*\}*/
|
||||
|
||||
/* From the high-level serial driver */
|
||||
extern struct Serial *ser_handles[SER_CNT];
|
||||
|
||||
/* TX and RX buffers */
|
||||
static unsigned char uart0_txbuffer[CONFIG_UART0_TXBUFSIZE];
|
||||
static unsigned char uart0_rxbuffer[CONFIG_UART0_RXBUFSIZE];
|
||||
#if USART_PORTS > 1
|
||||
static unsigned char uart1_txbuffer[CONFIG_UART1_TXBUFSIZE];
|
||||
static unsigned char uart1_rxbuffer[CONFIG_UART1_RXBUFSIZE];
|
||||
#endif
|
||||
static unsigned char spi0_txbuffer[CONFIG_SPI0_TXBUFSIZE];
|
||||
static unsigned char spi0_rxbuffer[CONFIG_SPI0_RXBUFSIZE];
|
||||
#if CPU_ARM_SAM7X
|
||||
static unsigned char spi1_txbuffer[CONFIG_SPI1_TXBUFSIZE];
|
||||
static unsigned char spi1_rxbuffer[CONFIG_SPI1_RXBUFSIZE];
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Internal hardware state structure
|
||||
*
|
||||
* The \a sending variable is true while the transmission
|
||||
* interrupt is retriggering itself.
|
||||
*
|
||||
* For the USARTs the \a sending flag is useful for taking specific
|
||||
* actions before sending a burst of data, at the start of a trasmission
|
||||
* but not before every char sent.
|
||||
*
|
||||
* For the SPI, this flag is necessary because the SPI sends and receives
|
||||
* bytes at the same time and the SPI IRQ is unique for send/receive.
|
||||
* The only way to start transmission is to write data in SPDR (this
|
||||
* is done by spi_starttx()). We do this *only* if a transfer is
|
||||
* not already started.
|
||||
*/
|
||||
struct ArmSerial
|
||||
{
|
||||
struct SerialHardware hw;
|
||||
volatile bool sending;
|
||||
};
|
||||
|
||||
static ISR_PROTO(uart0_irq_dispatcher);
|
||||
#if USART_PORTS > 1
|
||||
static ISR_PROTO(uart1_irq_dispatcher);
|
||||
#endif
|
||||
static ISR_PROTO(spi0_irq_handler);
|
||||
#if CPU_ARM_SAM7X
|
||||
static ISR_PROTO(spi1_irq_handler);
|
||||
#endif
|
||||
/*
|
||||
* Callbacks for USART0
|
||||
*/
|
||||
static void uart0_init(
|
||||
UNUSED_ARG(struct SerialHardware *, _hw),
|
||||
UNUSED_ARG(struct Serial *, ser))
|
||||
{
|
||||
US0_IDR = 0xFFFFFFFF;
|
||||
pmc_periphEnable(US0_ID);
|
||||
|
||||
/*
|
||||
* - Reset USART0
|
||||
* - Set serial param: mode Normal, 8bit data, 1bit stop, parity none
|
||||
* - Enable both the receiver and the transmitter
|
||||
* - Enable only the RX complete interrupt
|
||||
*/
|
||||
US0_CR = BV(US_RSTRX) | BV(US_RSTTX);
|
||||
US0_MR = US_CHMODE_NORMAL | US_CHRL_8 | US_NBSTOP_1 | US_PAR_NO;
|
||||
US0_CR = BV(US_RXEN) | BV(US_TXEN);
|
||||
US0_IER = BV(US_RXRDY);
|
||||
|
||||
SER_UART0_BUS_TXINIT;
|
||||
|
||||
sysirq_setPriority(INT_US0, SERIRQ_PRIORITY);
|
||||
sysirq_setHandler(INT_US0, uart0_irq_dispatcher);
|
||||
|
||||
SER_STROBE_INIT;
|
||||
}
|
||||
|
||||
static void uart0_cleanup(UNUSED_ARG(struct SerialHardware *, _hw))
|
||||
{
|
||||
US0_CR = BV(US_RSTRX) | BV(US_RSTTX) | BV(US_RXDIS) | BV(US_TXDIS) | BV(US_RSTSTA);
|
||||
}
|
||||
|
||||
static void uart0_enabletxirq(struct SerialHardware *_hw)
|
||||
{
|
||||
struct ArmSerial *hw = (struct ArmSerial *)_hw;
|
||||
|
||||
/*
|
||||
* WARNING: racy code here! The tx interrupt sets hw->sending to false
|
||||
* when it runs with an empty fifo. The order of statements in the
|
||||
* if-block matters.
|
||||
*/
|
||||
if (!hw->sending)
|
||||
{
|
||||
hw->sending = true;
|
||||
/*
|
||||
* - Enable the transmitter
|
||||
* - Enable TX empty interrupt
|
||||
*/
|
||||
SER_UART0_BUS_TXBEGIN;
|
||||
US0_IER = BV(US_TXEMPTY);
|
||||
}
|
||||
}
|
||||
|
||||
static void uart0_setbaudrate(UNUSED_ARG(struct SerialHardware *, _hw), unsigned long rate)
|
||||
{
|
||||
/* Compute baud-rate period */
|
||||
US0_BRGR = CPU_FREQ / (16 * rate);
|
||||
//DB(kprintf("uart0_setbaudrate(rate=%lu): period=%d\n", rate, period);)
|
||||
}
|
||||
|
||||
static void uart0_setparity(UNUSED_ARG(struct SerialHardware *, _hw), int parity)
|
||||
{
|
||||
US0_MR &= ~US_PAR_MASK;
|
||||
/* Set UART parity */
|
||||
switch(parity)
|
||||
{
|
||||
case SER_PARITY_NONE:
|
||||
{
|
||||
/* Parity none. */
|
||||
US0_MR |= US_PAR_NO;
|
||||
break;
|
||||
}
|
||||
case SER_PARITY_EVEN:
|
||||
{
|
||||
/* Even parity. */
|
||||
US0_MR |= US_PAR_EVEN;
|
||||
break;
|
||||
}
|
||||
case SER_PARITY_ODD:
|
||||
{
|
||||
/* Odd parity. */
|
||||
US0_MR |= US_PAR_ODD;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
#if USART_PORTS > 1
|
||||
|
||||
/*
|
||||
* Callbacks for USART1
|
||||
*/
|
||||
static void uart1_init(
|
||||
UNUSED_ARG(struct SerialHardware *, _hw),
|
||||
UNUSED_ARG(struct Serial *, ser))
|
||||
{
|
||||
US1_IDR = 0xFFFFFFFF;
|
||||
pmc_periphEnable(US1_ID);
|
||||
|
||||
/*
|
||||
* - Reset USART1
|
||||
* - Set serial param: mode Normal, 8bit data, 1bit stop, parity none
|
||||
* - Enable both the receiver and the transmitter
|
||||
* - Enable only the RX complete interrupt
|
||||
*/
|
||||
US1_CR = BV(US_RSTRX) | BV(US_RSTTX);
|
||||
US1_MR = US_CHMODE_NORMAL | US_CHRL_8 | US_NBSTOP_1 | US_PAR_NO;
|
||||
US1_CR = BV(US_RXEN) | BV(US_TXEN);
|
||||
US1_IER = BV(US_RXRDY);
|
||||
|
||||
SER_UART1_BUS_TXINIT;
|
||||
|
||||
sysirq_setPriority(INT_US1, SERIRQ_PRIORITY);
|
||||
sysirq_setHandler(INT_US1, uart1_irq_dispatcher);
|
||||
|
||||
SER_STROBE_INIT;
|
||||
}
|
||||
|
||||
static void uart1_cleanup(UNUSED_ARG(struct SerialHardware *, _hw))
|
||||
{
|
||||
US1_CR = BV(US_RSTRX) | BV(US_RSTTX) | BV(US_RXDIS) | BV(US_TXDIS) | BV(US_RSTSTA);
|
||||
}
|
||||
|
||||
static void uart1_enabletxirq(struct SerialHardware *_hw)
|
||||
{
|
||||
struct ArmSerial *hw = (struct ArmSerial *)_hw;
|
||||
|
||||
/*
|
||||
* WARNING: racy code here! The tx interrupt sets hw->sending to false
|
||||
* when it runs with an empty fifo. The order of statements in the
|
||||
* if-block matters.
|
||||
*/
|
||||
if (!hw->sending)
|
||||
{
|
||||
hw->sending = true;
|
||||
/*
|
||||
* - Enable the transmitter
|
||||
* - Enable TX empty interrupt
|
||||
*/
|
||||
SER_UART1_BUS_TXBEGIN;
|
||||
US1_IER = BV(US_TXEMPTY);
|
||||
}
|
||||
}
|
||||
|
||||
static void uart1_setbaudrate(UNUSED_ARG(struct SerialHardware *, _hw), unsigned long rate)
|
||||
{
|
||||
/* Compute baud-rate period */
|
||||
US1_BRGR = CPU_FREQ / (16 * rate);
|
||||
//DB(kprintf("uart0_setbaudrate(rate=%lu): period=%d\n", rate, period);)
|
||||
}
|
||||
|
||||
static void uart1_setparity(UNUSED_ARG(struct SerialHardware *, _hw), int parity)
|
||||
{
|
||||
US1_MR &= ~US_PAR_MASK;
|
||||
/* Set UART parity */
|
||||
switch(parity)
|
||||
{
|
||||
case SER_PARITY_NONE:
|
||||
{
|
||||
/* Parity none. */
|
||||
US1_MR |= US_PAR_NO;
|
||||
break;
|
||||
}
|
||||
case SER_PARITY_EVEN:
|
||||
{
|
||||
/* Even parity. */
|
||||
US1_MR |= US_PAR_EVEN;
|
||||
break;
|
||||
}
|
||||
case SER_PARITY_ODD:
|
||||
{
|
||||
/* Odd parity. */
|
||||
US1_MR |= US_PAR_ODD;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* USART_PORTS > 1 */
|
||||
|
||||
/* SPI driver */
|
||||
static void spi0_init(UNUSED_ARG(struct SerialHardware *, _hw), UNUSED_ARG(struct Serial *, ser))
|
||||
{
|
||||
SER_SPI0_BUS_TXINIT;
|
||||
|
||||
/* Reset device */
|
||||
SPI0_CR = BV(SPI_SWRST);
|
||||
|
||||
/*
|
||||
* Set SPI to master mode, fixed peripheral select, chip select directly connected to a peripheral device,
|
||||
* SPI clock set to MCK, mode fault detection disabled, loopback disable, NPCS0 active, Delay between CS = 0
|
||||
*/
|
||||
SPI0_MR = BV(SPI_MSTR) | BV(SPI_MODFDIS);
|
||||
|
||||
/*
|
||||
* Set SPI mode.
|
||||
* At reset clock division factor is set to 0, that is
|
||||
* *forbidden*. Set SPI clock to minimum to keep it valid.
|
||||
* Set all possible chip select registers in case user manually
|
||||
* change CPS field in SPI_MR.
|
||||
*/
|
||||
SPI0_CSR0 = BV(SPI_NCPHA) | (255 << SPI_SCBR_SHIFT);
|
||||
SPI0_CSR1 = BV(SPI_NCPHA) | (255 << SPI_SCBR_SHIFT);
|
||||
SPI0_CSR2 = BV(SPI_NCPHA) | (255 << SPI_SCBR_SHIFT);
|
||||
SPI0_CSR3 = BV(SPI_NCPHA) | (255 << SPI_SCBR_SHIFT);
|
||||
|
||||
/* Disable all irqs */
|
||||
SPI0_IDR = 0xFFFFFFFF;
|
||||
|
||||
//sysirq_setPriority(INT_SPI0, SERIRQ_PRIORITY);
|
||||
sysirq_setHandler(INT_SPI0, spi0_irq_handler);
|
||||
pmc_periphEnable(SPI0_ID);
|
||||
|
||||
/* Enable SPI */
|
||||
SPI0_CR = BV(SPI_SPIEN);
|
||||
|
||||
SER_STROBE_INIT;
|
||||
}
|
||||
|
||||
static void spi0_cleanup(UNUSED_ARG(struct SerialHardware *, _hw))
|
||||
{
|
||||
/* Disable SPI */
|
||||
SPI0_CR = BV(SPI_SPIDIS);
|
||||
|
||||
/* Disable all irqs */
|
||||
SPI0_IDR = 0xFFFFFFFF;
|
||||
|
||||
SER_SPI0_BUS_TXCLOSE;
|
||||
}
|
||||
|
||||
static void spi0_starttx(struct SerialHardware *_hw)
|
||||
{
|
||||
struct ArmSerial *hw = (struct ArmSerial *)_hw;
|
||||
|
||||
cpu_flags_t flags;
|
||||
IRQ_SAVE_DISABLE(flags);
|
||||
|
||||
/* Send data only if the SPI is not already transmitting */
|
||||
if (!hw->sending && !fifo_isempty(&ser_handles[SER_SPI0]->txfifo))
|
||||
{
|
||||
hw->sending = true;
|
||||
SPI0_TDR = fifo_pop(&ser_handles[SER_SPI0]->txfifo);
|
||||
/* Enable interrupt on tx buffer empty */
|
||||
SPI0_IER = BV(SPI_TXEMPTY);
|
||||
}
|
||||
|
||||
IRQ_RESTORE(flags);
|
||||
}
|
||||
|
||||
static void spi0_setbaudrate(UNUSED_ARG(struct SerialHardware *, _hw), unsigned long rate)
|
||||
{
|
||||
SPI0_CSR0 &= ~SPI_SCBR;
|
||||
|
||||
ASSERT((uint8_t)DIV_ROUND(CPU_FREQ, rate));
|
||||
SPI0_CSR0 |= DIV_ROUND(CPU_FREQ, rate) << SPI_SCBR_SHIFT;
|
||||
}
|
||||
|
||||
#if CPU_ARM_SAM7X
|
||||
/* SPI driver */
|
||||
static void spi1_init(UNUSED_ARG(struct SerialHardware *, _hw), UNUSED_ARG(struct Serial *, ser))
|
||||
{
|
||||
SER_SPI1_BUS_TXINIT;
|
||||
|
||||
/* Reset device */
|
||||
SPI1_CR = BV(SPI_SWRST);
|
||||
|
||||
/*
|
||||
* Set SPI to master mode, fixed peripheral select, chip select directly connected to a peripheral device,
|
||||
* SPI clock set to MCK, mode fault detection disabled, loopback disable, NPCS0 active, Delay between CS = 0
|
||||
*/
|
||||
SPI1_MR = BV(SPI_MSTR) | BV(SPI_MODFDIS);
|
||||
|
||||
/*
|
||||
* Set SPI mode.
|
||||
* At reset clock division factor is set to 0, that is
|
||||
* *forbidden*. Set SPI clock to minimum to keep it valid.
|
||||
* Set all possible chip select registers in case user manually
|
||||
* change chip select.
|
||||
*/
|
||||
SPI1_CSR0 = BV(SPI_NCPHA) | (255 << SPI_SCBR_SHIFT);
|
||||
SPI1_CSR1 = BV(SPI_NCPHA) | (255 << SPI_SCBR_SHIFT);
|
||||
SPI1_CSR2 = BV(SPI_NCPHA) | (255 << SPI_SCBR_SHIFT);
|
||||
SPI1_CSR3 = BV(SPI_NCPHA) | (255 << SPI_SCBR_SHIFT);
|
||||
|
||||
/* Disable all SPI irqs */
|
||||
SPI1_IDR = 0xFFFFFFFF;
|
||||
|
||||
sysirq_setPriority(INT_SPI1, SERIRQ_PRIORITY);
|
||||
sysirq_setHandler(INT_SPI1, spi1_irq_dispatcher);
|
||||
pmc_periphEnable(SPI1_ID);
|
||||
|
||||
/* Enable SPI */
|
||||
SPI1_CR = BV(SPI_SPIEN);
|
||||
|
||||
SER_STROBE_INIT;
|
||||
}
|
||||
|
||||
static void spi1_cleanup(UNUSED_ARG(struct SerialHardware *, _hw))
|
||||
{
|
||||
/* Disable SPI */
|
||||
SPI1_CR = BV(SPI_SPIDIS);
|
||||
|
||||
/* Disable all irqs */
|
||||
SPI1_IDR = 0xFFFFFFFF;
|
||||
|
||||
SER_SPI1_BUS_TXCLOSE;
|
||||
}
|
||||
|
||||
static void spi1_starttx(struct SerialHardware *_hw)
|
||||
{
|
||||
struct ArmSerial *hw = (struct ArmSerial *)_hw;
|
||||
|
||||
cpu_flags_t flags;
|
||||
IRQ_SAVE_DISABLE(flags);
|
||||
|
||||
/* Send data only if the SPI is not already transmitting */
|
||||
if (!hw->sending && !fifo_isempty(&ser_handles[SER_SPI1]->txfifo))
|
||||
{
|
||||
hw->sending = true;
|
||||
SPI1_TDR = fifo_pop(&ser_handles[SER_SPI1]->txfifo);
|
||||
/* Enable interrupt on tx buffer empty */
|
||||
SPI1_IER = BV(SPI_TXEMPTY);
|
||||
}
|
||||
|
||||
IRQ_RESTORE(flags);
|
||||
}
|
||||
|
||||
static void spi1_setbaudrate(UNUSED_ARG(struct SerialHardware *, _hw), unsigned long rate)
|
||||
{
|
||||
SPI1_CSR0 &= ~SPI_SCBR;
|
||||
|
||||
ASSERT((uint8_t)DIV_ROUND(CPU_FREQ, rate));
|
||||
SPI1_CSR0 |= DIV_ROUND(CPU_FREQ, rate) << SPI_SCBR_SHIFT;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void spi_setparity(UNUSED_ARG(struct SerialHardware *, _hw), UNUSED_ARG(int, parity))
|
||||
{
|
||||
// nop
|
||||
}
|
||||
|
||||
|
||||
static bool tx_sending(struct SerialHardware* _hw)
|
||||
{
|
||||
struct ArmSerial *hw = (struct ArmSerial *)_hw;
|
||||
return hw->sending;
|
||||
}
|
||||
|
||||
// FIXME: move into compiler.h? Ditch?
|
||||
#if COMPILER_C99
|
||||
#define C99INIT(name,val) .name = val
|
||||
#elif defined(__GNUC__)
|
||||
#define C99INIT(name,val) name: val
|
||||
#else
|
||||
#warning No designated initializers, double check your code
|
||||
#define C99INIT(name,val) (val)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* High-level interface data structures
|
||||
*/
|
||||
static const struct SerialHardwareVT UART0_VT =
|
||||
{
|
||||
C99INIT(init, uart0_init),
|
||||
C99INIT(cleanup, uart0_cleanup),
|
||||
C99INIT(setBaudrate, uart0_setbaudrate),
|
||||
C99INIT(setParity, uart0_setparity),
|
||||
C99INIT(txStart, uart0_enabletxirq),
|
||||
C99INIT(txSending, tx_sending),
|
||||
};
|
||||
|
||||
#if USART_PORTS > 1
|
||||
|
||||
static const struct SerialHardwareVT UART1_VT =
|
||||
{
|
||||
C99INIT(init, uart1_init),
|
||||
C99INIT(cleanup, uart1_cleanup),
|
||||
C99INIT(setBaudrate, uart1_setbaudrate),
|
||||
C99INIT(setParity, uart1_setparity),
|
||||
C99INIT(txStart, uart1_enabletxirq),
|
||||
C99INIT(txSending, tx_sending),
|
||||
};
|
||||
|
||||
#endif /* USART_PORTS > 1 */
|
||||
|
||||
static const struct SerialHardwareVT SPI0_VT =
|
||||
{
|
||||
C99INIT(init, spi0_init),
|
||||
C99INIT(cleanup, spi0_cleanup),
|
||||
C99INIT(setBaudrate, spi0_setbaudrate),
|
||||
C99INIT(setParity, spi_setparity),
|
||||
C99INIT(txStart, spi0_starttx),
|
||||
C99INIT(txSending, tx_sending),
|
||||
};
|
||||
#if CPU_ARM_SAM7X
|
||||
static const struct SerialHardwareVT SPI1_VT =
|
||||
{
|
||||
C99INIT(init, spi1_init),
|
||||
C99INIT(cleanup, spi1_cleanup),
|
||||
C99INIT(setBaudrate, spi1_setbaudrate),
|
||||
C99INIT(setParity, spi_setparity),
|
||||
C99INIT(txStart, spi1_starttx),
|
||||
C99INIT(txSending, tx_sending),
|
||||
};
|
||||
#endif
|
||||
|
||||
static struct ArmSerial UARTDescs[SER_CNT] =
|
||||
{
|
||||
{
|
||||
C99INIT(hw, /**/) {
|
||||
C99INIT(table, &UART0_VT),
|
||||
C99INIT(txbuffer, uart0_txbuffer),
|
||||
C99INIT(rxbuffer, uart0_rxbuffer),
|
||||
C99INIT(txbuffer_size, sizeof(uart0_txbuffer)),
|
||||
C99INIT(rxbuffer_size, sizeof(uart0_rxbuffer)),
|
||||
},
|
||||
C99INIT(sending, false),
|
||||
},
|
||||
#if USART_PORTS > 1
|
||||
{
|
||||
C99INIT(hw, /**/) {
|
||||
C99INIT(table, &UART1_VT),
|
||||
C99INIT(txbuffer, uart1_txbuffer),
|
||||
C99INIT(rxbuffer, uart1_rxbuffer),
|
||||
C99INIT(txbuffer_size, sizeof(uart1_txbuffer)),
|
||||
C99INIT(rxbuffer_size, sizeof(uart1_rxbuffer)),
|
||||
},
|
||||
C99INIT(sending, false),
|
||||
},
|
||||
#endif
|
||||
|
||||
{
|
||||
C99INIT(hw, /**/) {
|
||||
C99INIT(table, &SPI0_VT),
|
||||
C99INIT(txbuffer, spi0_txbuffer),
|
||||
C99INIT(rxbuffer, spi0_rxbuffer),
|
||||
C99INIT(txbuffer_size, sizeof(spi0_txbuffer)),
|
||||
C99INIT(rxbuffer_size, sizeof(spi0_rxbuffer)),
|
||||
},
|
||||
C99INIT(sending, false),
|
||||
},
|
||||
#if CPU_ARM_SAM7X
|
||||
{
|
||||
C99INIT(hw, /**/) {
|
||||
C99INIT(table, &SPI1_VT),
|
||||
C99INIT(txbuffer, spi1_txbuffer),
|
||||
C99INIT(rxbuffer, spi1_rxbuffer),
|
||||
C99INIT(txbuffer_size, sizeof(spi1_txbuffer)),
|
||||
C99INIT(rxbuffer_size, sizeof(spi1_rxbuffer)),
|
||||
},
|
||||
C99INIT(sending, false),
|
||||
}
|
||||
|
||||
#endif
|
||||
};
|
||||
|
||||
struct SerialHardware *ser_hw_getdesc(int unit)
|
||||
{
|
||||
ASSERT(unit < SER_CNT);
|
||||
return &UARTDescs[unit].hw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serial 0 TX interrupt handler
|
||||
*/
|
||||
INLINE void uart0_irq_tx(void)
|
||||
{
|
||||
SER_STROBE_ON;
|
||||
|
||||
struct FIFOBuffer * const txfifo = &ser_handles[SER_UART0]->txfifo;
|
||||
|
||||
if (fifo_isempty(txfifo))
|
||||
{
|
||||
/*
|
||||
* - Disable the TX empty interrupts
|
||||
*/
|
||||
US0_IDR = BV(US_TXEMPTY);
|
||||
SER_UART0_BUS_TXEND;
|
||||
UARTDescs[SER_UART0].sending = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
char c = fifo_pop(txfifo);
|
||||
SER_UART0_BUS_TXCHAR(c);
|
||||
}
|
||||
|
||||
SER_STROBE_OFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serial 0 RX complete interrupt handler.
|
||||
*/
|
||||
INLINE void uart0_irq_rx(void)
|
||||
{
|
||||
SER_STROBE_ON;
|
||||
|
||||
/* Should be read before US_CRS */
|
||||
ser_handles[SER_UART0]->status |= US0_CSR & (SERRF_RXSROVERRUN | SERRF_FRAMEERROR);
|
||||
US0_CR = BV(US_RSTSTA);
|
||||
|
||||
char c = US0_RHR;
|
||||
struct FIFOBuffer * const rxfifo = &ser_handles[SER_UART0]->rxfifo;
|
||||
|
||||
if (fifo_isfull(rxfifo))
|
||||
ser_handles[SER_UART0]->status |= SERRF_RXFIFOOVERRUN;
|
||||
else
|
||||
fifo_push(rxfifo, c);
|
||||
|
||||
SER_STROBE_OFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serial IRQ dispatcher for USART0.
|
||||
*/
|
||||
static DECLARE_ISR(uart0_irq_dispatcher)
|
||||
{
|
||||
if (US0_CSR & BV(US_RXRDY))
|
||||
uart0_irq_rx();
|
||||
|
||||
if (US0_CSR & BV(US_TXEMPTY))
|
||||
uart0_irq_tx();
|
||||
|
||||
SER_INT_ACK;
|
||||
}
|
||||
|
||||
#if USART_PORTS > 1
|
||||
|
||||
/**
|
||||
* Serial 1 TX interrupt handler
|
||||
*/
|
||||
INLINE void uart1_irq_tx(void)
|
||||
{
|
||||
SER_STROBE_ON;
|
||||
|
||||
struct FIFOBuffer * const txfifo = &ser_handles[SER_UART1]->txfifo;
|
||||
|
||||
if (fifo_isempty(txfifo))
|
||||
{
|
||||
/*
|
||||
* - Disable the TX empty interrupts
|
||||
*/
|
||||
US1_IDR = BV(US_TXEMPTY);
|
||||
SER_UART1_BUS_TXEND;
|
||||
UARTDescs[SER_UART1].sending = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
char c = fifo_pop(txfifo);
|
||||
SER_UART1_BUS_TXCHAR(c);
|
||||
}
|
||||
|
||||
SER_STROBE_OFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serial 1 RX complete interrupt handler.
|
||||
*/
|
||||
INLINE void uart1_irq_rx(void)
|
||||
{
|
||||
SER_STROBE_ON;
|
||||
|
||||
/* Should be read before US_CRS */
|
||||
ser_handles[SER_UART1]->status |= US1_CSR & (SERRF_RXSROVERRUN | SERRF_FRAMEERROR);
|
||||
US1_CR = BV(US_RSTSTA);
|
||||
|
||||
char c = US1_RHR;
|
||||
struct FIFOBuffer * const rxfifo = &ser_handles[SER_UART1]->rxfifo;
|
||||
|
||||
if (fifo_isfull(rxfifo))
|
||||
ser_handles[SER_UART1]->status |= SERRF_RXFIFOOVERRUN;
|
||||
else
|
||||
fifo_push(rxfifo, c);
|
||||
|
||||
SER_STROBE_OFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serial IRQ dispatcher for USART1.
|
||||
*/
|
||||
static DECLARE_ISR(uart1_irq_dispatcher)
|
||||
{
|
||||
if (US1_CSR & BV(US_RXRDY))
|
||||
uart1_irq_rx();
|
||||
|
||||
if (US1_CSR & BV(US_TXEMPTY))
|
||||
uart1_irq_tx();
|
||||
|
||||
SER_INT_ACK;
|
||||
}
|
||||
|
||||
#endif /* USART_PORTS > 1 */
|
||||
|
||||
/**
|
||||
* SPI0 interrupt handler
|
||||
*/
|
||||
static DECLARE_ISR(spi0_irq_handler)
|
||||
{
|
||||
SER_STROBE_ON;
|
||||
|
||||
char c = SPI0_RDR;
|
||||
/* Read incoming byte. */
|
||||
if (!fifo_isfull(&ser_handles[SER_SPI0]->rxfifo))
|
||||
fifo_push(&ser_handles[SER_SPI0]->rxfifo, c);
|
||||
/*
|
||||
* FIXME
|
||||
else
|
||||
ser_handles[SER_SPI0]->status |= SERRF_RXFIFOOVERRUN;
|
||||
*/
|
||||
|
||||
/* Send */
|
||||
if (!fifo_isempty(&ser_handles[SER_SPI0]->txfifo))
|
||||
SPI0_TDR = fifo_pop(&ser_handles[SER_SPI0]->txfifo);
|
||||
else
|
||||
{
|
||||
UARTDescs[SER_SPI0].sending = false;
|
||||
/* Disable interrupt on tx buffer empty */
|
||||
SPI0_IDR = BV(SPI_TXEMPTY);
|
||||
}
|
||||
|
||||
SER_INT_ACK;
|
||||
|
||||
SER_STROBE_OFF;
|
||||
}
|
||||
|
||||
|
||||
#if CPU_ARM_SAM7X
|
||||
/**
|
||||
* SPI1 interrupt handler
|
||||
*/
|
||||
static DECLARE_ISR(spi1_irq_handler)
|
||||
{
|
||||
SER_STROBE_ON;
|
||||
|
||||
char c = SPI1_RDR;
|
||||
/* Read incoming byte. */
|
||||
if (!fifo_isfull(&ser_handles[SER_SPI1]->rxfifo))
|
||||
fifo_push(&ser_handles[SER_SPI1]->rxfifo, c);
|
||||
/*
|
||||
* FIXME
|
||||
else
|
||||
ser_handles[SER_SPI1]->status |= SERRF_RXFIFOOVERRUN;
|
||||
*/
|
||||
|
||||
/* Send */
|
||||
if (!fifo_isempty(&ser_handles[SER_SPI1]->txfifo))
|
||||
SPI1_TDR = fifo_pop(&ser_handles[SER_SPI1]->txfifo);
|
||||
else
|
||||
{
|
||||
UARTDescs[SER_SPI1].sending = false;
|
||||
/* Disable interrupt on tx buffer empty */
|
||||
SPI1_IDR = BV(SPI_TXEMPTY);
|
||||
}
|
||||
|
||||
SER_INT_ACK;
|
||||
|
||||
SER_STROBE_OFF;
|
||||
}
|
||||
#endif
|
81
bertos/cpu/cortex-m3/drv/ser_sam3.h
Normal file
81
bertos/cpu/cortex-m3/drv/ser_sam3.h
Normal file
|
@ -0,0 +1,81 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2003,2004 Develer S.r.l. (http://www.develer.com/)
|
||||
* Copyright 2000 Bernie Innocenti <bernie@codewiz.org>
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief High level serial I/O API
|
||||
*
|
||||
* \author Daniele Basile <asterix@develer.com>
|
||||
*/
|
||||
|
||||
#ifndef SER_SAM3_H
|
||||
#define SER_SAM3_H
|
||||
|
||||
#include <cfg/macros.h> /* BV() */
|
||||
#include <cfg/compiler.h> /* uint32_t */
|
||||
#include <cpu/detect.h> /* CPU_* */
|
||||
|
||||
/** \name Serial Error/status flags. */
|
||||
/*\{*/
|
||||
typedef uint32_t serstatus_t;
|
||||
|
||||
/* Software errors */
|
||||
#define SERRF_RXFIFOOVERRUN BV(0) /**< Rx FIFO buffer overrun */
|
||||
#define SERRF_RXTIMEOUT BV(1) /**< Receive timeout */
|
||||
#define SERRF_TXTIMEOUT BV(2) /**< Transmit timeout */
|
||||
|
||||
/*
|
||||
* Hardware errors.
|
||||
* These flags map directly to the ARM USART Channel Status Register (US_CSR).
|
||||
*/
|
||||
#define SERRF_RXSROVERRUN BV(5) /**< Rx shift register overrun */
|
||||
#define SERRF_FRAMEERROR BV(6) /**< Stop bit missing */
|
||||
#define SERRF_PARITYERROR BV(7) /**< Parity error */
|
||||
#define SERRF_NOISEERROR 0 /**< Unsupported */
|
||||
/*\}*/
|
||||
|
||||
/**
|
||||
* \name Serial hw numbers
|
||||
*
|
||||
* \{
|
||||
*/
|
||||
enum
|
||||
{
|
||||
SER_UART0,
|
||||
#if !CPU_CM3_SAM3U
|
||||
SER_UART1,
|
||||
#endif
|
||||
SER_SPI0,
|
||||
SER_CNT /**< Number of serial ports */
|
||||
};
|
||||
/*\}*/
|
||||
|
||||
#endif /* SER_SAM3_H */
|
392
bertos/cpu/cortex-m3/drv/ser_stm32.c
Normal file
392
bertos/cpu/cortex-m3/drv/ser_stm32.c
Normal file
|
@ -0,0 +1,392 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief STM32 UART interface driver.
|
||||
*
|
||||
* \author Daniele Basile <asterix@develer.com>
|
||||
*/
|
||||
|
||||
#include "ser_stm32.h"
|
||||
|
||||
#include "cfg/cfg_ser.h"
|
||||
|
||||
#include <cfg/macros.h> /* for BV() */
|
||||
#include <cfg/debug.h>
|
||||
|
||||
#include <drv/gpio_stm32.h>
|
||||
#include <drv/irq_cm3.h>
|
||||
#include <drv/clock_stm32.h>
|
||||
#include <drv/ser_p.h>
|
||||
#include <drv/ser.h>
|
||||
|
||||
|
||||
/* From the high-level serial driver */
|
||||
extern struct Serial *ser_handles[SER_CNT];
|
||||
|
||||
struct CM3Serial
|
||||
{
|
||||
struct SerialHardware hw;
|
||||
volatile bool sending;
|
||||
uint32_t base;
|
||||
sysirq_t irq;
|
||||
};
|
||||
|
||||
/* Forward declaration */
|
||||
static struct CM3Serial UARTDesc[SER_CNT];
|
||||
|
||||
/* GPIO descriptor for UART pins */
|
||||
struct gpio_uart_info
|
||||
{
|
||||
/* GPIO base address register */
|
||||
uint32_t base;
|
||||
/* Pin(s) bitmask */
|
||||
uint32_t rx_pin;
|
||||
uint32_t tx_pin;
|
||||
/* Sysctl */
|
||||
uint32_t sysctl_gpio;
|
||||
uint32_t sysctl_usart;
|
||||
|
||||
};
|
||||
|
||||
/* Table to retrieve GPIO pins configuration to work as UART pins */
|
||||
static const struct gpio_uart_info gpio_uart[SER_CNT] =
|
||||
{
|
||||
/* UART1 */
|
||||
{
|
||||
.base = GPIOA_BASE,
|
||||
.rx_pin = GPIO_USART1_RX_PIN,
|
||||
.tx_pin = GPIO_USART1_TX_PIN,
|
||||
.sysctl_gpio = RCC_APB2_GPIOA,
|
||||
.sysctl_usart = RCC_APB2_USART1,
|
||||
},
|
||||
/* UART2 */
|
||||
{
|
||||
.base = GPIOA_BASE,
|
||||
.rx_pin = GPIO_USART2_RX_PIN,
|
||||
.tx_pin = GPIO_USART2_TX_PIN,
|
||||
.sysctl_gpio = RCC_APB2_GPIOA,
|
||||
.sysctl_usart = RCC_APB1_USART2,
|
||||
},
|
||||
#if CPU_CM3_STM32F103RB || CPU_CM3_STM32F103RE
|
||||
/* UART3 */
|
||||
{
|
||||
.base = GPIOB_BASE,
|
||||
.rx_pin = GPIO_USART3_RX_PIN,
|
||||
.tx_pin = GPIO_USART3_TX_PIN,
|
||||
.sysctl_gpio = RCC_APB2_GPIOB,
|
||||
.sysctl_usart = RCC_APB1_USART3,
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
#define USART1_PORT 0
|
||||
#define USART2_PORT 1
|
||||
#define USART3_PORT 2
|
||||
|
||||
void stm32_uartSetBaudRate(uint32_t base, unsigned long baud)
|
||||
{
|
||||
struct stm32_usart *_base = (struct stm32_usart *)base;
|
||||
_base->BRR = evaluate_brr(_base, CPU_FREQ, baud);
|
||||
}
|
||||
|
||||
void stm32_uartSetParity(uint32_t base, int parity)
|
||||
{
|
||||
struct stm32_usart *_base = (struct stm32_usart *)base;
|
||||
|
||||
/* USART_WORD_LEN_8B */
|
||||
_base->CR1 &= ~BV(CR1_M);
|
||||
|
||||
switch(parity)
|
||||
{
|
||||
case SER_PARITY_NONE:
|
||||
_base->CR1 &= ~BV(CR1_PCE);
|
||||
break;
|
||||
case SER_PARITY_ODD:
|
||||
_base->CR1 |= (BV(CR1_PCE) | BV(CR1_PS));
|
||||
break;
|
||||
case SER_PARITY_EVEN:
|
||||
_base->CR1 |= BV(CR1_PCE);
|
||||
_base->CR1 &= ~BV(CR1_PS);
|
||||
break;
|
||||
default:
|
||||
ASSERT(0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void stm32_uartInit(int port)
|
||||
{
|
||||
struct stm32_usart *base = (struct stm32_usart *)UARTDesc[port].base;
|
||||
|
||||
ASSERT(port >= 0 && port < SER_CNT);
|
||||
|
||||
/* Enable clocking on AFIO */
|
||||
RCC->APB2ENR |= RCC_APB2_AFIO;
|
||||
RCC->APB2ENR |= gpio_uart[port].sysctl_gpio;
|
||||
|
||||
/* Configure USART pins */
|
||||
if (port == USART1_PORT)
|
||||
{
|
||||
RCC->APB2ENR |= gpio_uart[port].sysctl_usart;
|
||||
}
|
||||
else
|
||||
{
|
||||
RCC->APB1ENR |= gpio_uart[port].sysctl_usart;
|
||||
}
|
||||
|
||||
stm32_gpioPinConfig((struct stm32_gpio *)gpio_uart[port].base, gpio_uart[port].tx_pin,
|
||||
GPIO_MODE_AF_PP, GPIO_SPEED_50MHZ);
|
||||
|
||||
stm32_gpioPinConfig((struct stm32_gpio *)gpio_uart[port].base, gpio_uart[port].rx_pin,
|
||||
GPIO_MODE_IN_FLOATING, GPIO_SPEED_50MHZ);
|
||||
|
||||
/* Clear control registry */
|
||||
base->CR2 = 0;
|
||||
base->CR1 = 0;
|
||||
base->CR3 = 0;
|
||||
base->SR = 0;
|
||||
|
||||
/* Set serial param: 115.200 bps, no parity */
|
||||
stm32_uartSetBaudRate(UARTDesc[port].base, 115200);
|
||||
stm32_uartSetParity(UARTDesc[port].base, SER_PARITY_NONE);
|
||||
|
||||
|
||||
/* Enable trasmision and receiver */
|
||||
base->CR1 |= (BV(CR1_TE) | BV(CR1_RE));
|
||||
|
||||
}
|
||||
|
||||
static bool tx_sending(struct SerialHardware *_hw)
|
||||
{
|
||||
struct CM3Serial *hw = (struct CM3Serial *)_hw;
|
||||
return hw->sending;
|
||||
}
|
||||
|
||||
static void uart_irq_rx(int port)
|
||||
{
|
||||
struct FIFOBuffer *rxfifo = &ser_handles[port]->rxfifo;
|
||||
struct stm32_usart *base = (struct stm32_usart *)UARTDesc[port].base;
|
||||
char c;
|
||||
|
||||
while (stm32_uartRxReady(UARTDesc[port].base))
|
||||
{
|
||||
c = base->DR;
|
||||
if (fifo_isfull(rxfifo))
|
||||
ser_handles[port]->status |= SERRF_RXFIFOOVERRUN;
|
||||
else
|
||||
fifo_push(rxfifo, c);
|
||||
}
|
||||
}
|
||||
|
||||
static void uart_irq_tx(int port)
|
||||
{
|
||||
struct FIFOBuffer *txfifo = &ser_handles[port]->txfifo;
|
||||
struct stm32_usart *base = (struct stm32_usart *)UARTDesc[port].base;
|
||||
|
||||
if (fifo_isempty(txfifo))
|
||||
{
|
||||
/*
|
||||
* Disable TX empty interrupts if there're no more
|
||||
* characters to transmit.
|
||||
*/
|
||||
base->CR1 &= ~BV(CR1_TXEIE);
|
||||
UARTDesc[port].sending = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
base->DR = fifo_pop(txfifo);
|
||||
}
|
||||
}
|
||||
|
||||
static void uart_common_irq_handler(int port)
|
||||
{
|
||||
struct stm32_usart *base = (struct stm32_usart *)UARTDesc[port].base;
|
||||
uint32_t status;
|
||||
|
||||
/* Read and clear the IRQ status */
|
||||
status = base->SR;
|
||||
|
||||
/* Check hw errors */
|
||||
ser_handles[port]->status = status &
|
||||
(BV(SR_ORE) | BV(SR_FE) | BV(SR_PE) | BV(SR_NE));
|
||||
|
||||
/* Process the IRQ */
|
||||
if (status & BV(CR1_RXNEIE))
|
||||
{
|
||||
uart_irq_rx(port);
|
||||
}
|
||||
if (status & (BV(CR1_TXEIE) | BV(CR1_TCIE)))
|
||||
{
|
||||
uart_irq_tx(port);
|
||||
}
|
||||
}
|
||||
|
||||
static void stm32_uartIRQEnable(int port, sysirq_handler_t handler)
|
||||
{
|
||||
struct stm32_usart *base = (struct stm32_usart *)UARTDesc[port].base;
|
||||
|
||||
/* Register the IRQ handler */
|
||||
sysirq_setHandler(UARTDesc[port].irq, handler);
|
||||
|
||||
base->CR1 |= BV(CR1_RXNEIE);
|
||||
}
|
||||
|
||||
static void stm32_uartIRQDisable(int port)
|
||||
{
|
||||
struct stm32_usart *base = (struct stm32_usart *)UARTDesc[port].base;
|
||||
|
||||
base->CR1 &= ~(BV(CR1_RXNEIE) | USART_FLAG_TXE);
|
||||
}
|
||||
|
||||
|
||||
/* UART class definition */
|
||||
#define UART_PORT(port) \
|
||||
/* UART TX and RX buffers */ \
|
||||
static unsigned char uart ## port ## _txbuffer[CONFIG_UART ## port ## _TXBUFSIZE]; \
|
||||
static unsigned char uart ## port ## _rxbuffer[CONFIG_UART ## port ## _RXBUFSIZE]; \
|
||||
\
|
||||
/* UART interrupt handler */ \
|
||||
static DECLARE_ISR(uart ## port ## _irq_handler) \
|
||||
{ \
|
||||
uart_common_irq_handler(USART ## port ## _PORT); \
|
||||
} \
|
||||
\
|
||||
/* UART public methods */ \
|
||||
static void uart ## port ## _txStart(struct SerialHardware *_hw) \
|
||||
{ \
|
||||
struct FIFOBuffer *txfifo = &ser_handles[USART ## port ## _PORT]->txfifo; \
|
||||
struct CM3Serial *hw = (struct CM3Serial *)_hw; \
|
||||
struct stm32_usart *base = (struct stm32_usart *)USART## port ## _BASE; \
|
||||
if (hw->sending) \
|
||||
return; \
|
||||
stm32_uartPutChar(USART ## port ## _BASE, fifo_pop(txfifo)); \
|
||||
if (!fifo_isempty(txfifo)) \
|
||||
{ \
|
||||
hw->sending = true; \
|
||||
base->CR1 |= BV(CR1_TXEIE); \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
static void uart ## port ## _setbaudrate(UNUSED_ARG(struct SerialHardware *, hw), \
|
||||
unsigned long baud) \
|
||||
{ \
|
||||
stm32_uartSetBaudRate(USART## port ## _BASE, baud); \
|
||||
} \
|
||||
\
|
||||
static void uart ## port ## _setparity(UNUSED_ARG(struct SerialHardware *, hw), \
|
||||
int parity) \
|
||||
{ \
|
||||
stm32_uartSetParity(USART## port ## _BASE, parity); \
|
||||
} \
|
||||
\
|
||||
static void uart ## port ## _cleanup(struct SerialHardware *_hw) \
|
||||
{ \
|
||||
struct CM3Serial *hw = (struct CM3Serial *)_hw; \
|
||||
hw->sending = false; \
|
||||
stm32_uartIRQDisable(USART ## port ## _PORT); \
|
||||
stm32_uartClear(USART## port ## _BASE); \
|
||||
stm32_uartDisable(USART## port ## _BASE); \
|
||||
} \
|
||||
\
|
||||
static void uart ## port ## _init(UNUSED_ARG(struct SerialHardware *, hw), \
|
||||
UNUSED_ARG(struct Serial *, ser)) \
|
||||
{ \
|
||||
stm32_uartInit(USART ## port ## _PORT); \
|
||||
stm32_uartEnable(USART## port ## _BASE); \
|
||||
stm32_uartIRQEnable(USART ## port ## _PORT, uart ## port ## _irq_handler); \
|
||||
} \
|
||||
\
|
||||
/* UART operations */ \
|
||||
static const struct SerialHardwareVT USART ## port ## _VT = \
|
||||
{ \
|
||||
.init = uart ## port ## _init, \
|
||||
.cleanup = uart ## port ## _cleanup, \
|
||||
.setBaudrate = uart ## port ## _setbaudrate, \
|
||||
.setParity = uart ## port ## _setparity, \
|
||||
.txStart = uart ## port ## _txStart, \
|
||||
.txSending = tx_sending, \
|
||||
};
|
||||
|
||||
/* UART port instances */
|
||||
UART_PORT(1)
|
||||
UART_PORT(2)
|
||||
#if CPU_CM3_STM32F103RB || CPU_CM3_STM32F103RE
|
||||
UART_PORT(3)
|
||||
#endif
|
||||
|
||||
static struct CM3Serial UARTDesc[SER_CNT] =
|
||||
{
|
||||
{
|
||||
.hw = {
|
||||
.table = &USART1_VT,
|
||||
.txbuffer = uart1_txbuffer,
|
||||
.rxbuffer = uart1_rxbuffer,
|
||||
.txbuffer_size = sizeof(uart1_txbuffer),
|
||||
.rxbuffer_size = sizeof(uart1_rxbuffer),
|
||||
},
|
||||
.sending = false,
|
||||
.base = USART1_BASE,
|
||||
.irq = USART1_IRQHANDLER,
|
||||
},
|
||||
{
|
||||
.hw = {
|
||||
.table = &USART2_VT,
|
||||
.txbuffer = uart2_txbuffer,
|
||||
.rxbuffer = uart2_rxbuffer,
|
||||
.txbuffer_size = sizeof(uart2_txbuffer),
|
||||
.rxbuffer_size = sizeof(uart2_rxbuffer),
|
||||
},
|
||||
.sending = false,
|
||||
.base = USART2_BASE,
|
||||
.irq = USART2_IRQHANDLER,
|
||||
},
|
||||
#if CPU_CM3_STM32F103RB || CPU_CM3_STM32F103RE
|
||||
{
|
||||
.hw = {
|
||||
.table = &USART3_VT,
|
||||
.txbuffer = uart3_txbuffer,
|
||||
.rxbuffer = uart3_rxbuffer,
|
||||
.txbuffer_size = sizeof(uart3_txbuffer),
|
||||
.rxbuffer_size = sizeof(uart3_rxbuffer),
|
||||
},
|
||||
.sending = false,
|
||||
.base = USART3_BASE,
|
||||
.irq = USART3_IRQHANDLER,
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
struct SerialHardware *ser_hw_getdesc(int port)
|
||||
{
|
||||
ASSERT(port >= 0 && port < SER_CNT);
|
||||
return &UARTDesc[port].hw;
|
||||
}
|
130
bertos/cpu/cortex-m3/drv/ser_stm32.h
Normal file
130
bertos/cpu/cortex-m3/drv/ser_stm32.h
Normal file
|
@ -0,0 +1,130 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief STM32F10xx UART interface driver.
|
||||
*
|
||||
* \author Daniele Basile <asterix@develer.com>
|
||||
*/
|
||||
|
||||
#ifndef SER_STM32_H
|
||||
#define SER_STM32_H
|
||||
|
||||
#include <cfg/debug.h>
|
||||
|
||||
#include <cpu/power.h> /* cpu_relax() */
|
||||
|
||||
#include <io/stm32.h>
|
||||
|
||||
/* Serial hardware numbers */
|
||||
enum
|
||||
{
|
||||
SER_UART1 = 0,
|
||||
SER_UART2,
|
||||
#if CPU_CM3_STM32F103RB || CPU_CM3_STM32F103RE
|
||||
SER_UART3,
|
||||
#endif
|
||||
SER_CNT //< Number of serial ports
|
||||
};
|
||||
|
||||
/* Software errors */
|
||||
#define SERRF_RXFIFOOVERRUN BV(6) //< Rx FIFO buffer overrun
|
||||
#define SERRF_RXTIMEOUT BV(5) //< Receive timeout
|
||||
#define SERRF_TXTIMEOUT BV(4) //< Transmit timeout
|
||||
|
||||
/*
|
||||
* Hardware errors.
|
||||
*/
|
||||
#define SERRF_RXSROVERRUN SR_ORE //< Input overrun
|
||||
#define SERRF_FRAMEERROR SR_FE //< Stop bit missing
|
||||
#define SERRF_PARITYERROR SR_PE //< Parity error
|
||||
#define SERRF_NOISEERROR SR_NE //< Noise error
|
||||
|
||||
/* Serial error/status flags */
|
||||
typedef uint32_t serstatus_t;
|
||||
|
||||
INLINE void stm32_uartDisable(uint32_t base)
|
||||
{
|
||||
struct stm32_usart *_base = (struct stm32_usart *)base;
|
||||
_base->CR1 &= ~CR1_RUN_RESET;
|
||||
}
|
||||
|
||||
INLINE void stm32_uartEnable(uint32_t base)
|
||||
{
|
||||
struct stm32_usart *_base = (struct stm32_usart *)base;
|
||||
_base->CR1 |= CR1_RUN_SET;
|
||||
}
|
||||
|
||||
/* Clear the flags register */
|
||||
INLINE void stm32_uartClear(uint32_t base)
|
||||
{
|
||||
struct stm32_usart *_base = (struct stm32_usart *)base;
|
||||
_base->SR &= ~USART_FLAG_MASK;
|
||||
}
|
||||
|
||||
INLINE bool stm32_uartTxDone(uint32_t base)
|
||||
{
|
||||
struct stm32_usart *_base = (struct stm32_usart *)base;
|
||||
return (_base->SR & USART_FLAG_TC);
|
||||
}
|
||||
|
||||
INLINE bool stm32_uartTxReady(uint32_t base)
|
||||
{
|
||||
struct stm32_usart *_base = (struct stm32_usart *)base;
|
||||
return (_base->SR & (BV(CR1_TXEIE) | BV(CR1_TCIE)));
|
||||
}
|
||||
|
||||
INLINE bool stm32_uartRxReady(uint32_t base)
|
||||
{
|
||||
struct stm32_usart *_base = (struct stm32_usart *)base;
|
||||
return (_base->SR & BV(CR1_RXNEIE));
|
||||
}
|
||||
|
||||
INLINE int stm32_uartPutChar(uint32_t base, unsigned char c)
|
||||
{
|
||||
struct stm32_usart *_base = (struct stm32_usart *)base;
|
||||
while (!stm32_uartTxReady(base))
|
||||
cpu_relax();
|
||||
_base->DR = c;
|
||||
return c;
|
||||
}
|
||||
|
||||
INLINE int stm32_uartGetChar(uint32_t base)
|
||||
{
|
||||
struct stm32_usart * _base = (struct stm32_usart *)base;
|
||||
return _base->DR;
|
||||
}
|
||||
|
||||
void stm32_uartSetBaudRate(uint32_t base, unsigned long baud);
|
||||
void stm32_uartSetParity(uint32_t base, int parity);
|
||||
void stm32_uartInit(int port);
|
||||
|
||||
#endif /* SER_STM32_H */
|
170
bertos/cpu/cortex-m3/drv/spi_dma_sam3.c
Normal file
170
bertos/cpu/cortex-m3/drv/spi_dma_sam3.c
Normal file
|
@ -0,0 +1,170 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2008 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief SPI driver with DMA.
|
||||
*
|
||||
* \author Francesco Sacchi <batt@develer.com>
|
||||
* \author Luca Ottaviano <lottaviano@develer.com>
|
||||
*/
|
||||
|
||||
#include "cfg/cfg_spi_dma.h"
|
||||
#include "hw/hw_spi_dma.h"
|
||||
|
||||
#include <io/cm3.h>
|
||||
#include <io/kfile.h>
|
||||
|
||||
#include <struct/fifobuf.h>
|
||||
#include <struct/kfile_fifo.h>
|
||||
|
||||
#include <drv/timer.h>
|
||||
#include <drv/spi_dma.h>
|
||||
|
||||
|
||||
#include <cpu/attr.h>
|
||||
#include <cpu/power.h>
|
||||
|
||||
#include <string.h> /* memset */
|
||||
|
||||
|
||||
void spi_dma_setclock(uint32_t rate)
|
||||
{
|
||||
SPI0_CSR0 &= ~SPI_SCBR;
|
||||
|
||||
ASSERT((uint8_t)DIV_ROUND(CPU_FREQ, rate));
|
||||
SPI0_CSR0 |= DIV_ROUND(CPU_FREQ, rate) << SPI_SCBR_SHIFT;
|
||||
}
|
||||
|
||||
|
||||
static int spi_dma_flush(UNUSED_ARG(struct KFile *, fd))
|
||||
{
|
||||
/* Wait for DMA to finish */
|
||||
while (!(SPI0_SR & BV(SPI_TXBUFE)))
|
||||
cpu_relax();
|
||||
|
||||
/* Wait until last bit has been shifted out */
|
||||
while (!(SPI0_SR & BV(SPI_TXEMPTY)))
|
||||
cpu_relax();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t spi_dma_write(struct KFile *fd, const void *_buf, size_t size)
|
||||
{
|
||||
SPI0_PTCR = BV(PDC_PTCR_TXTDIS);
|
||||
SPI0_TPR = (reg32_t)_buf;
|
||||
SPI0_TCR = size;
|
||||
SPI0_PTCR = BV(PDC_PTSR_TXTEN);
|
||||
spi_dma_flush(fd);
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Dummy buffer used to transmit 0xff chars while receiving data.
|
||||
* This buffer is completetly constant and the compiler should allocate it
|
||||
* in flash memory.
|
||||
*/
|
||||
static const uint8_t tx_dummy_buf[CONFIG_SPI_DMA_MAX_RX] = { [0 ... (CONFIG_SPI_DMA_MAX_RX - 1)] = 0xFF };
|
||||
|
||||
static size_t spi_dma_read(UNUSED_ARG(struct KFile *, fd), void *_buf, size_t size)
|
||||
{
|
||||
size_t count, total_rx = 0;
|
||||
uint8_t *buf = (uint8_t *)_buf;
|
||||
|
||||
while (size)
|
||||
{
|
||||
count = MIN(size, (size_t)CONFIG_SPI_DMA_MAX_RX);
|
||||
|
||||
SPI0_PTCR = BV(PDC_PTCR_TXTDIS) | BV(PDC_PTCR_RXTDIS);
|
||||
|
||||
SPI0_RPR = (reg32_t)buf;
|
||||
SPI0_RCR = count;
|
||||
SPI0_TPR = (reg32_t)tx_dummy_buf;
|
||||
SPI0_TCR = count;
|
||||
|
||||
/* Avoid reading the previous sent char */
|
||||
*buf = SPI0_RDR;
|
||||
|
||||
/* Start transfer */
|
||||
SPI0_PTCR = BV(PDC_PTCR_RXTEN) | BV(PDC_PTCR_TXTEN);
|
||||
|
||||
/* wait for transfer to finish */
|
||||
while (!(SPI0_SR & BV(SPI_ENDRX)))
|
||||
cpu_relax();
|
||||
|
||||
size -= count;
|
||||
total_rx += count;
|
||||
buf += count;
|
||||
}
|
||||
SPI0_PTCR = BV(PDC_PTCR_RXTDIS) | BV(PDC_PTCR_TXTDIS);
|
||||
|
||||
return total_rx;
|
||||
}
|
||||
|
||||
#define SPI_DMA_IRQ_PRIORITY 4
|
||||
|
||||
void spi_dma_init(SpiDma *spi)
|
||||
{
|
||||
/* Disable PIO on SPI pins */
|
||||
PIOA_PDR = BV(SPI0_SPCK) | BV(SPI0_MOSI) | BV(SPI0_MISO);
|
||||
|
||||
/* Reset device */
|
||||
SPI0_CR = BV(SPI_SWRST);
|
||||
|
||||
/*
|
||||
* Set SPI to master mode, fixed peripheral select, chip select directly connected to a peripheral device,
|
||||
* SPI clock set to MCK, mode fault detection disabled, loopback disable, NPCS0 active, Delay between CS = 0
|
||||
*/
|
||||
SPI0_MR = BV(SPI_MSTR) | BV(SPI_MODFDIS);
|
||||
|
||||
/*
|
||||
* Set SPI mode.
|
||||
* At reset clock division factor is set to 0, that is
|
||||
* *forbidden*. Set SPI clock to minimum to keep it valid.
|
||||
*/
|
||||
SPI0_CSR0 = BV(SPI_NCPHA) | (255 << SPI_SCBR_SHIFT);
|
||||
|
||||
/* Disable all irqs */
|
||||
SPI0_IDR = 0xFFFFFFFF;
|
||||
/* Enable SPI clock. */
|
||||
PMC_PCER = BV(SPI0_ID);
|
||||
|
||||
/* Enable SPI */
|
||||
SPI0_CR = BV(SPI_SPIEN);
|
||||
|
||||
DB(spi->fd._type = KFT_SPIDMA);
|
||||
spi->fd.write = spi_dma_write;
|
||||
spi->fd.read = spi_dma_read;
|
||||
spi->fd.flush = spi_dma_flush;
|
||||
|
||||
SPI_DMA_STROBE_INIT();
|
||||
}
|
208
bertos/cpu/cortex-m3/drv/ssi_lm3s.c
Normal file
208
bertos/cpu/cortex-m3/drv/ssi_lm3s.c
Normal file
|
@ -0,0 +1,208 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief LM3S1968 Synchronous Serial Interface (SSI) driver.
|
||||
*
|
||||
* \author Andrea Righi <arighi@develer.com>
|
||||
*/
|
||||
|
||||
|
||||
#include "ssi_lm3s.h"
|
||||
|
||||
#include <cfg/compiler.h>
|
||||
#include <cfg/debug.h>
|
||||
|
||||
#include <string.h> /* memset() */
|
||||
|
||||
/* SSI clocking informations (CPSDVSR + SCR) */
|
||||
struct SSIClock
|
||||
{
|
||||
unsigned int cpsdvsr;
|
||||
unsigned int scr;
|
||||
};
|
||||
|
||||
/*
|
||||
* Evaluate the SSI clock prescale (SSICPSR) and SSI serial clock rate (SCR).
|
||||
*/
|
||||
INLINE struct SSIClock
|
||||
lm3s_ssiPrescale(unsigned int bitrate)
|
||||
{
|
||||
struct SSIClock ret;
|
||||
|
||||
for (ret.cpsdvsr = 2, ret.scr = CPU_FREQ / bitrate / ret.cpsdvsr - 1;
|
||||
ret.scr > 255; ret.cpsdvsr += 2);
|
||||
ASSERT(ret.cpsdvsr < 255);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the SSI interface.
|
||||
*
|
||||
* Return 0 in case of success, a negative value otherwise.
|
||||
*/
|
||||
int lm3s_ssiOpen(uint32_t addr, uint32_t frame, int mode,
|
||||
int bitrate, uint32_t data_width)
|
||||
{
|
||||
struct SSIClock ssi_clock;
|
||||
|
||||
ASSERT(addr == SSI0_BASE || addr == SSI1_BASE);
|
||||
/* Configure the SSI operating mode */
|
||||
switch (mode)
|
||||
{
|
||||
/* SSI Slave Mode Output Disable */
|
||||
case SSI_MODE_SLAVE_OD:
|
||||
HWREG(addr + SSI_O_CR1) = SSI_CR1_SOD;
|
||||
break;
|
||||
/* SSI Slave */
|
||||
case SSI_MODE_SLAVE:
|
||||
HWREG(addr + SSI_O_CR1) = SSI_CR1_MS;
|
||||
break;
|
||||
/* SSI Master */
|
||||
case SSI_MODE_MASTER:
|
||||
HWREG(addr + SSI_O_CR1) = 0;
|
||||
break;
|
||||
default:
|
||||
ASSERT(0);
|
||||
return -1;
|
||||
}
|
||||
/* Configure the peripheral clock and frame format */
|
||||
ssi_clock = lm3s_ssiPrescale(bitrate);
|
||||
HWREG(addr + SSI_O_CPSR) = ssi_clock.cpsdvsr;
|
||||
HWREG(addr + SSI_O_CR0) =
|
||||
(ssi_clock.scr << 8) |
|
||||
((frame & 3) << 6) |
|
||||
(frame & SSI_CR0_FRF_M) |
|
||||
(data_width - 1);
|
||||
/* Enable the SSI interface */
|
||||
HWREG(addr + SSI_O_CR1) |= SSI_CR1_SSE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write data to the SSI bus.
|
||||
*
|
||||
* Return the number of bytes written to the bus.
|
||||
*/
|
||||
static size_t lm3s_ssiWrite(struct KFile *fd, const void *buf, size_t size)
|
||||
{
|
||||
LM3SSSI *fds = LM3SSSI_CAST(fd);
|
||||
const char *p = (const char *)buf;
|
||||
uint32_t frame;
|
||||
size_t count = 0;
|
||||
|
||||
while (count < size)
|
||||
{
|
||||
frame = p[count];
|
||||
if (fds->flags & LM3S_SSI_NONBLOCK)
|
||||
{
|
||||
if (!lm3s_ssiWriteFrameNonBlocking(fds->addr,
|
||||
frame))
|
||||
break;
|
||||
}
|
||||
else
|
||||
lm3s_ssiWriteFrame(fds->addr, frame);
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read data from the SSI bus.
|
||||
*
|
||||
* Return the number of bytes read from the bus.
|
||||
*/
|
||||
static size_t lm3s_ssiRead(struct KFile *fd, void *buf, size_t size)
|
||||
{
|
||||
LM3SSSI *fds = LM3SSSI_CAST(fd);
|
||||
|
||||
uint8_t *p = (uint8_t *)buf;
|
||||
uint32_t frame;
|
||||
size_t count = 0;
|
||||
|
||||
while (count < size)
|
||||
{
|
||||
if (fds->flags & LM3S_SSI_NONBLOCK)
|
||||
{
|
||||
if (!lm3s_ssiReadFrameNonBlocking(fds->addr, &frame))
|
||||
break;
|
||||
}
|
||||
else
|
||||
lm3s_ssiReadFrame(fds->addr, &frame);
|
||||
*p++ = (uint8_t)frame;
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
/* Wait for data in the TX FIFO being actually transmitted */
|
||||
static int lm3s_ssiFlush(struct KFile *fd)
|
||||
{
|
||||
LM3SSSI *fds = LM3SSSI_CAST(fd);
|
||||
|
||||
while (!lm3s_ssiTxDone(fds->addr))
|
||||
cpu_relax();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Disable the SSI interface */
|
||||
static int lm3s_ssiClose(struct KFile *fd)
|
||||
{
|
||||
LM3SSSI *fds = LM3SSSI_CAST(fd);
|
||||
|
||||
lm3s_ssiFlush(fd);
|
||||
HWREG(fds->addr + SSI_O_CR1) &= ~SSI_CR1_SSE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a LM3S SSI driver.
|
||||
*/
|
||||
void lm3s_ssiInit(struct LM3SSSI *fds, uint32_t addr, uint32_t frame, int mode,
|
||||
int bitrate, uint32_t data_width)
|
||||
{
|
||||
memset(fds, 0, sizeof(*fds));
|
||||
DB(fds->fd._type = KFT_LM3SSSI);
|
||||
|
||||
/* TODO: only 8-bit frame size is supported */
|
||||
ASSERT(data_width == 8);
|
||||
|
||||
fds->fd.write = lm3s_ssiWrite;
|
||||
fds->fd.read = lm3s_ssiRead;
|
||||
fds->fd.close = lm3s_ssiClose;
|
||||
fds->fd.flush = lm3s_ssiFlush;
|
||||
|
||||
fds->addr = addr;
|
||||
lm3s_ssiOpen(addr, frame, mode, bitrate, data_width);
|
||||
}
|
191
bertos/cpu/cortex-m3/drv/ssi_lm3s.h
Normal file
191
bertos/cpu/cortex-m3/drv/ssi_lm3s.h
Normal file
|
@ -0,0 +1,191 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief LM3S1968 Synchronous Serial Interface (SSI) driver.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SSI_LM3S_H
|
||||
#define SSI_LM3S_H
|
||||
|
||||
#include <cpu/power.h> /* cpu_relax() */
|
||||
#include <io/kfile.h> /* KFile */
|
||||
#include <io/lm3s.h>
|
||||
|
||||
/**
|
||||
* LM3S1968 SSI frame format
|
||||
*/
|
||||
/*\{*/
|
||||
#define SSI_FRF_MOTO_MODE_0 0x00000000 //< Moto fmt, polarity 0, phase 0
|
||||
#define SSI_FRF_MOTO_MODE_1 0x00000002 //< Moto fmt, polarity 0, phase 1
|
||||
#define SSI_FRF_MOTO_MODE_2 0x00000001 //< Moto fmt, polarity 1, phase 0
|
||||
#define SSI_FRF_MOTO_MODE_3 0x00000003 //< Moto fmt, polarity 1, phase 1
|
||||
#define SSI_FRF_TI 0x00000010 //< TI frame format
|
||||
#define SSI_FRF_NMW 0x00000020 //< National MicroWire frame format
|
||||
/*\}*/
|
||||
|
||||
/**
|
||||
* LM3S1968 SSI operational mode
|
||||
*/
|
||||
/*\{*/
|
||||
#define SSI_MODE_MASTER 0x00000000 //< SSI master
|
||||
#define SSI_MODE_SLAVE 0x00000001 //< SSI slave
|
||||
#define SSI_MODE_SLAVE_OD 0x00000002 //< SSI slave with output disabled
|
||||
/*\}*/
|
||||
|
||||
/* LM3S SSI handle properties */
|
||||
enum
|
||||
{
|
||||
/* Non-blocking I/O */
|
||||
LM3S_SSI_NONBLOCK = 1,
|
||||
};
|
||||
|
||||
/** LM3S1968 SSI handle structure */
|
||||
typedef struct LM3SSSI
|
||||
{
|
||||
/* SSI Kfile structure */
|
||||
KFile fd;
|
||||
|
||||
/* Handle properties */
|
||||
uint32_t flags;
|
||||
|
||||
/* SSI port address */
|
||||
uint32_t addr;
|
||||
} LM3SSSI;
|
||||
|
||||
/**
|
||||
* ID for LM3S SSI.
|
||||
*/
|
||||
#define KFT_LM3SSSI MAKE_ID('L', 'S', 'S', 'I')
|
||||
|
||||
INLINE LM3SSSI *LM3SSSI_CAST(KFile *fd)
|
||||
{
|
||||
ASSERT(fd->_type == KFT_LM3SSSI);
|
||||
return (LM3SSSI *)fd;
|
||||
}
|
||||
|
||||
/* KFile interface to LM3S SSI */
|
||||
void lm3s_ssiInit(struct LM3SSSI *fds, uint32_t addr, uint32_t frame, int mode,
|
||||
int bitrate, uint32_t data_width);
|
||||
|
||||
/* Raw interface to LM3S SSI */
|
||||
int lm3s_ssiOpen(uint32_t addr, uint32_t frame, int mode,
|
||||
int bitrate, uint32_t data_width);
|
||||
|
||||
/*
|
||||
* Check if the SSI transmitter is busy or not
|
||||
*
|
||||
* This allows to determine whether the TX FIFO have been cleared by the
|
||||
* hardware, so the transmission can be safely considered completed.
|
||||
*/
|
||||
INLINE bool lm3s_ssiTxDone(uint32_t base)
|
||||
{
|
||||
return (HWREG(base + SSI_O_SR) & SSI_SR_BSY) ? true : false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the SSI TX FIFO is full
|
||||
*/
|
||||
INLINE bool lm3s_ssiTxReady(uint32_t base)
|
||||
{
|
||||
return (HWREG(base + SSI_O_SR) & SSI_SR_TNF) ? true : false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for data available in the RX FIFO
|
||||
*/
|
||||
INLINE bool lm3s_ssiRxReady(uint32_t base)
|
||||
{
|
||||
return (HWREG(base + SSI_O_SR) & SSI_SR_RNE) ? true : false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a frame into the SSI receive FIFO without blocking.
|
||||
*
|
||||
* Return the number of frames read from the RX FIFO.
|
||||
*/
|
||||
INLINE int lm3s_ssiReadFrameNonBlocking(uint32_t base, uint32_t *val)
|
||||
{
|
||||
/* Check for data available in the RX FIFO */
|
||||
if (!lm3s_ssiRxReady(base))
|
||||
return 0;
|
||||
/* Read data from SSI RX FIFO */
|
||||
*val = HWREG(base + SSI_O_DR);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a frame from the SSI receive FIFO.
|
||||
*/
|
||||
INLINE void lm3s_ssiReadFrame(uint32_t base, uint32_t *val)
|
||||
{
|
||||
/* Wait for data available in the RX FIFO */
|
||||
while (!lm3s_ssiRxReady(base))
|
||||
cpu_relax();
|
||||
/* Read data from SSI RX FIFO */
|
||||
*val = HWREG(base + SSI_O_DR);
|
||||
}
|
||||
|
||||
/*
|
||||
* Put a frame into the SSI transmit FIFO without blocking.
|
||||
*
|
||||
* NOTE: the upper bits of the frame will be automatically discarded by the
|
||||
* hardware according to the frame data width.
|
||||
*
|
||||
* Return the number of frames written to the TX FIFO.
|
||||
*/
|
||||
INLINE int lm3s_ssiWriteFrameNonBlocking(uint32_t base, uint32_t val)
|
||||
{
|
||||
/* Check for available space in the TX FIFO */
|
||||
if (!lm3s_ssiTxReady(base))
|
||||
return 0;
|
||||
/* Enqueue data to the TX FIFO */
|
||||
HWREG(base + SSI_O_DR) = val;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Put a frame into the SSI transmit FIFO.
|
||||
*
|
||||
* NOTE: the upper bits of the frame will be automatically discarded by the
|
||||
* hardware according to the frame data width.
|
||||
*/
|
||||
INLINE void lm3s_ssiWriteFrame(uint32_t base, uint32_t val)
|
||||
{
|
||||
/* Wait for available space in the TX FIFO */
|
||||
while (!lm3s_ssiTxReady(base))
|
||||
cpu_relax();
|
||||
/* Enqueue data to the TX FIFO */
|
||||
HWREG(base + SSI_O_DR) = val;
|
||||
}
|
||||
|
||||
#endif /* SSI_LM3S_H */
|
74
bertos/cpu/cortex-m3/drv/timer_cm3.c
Normal file
74
bertos/cpu/cortex-m3/drv/timer_cm3.c
Normal file
|
@ -0,0 +1,74 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \brief Low-level timer driver (SysTick) for LM3S1968.
|
||||
*
|
||||
* \author Andrea Righi <arighi@develer.com>
|
||||
*/
|
||||
|
||||
#include "timer_cm3.h"
|
||||
|
||||
#include <cfg/debug.h>
|
||||
|
||||
#include <cpu/irq.h>
|
||||
|
||||
#include <drv/irq_cm3.h>
|
||||
|
||||
INLINE void timer_hw_setPeriod(unsigned long period)
|
||||
{
|
||||
ASSERT(period < (1 << 24));
|
||||
NVIC_ST_RELOAD_R = period - 1;
|
||||
}
|
||||
|
||||
static void timer_hw_enable(void)
|
||||
{
|
||||
NVIC_ST_CTRL_R |=
|
||||
NVIC_ST_CTRL_CLK_SRC | NVIC_ST_CTRL_ENABLE | NVIC_ST_CTRL_INTEN;
|
||||
}
|
||||
|
||||
static void timer_hw_disable(void)
|
||||
{
|
||||
NVIC_ST_CTRL_R &= ~(NVIC_ST_CTRL_ENABLE | NVIC_ST_CTRL_INTEN);
|
||||
}
|
||||
|
||||
void timer_hw_init(void)
|
||||
{
|
||||
timer_hw_setPeriod(CPU_FREQ / TIMER_TICKS_PER_SEC);
|
||||
sysirq_setHandler(FAULT_SYSTICK, timer_handler);
|
||||
timer_hw_enable();
|
||||
}
|
||||
|
||||
void timer_hw_exit(void)
|
||||
{
|
||||
timer_hw_disable();
|
||||
sysirq_freeHandler(FAULT_SYSTICK);
|
||||
}
|
109
bertos/cpu/cortex-m3/drv/timer_cm3.h
Normal file
109
bertos/cpu/cortex-m3/drv/timer_cm3.h
Normal file
|
@ -0,0 +1,109 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \author Andrea Righi <arighi@develer.com>
|
||||
*
|
||||
* \brief Low-level timer driver (SysTick) for Cortex-M3.
|
||||
*/
|
||||
|
||||
#ifndef TIMER_CM3_H
|
||||
#define TIMER_CM3_H
|
||||
|
||||
#include "cfg/cfg_timer.h" /* CONFIG_TIMER */
|
||||
|
||||
#include <cpu/detect.h>
|
||||
#include <cpu/irq.h>
|
||||
|
||||
#if CPU_CM3_LM3S
|
||||
#include <io/lm3s.h>
|
||||
#elif CPU_CM3_STM32
|
||||
#include <io/stm32.h>
|
||||
#elif CPU_CM3_SAM3
|
||||
#include <io/sam3.h>
|
||||
/*#elif Add other families here */
|
||||
#else
|
||||
#error Unknown CPU
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \name Values for CONFIG_TIMER.
|
||||
*
|
||||
* Select which hardware timer interrupt to use for system clock and softtimers.
|
||||
*
|
||||
* $WIZ$ timer_select = "TIMER_DEFAULT", "TIMER_ON_GPTM"
|
||||
*/
|
||||
#define TIMER_ON_GPTM 1
|
||||
|
||||
#define TIMER_DEFAULT TIMER_ON_GPTM ///< Default system timer
|
||||
|
||||
#if (CONFIG_TIMER == TIMER_ON_GPTM)
|
||||
/* Ticks frequency (HZ) */
|
||||
#define TIMER_TICKS_PER_SEC 1000
|
||||
|
||||
/* Frequency of the hardware high-precision timer. */
|
||||
#define TIMER_HW_HPTICKS_PER_SEC (CPU_FREQ)
|
||||
|
||||
/* Maximum value of the high-precision hardware counter register */
|
||||
#define TIMER_HW_CNT (CPU_FREQ / TIMER_TICKS_PER_SEC)
|
||||
|
||||
/** Type of time expressed in ticks of the hardware high-precision timer */
|
||||
typedef uint32_t hptime_t;
|
||||
#define SIZEOF_HPTIME_T 4
|
||||
|
||||
/* Timer ISR prototype */
|
||||
ISR_PROTO_CONTEXT_SWITCH(timer_handler);
|
||||
#define DEFINE_TIMER_ISR DECLARE_ISR_CONTEXT_SWITCH(timer_handler)
|
||||
|
||||
INLINE void timer_hw_irq(void)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
INLINE bool timer_hw_triggered(void)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
INLINE hptime_t timer_hw_hpread(void)
|
||||
{
|
||||
return (TIMER_HW_CNT - NVIC_ST_CURRENT_R);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#error Unimplemented value for CONFIG_TIMER
|
||||
#endif /* CONFIG_TIMER */
|
||||
|
||||
void timer_hw_init(void);
|
||||
void timer_hw_exit(void);
|
||||
|
||||
#endif /* TIMER_CM3_H */
|
1943
bertos/cpu/cortex-m3/drv/usb_stm32.c
Normal file
1943
bertos/cpu/cortex-m3/drv/usb_stm32.c
Normal file
File diff suppressed because it is too large
Load diff
233
bertos/cpu/cortex-m3/drv/usb_stm32.h
Normal file
233
bertos/cpu/cortex-m3/drv/usb_stm32.h
Normal file
|
@ -0,0 +1,233 @@
|
|||
/**
|
||||
* \file
|
||||
* <!--
|
||||
* This file is part of BeRTOS.
|
||||
*
|
||||
* Bertos 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 2 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
|
||||
*
|
||||
* As a special exception, you may use this file as part of a free software
|
||||
* library without restriction. Specifically, if other files instantiate
|
||||
* templates or use macros or inline functions from this file, or you compile
|
||||
* this file and link it with other files to produce an executable, this
|
||||
* file does not by itself cause the resulting executable to be covered by
|
||||
* the GNU General Public License. This exception does not however
|
||||
* invalidate any other reasons why the executable file might be covered by
|
||||
* the GNU General Public License.
|
||||
*
|
||||
* Copyright 2010 Develer S.r.l. (http://www.develer.com/)
|
||||
*
|
||||
* -->
|
||||
*
|
||||
* \author Andrea Righi <arighi@develer.com>
|
||||
*
|
||||
* \brief STM32: USB full-speed device driver
|
||||
*
|
||||
* Low-level USB device driver for the STM32 architecture.
|
||||
*/
|
||||
|
||||
#ifndef USB_STM32_H
|
||||
#define USB_STM32_H
|
||||
|
||||
#include <cfg/compiler.h>
|
||||
#include <drv/usb.h>
|
||||
#include <drv/usb_endpoint.h>
|
||||
|
||||
#define USB_BASE_ADDR 0x40005C00
|
||||
|
||||
#define USB_DM_PIN (1 << 11)
|
||||
#define USB_DP_PIN (1 << 12)
|
||||
#define USB_DISC_PIN (1 << 11)
|
||||
|
||||
#define USB_EP0_MAX_SIZE CONFIG_EP0_MAX_SIZE
|
||||
#define USB_XFER_MAX_SIZE 64
|
||||
|
||||
#define EP_MAX_SLOTS USB_EP_MAX
|
||||
#define EP_MAX_NUM (EP_MAX_SLOTS << 1)
|
||||
|
||||
/* USB packet memory organization */
|
||||
#define USB_PACKET_MEMORY_BASE 0x40006000
|
||||
#define USB_PACKET_MEMORY_SIZE 512
|
||||
|
||||
/* Offset of the buffer descriptor table inside the packet memory */
|
||||
#define USB_BDT_OFFSET \
|
||||
((USB_PACKET_MEMORY_SIZE - (sizeof(stm32_UsbBd) * EP_MAX_NUM)) & ~7)
|
||||
|
||||
#define USB_MEM_ADDR(offset) \
|
||||
(USB_PACKET_MEMORY_BASE + ((offset << 1) & ~3) + (offset & 1))
|
||||
|
||||
#define EP_DTB_READ(slot, offset) \
|
||||
(*((uint16_t *)(USB_MEM_ADDR((USB_BDT_OFFSET + \
|
||||
(slot) * sizeof(stm32_UsbBd) + \
|
||||
(offset))))))
|
||||
|
||||
#define EP_DTB_WRITE(slot, offset, data) (EP_DTB_READ(slot, offset) = data)
|
||||
|
||||
#define ADDR_TX_OFFSET offsetof(stm32_UsbBd, AddrTx)
|
||||
#define COUNT_TX_OFFSET offsetof(stm32_UsbBd, CountTx)
|
||||
#define ADDR_RX_OFFSET offsetof(stm32_UsbBd, AddrRx)
|
||||
#define COUNT_RX_OFFSET offsetof(stm32_UsbBd, CountRx)
|
||||
|
||||
#define USB_CTRL_RW_MASK 0x070F
|
||||
#define USB_CTRL_CLEAR_ONLY_MASK 0x8080
|
||||
#define USB_CTRL_TOGGLE_MASK 0x7070
|
||||
|
||||
/* CNTR register flags */
|
||||
#define bmCTRM 0x8000
|
||||
#define bmPMAOVRM 0x4000
|
||||
#define bmERRM 0x2000
|
||||
#define bmWKUPM 0x1000
|
||||
#define bmSUSPM 0x0800
|
||||
#define bmRESETM 0x0400
|
||||
#define bmSOFM 0x0200
|
||||
#define bmESOFM 0x0100
|
||||
|
||||
#define bmRESUME 0x0010
|
||||
#define bmFSUSP 0x0008
|
||||
#define bmLPMODE 0x0004
|
||||
#define bmPDWN 0x0002
|
||||
#define bmFRES 0x0001
|
||||
|
||||
/* USB error codes */
|
||||
enum stm32_usb_error
|
||||
{
|
||||
USB_OK = 0,
|
||||
USB_INTR_ERROR,
|
||||
USB_INVAL_ERROR,
|
||||
USB_NODEV_ERROR,
|
||||
USB_MEMORY_FULL,
|
||||
USB_BUF_OVERFLOW,
|
||||
USB_EP_STALLED,
|
||||
USB_FATAL_ERROR,
|
||||
};
|
||||
|
||||
/* STM32 USB endpoint types */
|
||||
enum stm32_UsbEpype
|
||||
{
|
||||
EP_BULK = 0,
|
||||
EP_CTRL,
|
||||
EP_ISO,
|
||||
EP_INTERRUPT,
|
||||
|
||||
EP_TYPE_MAX
|
||||
};
|
||||
|
||||
/* STM32 USB interrupt status register bits */
|
||||
typedef union
|
||||
{
|
||||
uint32_t status;
|
||||
struct {
|
||||
uint8_t EP_ID : 4;
|
||||
uint8_t DIR : 1;
|
||||
uint8_t : 2;
|
||||
uint8_t SZDPR : 1;
|
||||
uint8_t ESOF : 1;
|
||||
uint8_t SOF : 1;
|
||||
uint8_t RESET : 1;
|
||||
uint8_t SUSP : 1;
|
||||
uint8_t WKUP : 1;
|
||||
uint8_t ERR : 1;
|
||||
uint8_t PMAOVR : 1;
|
||||
uint8_t CTR : 1;
|
||||
};
|
||||
} PACKED stm32_usb_irq_status_t;
|
||||
|
||||
/* Endpoint state */
|
||||
typedef enum
|
||||
{
|
||||
EP_DISABLED = 0,
|
||||
EP_STALL,
|
||||
EP_NAK,
|
||||
EP_VALID
|
||||
} stm32_UsbEpState;
|
||||
|
||||
/* STM32 USB supported endpoints */
|
||||
typedef enum stm32_UsbEP
|
||||
{
|
||||
CTRL_ENP_OUT = 0, CTRL_ENP_IN,
|
||||
ENP1_OUT, ENP1_IN,
|
||||
ENP2_OUT, ENP2_IN,
|
||||
ENP3_OUT, ENP3_IN,
|
||||
ENP4_OUT, ENP4_IN,
|
||||
ENP5_OUT, ENP5_IN,
|
||||
ENP6_OUT, ENP6_IN,
|
||||
ENP7_OUT, ENP7_IN,
|
||||
ENP8_OUT, ENP8_IN,
|
||||
ENP9_OUT, ENP9_IN,
|
||||
ENP10_OUT, ENP10_IN,
|
||||
ENP11_OUT, ENP11_IN,
|
||||
ENP12_OUT, ENP12_IN,
|
||||
ENP13_OUT, ENP13_IN,
|
||||
ENP14_OUT, ENP14_IN,
|
||||
ENP15_OUT, ENP15_IN,
|
||||
|
||||
EP_MAX_HW_NUM
|
||||
} stm32_UsbEP;
|
||||
|
||||
/* STM32 USB packet memory slot */
|
||||
typedef struct stm32_UsbMemSlot
|
||||
{
|
||||
stm32_UsbEP ep_addr;
|
||||
uint16_t Start;
|
||||
uint16_t Size;
|
||||
struct stm32_UsbMemSlot *next;
|
||||
} stm32_UsbMemSlot;
|
||||
|
||||
/* STM32 USB buffer descriptor (packet memory) */
|
||||
typedef struct stm32_UsbBd
|
||||
{
|
||||
uint16_t AddrTx;
|
||||
uint16_t CountTx;
|
||||
uint16_t AddrRx;
|
||||
uint16_t CountRx;
|
||||
} PACKED stm32_UsbBd;
|
||||
|
||||
/* STM32 USB endpoint I/O status */
|
||||
typedef enum stm32_UsbIoStatus
|
||||
{
|
||||
NOT_READY = 0,
|
||||
NO_SERVICED,
|
||||
BEGIN_SERVICED,
|
||||
COMPLETE,
|
||||
BUFFER_UNDERRUN,
|
||||
BUFFER_OVERRUN,
|
||||
SETUP_OVERWRITE,
|
||||
STALLED,
|
||||
} stm32_UsbIoStatus;
|
||||
|
||||
/* STM32 USB hardware endpoint descriptor */
|
||||
typedef struct stm32_UsbEp
|
||||
{
|
||||
reg32_t *hw;
|
||||
uint8_t type;
|
||||
void (*complete)(int);
|
||||
ssize_t max_size;
|
||||
ssize_t offset;
|
||||
ssize_t size;
|
||||
stm32_UsbIoStatus status;
|
||||
union
|
||||
{
|
||||
uint8_t *read_buffer;
|
||||
const uint8_t *write_buffer;
|
||||
};
|
||||
int32_t avail_data;
|
||||
uint8_t flags;
|
||||
} stm32_UsbEp;
|
||||
|
||||
/* STM32 USB hardware endpoint flags */
|
||||
#define STM32_USB_EP_AVAIL_DATA BV(0)
|
||||
#define STM32_USB_EP_ZERO_PACKET BV(1)
|
||||
#define STM32_USB_EP_ZERO_POSSIBLE BV(2)
|
||||
|
||||
#endif /* USB_STM32_H */
|
Loading…
Add table
Add a link
Reference in a new issue