From fd20f0fa17bfdf81dc28e7d30f13ca80264b7c78 Mon Sep 17 00:00:00 2001 From: Andrew Wygle Date: Fri, 8 Apr 2016 01:43:46 -0700 Subject: Initial MSP430X Port. This port includes hal, pal, serial, and st drivers. It supports both large and small model code and data for MSP430X-class CPUs. It has only been tested on the EXP430FR5969 LaunchPad board. --- os/hal/ports/MSP430X/hal_lld.c | 87 +++++ os/hal/ports/MSP430X/hal_lld.h | 245 +++++++++++++ os/hal/ports/MSP430X/hal_pal_lld.c | 215 +++++++++++ os/hal/ports/MSP430X/hal_pal_lld.h | 390 ++++++++++++++++++++ os/hal/ports/MSP430X/hal_serial_lld.c | 657 ++++++++++++++++++++++++++++++++++ os/hal/ports/MSP430X/hal_serial_lld.h | 320 +++++++++++++++++ os/hal/ports/MSP430X/hal_st_lld.c | 206 +++++++++++ os/hal/ports/MSP430X/hal_st_lld.h | 216 +++++++++++ os/hal/ports/MSP430X/platform.mk | 8 + 9 files changed, 2344 insertions(+) create mode 100644 os/hal/ports/MSP430X/hal_lld.c create mode 100644 os/hal/ports/MSP430X/hal_lld.h create mode 100644 os/hal/ports/MSP430X/hal_pal_lld.c create mode 100644 os/hal/ports/MSP430X/hal_pal_lld.h create mode 100644 os/hal/ports/MSP430X/hal_serial_lld.c create mode 100644 os/hal/ports/MSP430X/hal_serial_lld.h create mode 100644 os/hal/ports/MSP430X/hal_st_lld.c create mode 100644 os/hal/ports/MSP430X/hal_st_lld.h create mode 100644 os/hal/ports/MSP430X/platform.mk (limited to 'os/hal/ports/MSP430X') diff --git a/os/hal/ports/MSP430X/hal_lld.c b/os/hal/ports/MSP430X/hal_lld.c new file mode 100644 index 0000000..872fe97 --- /dev/null +++ b/os/hal/ports/MSP430X/hal_lld.c @@ -0,0 +1,87 @@ +/* + ChibiOS - Copyright (C) 2016 Andrew Wygle aka awygle + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file MSP430X/hal_lld.c + * @brief MSP430X HAL subsystem low level driver source. + * + * @addtogroup HAL + * @{ + */ + +#include "hal.h" + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level HAL driver initialization. + * + * @notapi + */ +void hal_lld_init(void) { + /* Disable watchdog */ + /* TODO Real watchdog support */ + WDTCTL = WDTPW | WDTHOLD; + /* Init clock system */ + CSCTL0 = CSKEY; /* unlock clock system */ + CSCTL1 = MSP430X_DCOSEL; + CSCTL2 = (MSP430X_ACLK_SRC << 8) | (MSP430X_SMCLK_SRC << 4) | (MSP430X_MCLK_SRC); + CSCTL3 = (DIVIDER(MSP430X_ACLK_DIV) << 8) | (DIVIDER(MSP430X_SMCLK_DIV) << 4) | (DIVIDER(MSP430X_MCLK_DIV)); + CSCTL4 = (MSP430X_HFXTCLK_DRIVE << 14) | (MSP430X_HFXTCLK_BYPASS << 12) | (MSP430X_HFFREQ << 10) | HFXTOFF | \ + (MSP430X_LFXTCLK_DRIVE << 6) | (MSP430X_LFXTCLK_BYPASS << 4) | VLOOFF | LFXTOFF; + CSCTL6 = (MODCLKREQEN) | (SMCLKREQEN) | (MCLKREQEN) | (ACLKREQEN); + #if defined(MSP430X_USE_HFXT) && defined(MSP430X_USE_LFXT) + do { + CSCTL5 &= ~(HFXTOFFG | LFXTOFFG); + SFRIFG1 &= ~OFIFG; + } while (SFRIFG1 & OFIFG); + #elif defined(MSP430X_USE_HFXT) + do { + CSCTL5 &= ~(HFXTOFFG); + SFRIFG1 &= ~OFIFG; + } while (SFRIFG1 & OFIFG); + #elif defined(MSP430X_USE_LFXT) + do { + CSCTL5 &= ~(LFXTOFFG); + SFRIFG1 &= ~OFIFG; + } while (SFRIFG1 & OFIFG); + #endif + CSCTL0_H = 0xFF; /* Lock clock system */ +} + +/** @} */ diff --git a/os/hal/ports/MSP430X/hal_lld.h b/os/hal/ports/MSP430X/hal_lld.h new file mode 100644 index 0000000..9549453 --- /dev/null +++ b/os/hal/ports/MSP430X/hal_lld.h @@ -0,0 +1,245 @@ +/* + ChibiOS - Copyright (C) 2016 Andrew Wygle aka awygle + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file MSP430X/hal_lld.h + * @brief MSP430X HAL subsystem low level driver header. + * + * @addtogroup HAL + * @{ + */ + +#ifndef _HAL_LLD_H_ +#define _HAL_LLD_H_ + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/** + * @brief Defines the support for realtime counters in the HAL. + */ +/* someday*/ +#define HAL_IMPLEMENTS_COUNTERS FALSE + +/** + * @name Platform identification macros + * @{ + */ +#define PLATFORM_NAME "MSP430X" +/** @} */ + +#define MSP430X_LFXTCLK 0 +#define MSP430X_VLOCLK 1 +#define MSP430X_LFMODCLK 2 +#define MSP430X_DCOCLK 3 +#define MSP430X_MODCLK 4 +#define MSP430X_HFXTCLK 5 + +#if !defined(MSP430X_LFXTCLK_FREQ) + #define MSP430X_LFXTCLK_FREQ 32768 + #warning "LFXTCLK freqency not defined - assuming 32768 Hz" +#endif +#define MSP430X_VLOCLK_FREQ 10000 +#define MSP430X_MODCLK_FREQ 5000000 +#define MSP430X_LFMODCLK_FREQ (MSP430X_MODCLK_FREQ/128) +#if !defined(MSP430X_DCOCLK_FREQ) + #define MSP430X_DCOCLK_FREQ 8000000 + #warning "DCOCLK frequency not defined - assuming 8 MHz" +#endif +#if !defined(MSP430X_HFXTCLK_FREQ) + #define MSP430X_HFXTCLK_FREQ 0 + #warning "HFXTCLK frequency not defined - assuming disabled" +#endif + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name MSP430X configuration options + * @{ + */ + +/* Clock dividers */ +#if !defined(MSP430X_ACLK_DIV) + #define MSP430X_ACLK_DIV 1 +#endif +#if !defined(MSP430X_MCLK_DIV) + #define MSP430X_MCLK_DIV 8 +#endif +#if !defined(MSP430X_SMCLK_DIV) + #define MSP430X_SMCLK_DIV 8 +#endif + +/* Clock sources */ +#if !defined(MSP430X_ACLK_SRC) + #define MSP430X_ACLK_SRC MSP430X_LFXTCLK +#endif +#if !defined(MSP430X_MCLK_SRC) + #define MSP430X_MCLK_SRC MSP430X_DCOCLK +#endif +#if !defined(MSP430X_SMCLK_SRC) + #define MSP430X_SMCLK_SRC MSP430X_DCOCLK +#endif + +/* HFXT and LFXT settings */ +#if !defined(MSP430X_LFXTCLK_BYPASS) + #define MSP430X_LFXTCLK_BYPASS 0 +#endif +#if !defined(MSP430X_LFXTCLK_DRIVE) + #define MSP430X_LFXTCLK_DRIVE 3 +#endif +#if !defined(MSP430X_HFXTCLK_BYPASS) + #define MSP430X_HFXTCLK_BYPASS 0 +#endif +#if !defined(MSP430X_HFXTCLK_DRIVE) + #define MSP430X_HFXTCLK_DRIVE 3 +#endif + +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +/* + * Configuration-related checks. + */ +#if (MSP430X_ACLK_SRC == MSP430X_LFXTCLK) || (MSP430X_MCLK_SRC == MSP430X_LFXTCLK) || (MSP430X_SMCLK_SRC == MSP430X_LFXTCLK) + #define MSP430X_USE_LFXTCLK +#endif +#if (MSP430X_MCLK_SRC == MSP430X_HFXTCLK) || (MSP430X_SMCLK_SRC == MSP430X_HFXTCLK) + #define MSP430X_USE_HFXTCLK +#endif + +#if defined(MSP430X_USE_HFXTCLK) && MSP430X_HFXTCLK_FREQ == 0 + #error "HFXT requested as clock source but disabled" +#endif + +/* Clock speeds */ +#if (MSP430X_ACLK_SRC == MSP430X_LFXTCLK) + #define MSP430X_ACLK_FREQ (MSP430X_LFXTCLK_FREQ / MSP430X_ACLK_DIV) +#elif (MSP430X_ACLK_SRC == MSP430X_VLOCLK) + #define MSP430X_ACLK_FREQ (MSP430X_VLOCLK_FREQ / MSP430X_ACLK_DIV) +#elif (MSP430X_ACLK_SRC == MSP430X_LFMODCLK) + #define MSP430X_ACLK_FREQ (MSP430X_LFMODCLK_FREQ / MSP430X_ACLK_DIV) +#else + #error "ACLK source invalid!" +#endif +#if (MSP430X_MCLK_SRC == MSP430X_LFXTCLK) + #define MSP430X_MCLK_FREQ (MSP430X_LFXTCLK_FREQ / MSP430X_MCLK_DIV) +#elif (MSP430X_MCLK_SRC == MSP430X_VLOCLK) + #define MSP430X_MCLK_FREQ (MSP430X_VLOCLK_FREQ / MSP430X_MCLK_DIV) +#elif (MSP430X_MCLK_SRC == MSP430X_LFMODCLK) + #define MSP430X_MCLK_FREQ (MSP430X_LFMODCLK_FREQ / MSP430X_MCLK_DIV) +#elif (MSP430X_MCLK_SRC == MSP430X_DCOCLK) + #define MSP430X_MCLK_FREQ (MSP430X_DCOCLK_FREQ / MSP430X_MCLK_DIV) +#elif (MSP430X_MCLK_SRC == MSP430X_MODCLK) + #define MSP430X_MCLK_FREQ (MSP430X_MODCLK_FREQ / MSP430X_MCLK_DIV) +#elif (MSP430X_MCLK_SRC == MSP430X_HFXTCLK) + #define MSP430X_MCLK_FREQ (MSP430X_HFXTCLK_FREQ / MSP430X_MCLK_DIV) +#else + #error "MCLK source invalid!" +#endif +#if (MSP430X_SMCLK_SRC == MSP430X_LFXTCLK) + #define MSP430X_SMCLK_FREQ (MSP430X_LFXTCLK_FREQ / MSP430X_SMCLK_DIV) +#elif (MSP430X_SMCLK_SRC == MSP430X_VLOCLK) + #define MSP430X_SMCLK_FREQ (MSP430X_VLOCLK_FREQ / MSP430X_SMCLK_DIV) +#elif (MSP430X_SMCLK_SRC == MSP430X_LFMODCLK) + #define MSP430X_SMCLK_FREQ (MSP430X_LFMODCLK_FREQ / MSP430X_SMCLK_DIV) +#elif (MSP430X_SMCLK_SRC == MSP430X_DCOCLK) + #define MSP430X_SMCLK_FREQ (MSP430X_DCOCLK_FREQ / MSP430X_SMCLK_DIV) +#elif (MSP430X_SMCLK_SRC == MSP430X_MODCLK) + #define MSP430X_SMCLK_FREQ (MSP430X_MODCLK_FREQ / MSP430X_SMCLK_DIV) +#elif (MSP430X_SMCLK_SRC == MSP430X_HFXTCLK) + #define MSP430X_SMCLK_FREQ (MSP430X_HFXTCLK_FREQ / MSP430X_SMCLK_DIV) +#else + #error "SMCLK source invalid!" +#endif + +#if !defined(MSP430X_MCUCONF) +#error "Using an incorrect mcuconf.h file, MSP430X_MCUCONF not defined" +#endif + +/* HFXT-specific settings */ +#if MSP430X_HFXTCLK_FREQ <= 4000000 + #define MSP430X_HFFREQ HFFREQ_0 +#elif MSP430X_HFXTCLK_FREQ <= 8000000 + #define MSP430X_HFFREQ HFFREQ_1 +#elif MSP430X_HFXTCLK_FREQ <= 16000000 + #define MSP430X_HFFREQ HFFREQ_2 +#elif MSP430X_HFXTCLK_FREQ <= 24000000 + #define MSP430X_HFFREQ HFFREQ_3 +#else + #error "HFXT frequency too high - must be <= 24000000" +#endif + +/* DCO-specific settings */ +#if MSP430X_DCOCLK_FREQ == 1000000 + #define MSP430X_DCOSEL DCOFSEL_0 +#elif MSP430X_DCOCLK_FREQ == 2670000 + #define MSP430X_DCOSEL DCOFSEL_1 +#elif MSP430X_DCOCLK_FREQ == 3330000 + #define MSP430X_DCOSEL DCOFSEL_2 +#elif MSP430X_DCOCLK_FREQ == 4000000 + #define MSP430X_DCOSEL DCOFSEL_3 +#elif MSP430X_DCOCLK_FREQ == 5330000 + #define MSP430X_DCOSEL DCOFSEL_4 +#elif MSP430X_DCOCLK_FREQ == 6670000 + #define MSP430X_DCOSEL DCOFSEL_5 +#elif MSP430X_DCOCLK_FREQ == 8000000 + #define MSP430X_DCOSEL DCOFSEL_6 +#elif MSP430X_DCOCLK_FREQ == 16000000 + #define MSP430X_DCOSEL (DCORSEL | DCOFSEL_4) +#elif MSP430X_DCOCLK_FREQ == 21000000 + #define MSP430X_DCOSEL (DCORSEL | DCOFSEL_5) +#elif MSP430X_DCOCLK_FREQ == 24000000 + #define MSP430X_DCOSEL (DCORSEL | DCOFSEL_6) +#else + #error "DCO frequency invalid" +#endif + +#if MSP430X_LFXTCLK_FREQ > 50000 + #error "LFXT frequency too high - must be <= 5000" +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +#define DIVIDER(x) DIV_HELPER(x) +#define DIV_HELPER(x) DIVM__ ## x + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + void hal_lld_init(void); +#ifdef __cplusplus +} +#endif + +#endif /* _HAL_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/MSP430X/hal_pal_lld.c b/os/hal/ports/MSP430X/hal_pal_lld.c new file mode 100644 index 0000000..4cfff3b --- /dev/null +++ b/os/hal/ports/MSP430X/hal_pal_lld.c @@ -0,0 +1,215 @@ +/* + ChibiOS - Copyright (C) 2016 Andrew Wygle aka awygle + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file MSP430X/hal_pal_lld.c + * @brief MSP430X PAL subsystem low level driver source. + * + * @addtogroup PAL + * @{ + */ + +#include "hal.h" + +#if (HAL_USE_PAL == TRUE) || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief MSP430X I/O ports configuration. + * @details GPIO registers initialization + * + * @param[in] config the MSP430X ports configuration + * + * @notapi + */ +void _pal_lld_init(const PALConfig *config) { + +#if defined(PA_BASE) || defined(__DOXYGEN__) + PAOUT = config->porta.out; + PADIR = config->porta.dir; + PAREN = config->porta.ren; + PASEL0 = config->porta.sel0; + PASEL1 = config->porta.sel1; + PAIES = config->porta.ies; + PAIE = config->porta.ie; +#endif +#if defined(PB_BASE) || defined(__DOXYGEN__) + PBOUT = config->portb.out; + PBDIR = config->portb.dir; + PBREN = config->portb.ren; + PBSEL0 = config->portb.sel0; + PBSEL1 = config->portb.sel1; + PBIES = config->portb.ies; + PBIE = config->portb.ie; +#endif +#if defined(PC_BASE) || defined(__DOXYGEN__) + PCOUT = config->portc.out; + PCDIR = config->portc.dir; + PCREN = config->portc.ren; + PCSEL0 = config->portc.sel0; + PCSEL1 = config->portc.sel1; + PCIES = config->portc.ies; + PCIE = config->portc.ie; +#endif +#if defined(PD_BASE) || defined(__DOXYGEN__) + PDOUT = config->portd.out; + PDDIR = config->portd.dir; + PDREN = config->portd.ren; + PDSEL0 = config->portd.sel0; + PDSEL1 = config->portd.sel1; + PDIES = config->portd.ies; + PDIE = config->portd.ie; +#endif +#if defined(PE_BASE) || defined(__DOXYGEN__) + PEOUT = config->porte.out; + PEDIR = config->porte.dir; + PEREN = config->porte.ren; + PESEL0 = config->porte.sel0; + PESEL1 = config->porte.sel1; + PEIES = config->porte.ies; + PEIE = config->porte.ie; +#endif +#if defined(PF_BASE) || defined(__DOXYGEN__) + PFOUT = config->portf.out; + PFDIR = config->portf.dir; + PFREN = config->portf.ren; + PFSEL0 = config->portf.sel0; + PFSEL1 = config->portf.sel1; + PFIES = config->portf.ies; + PFIE = config->portf.ie; +#endif + PJOUT = config->portj.out; + PJDIR = config->portj.dir; + PJREN = config->portj.ren; + PJSEL0 = config->portj.sel0; + PJSEL1 = config->portj.sel1; + + PM5CTL0 &= ~LOCKLPM5; +} + +/** + * @brief Pads mode setup. + * @details This function programs a pads group belonging to the same port + * with the specified mode. + * @note @p PAL_MODE_UNCONNECTED is implemented as input with pullup. + * + * @param[in] port the port identifier + * @param[in] mask the group mask + * @param[in] mode the mode + * + * @notapi + */ +void _pal_lld_setgroupmode(ioportid_t port, + ioportmask_t mask, + iomode_t mode) { + + switch (mode) { + case PAL_MODE_RESET: + case PAL_MODE_INPUT: + port->dir &= ~mask; + port->ren &= ~mask; + if ((port->sel0 & mask) && (port->sel1 & mask)) + port->selc = mask; + else { + port->sel0 &= ~mask; + port->sel1 &= ~mask; + } + break; + case PAL_MODE_UNCONNECTED: + case PAL_MODE_INPUT_PULLUP: + port->dir &= ~mask; + port->ren |= mask; + port->out |= mask; + if ((port->sel0 & mask) && (port->sel1 & mask)) + port->selc = mask; + else { + port->sel0 &= ~mask; + port->sel1 &= ~mask; + } + break; + case PAL_MODE_INPUT_PULLDOWN: + port->dir &= ~mask; + port->ren |= mask; + port->out &= ~mask; + if ((port->sel0 & mask) && (port->sel1 & mask)) + port->selc = mask; + else { + port->sel0 &= ~mask; + port->sel1 &= ~mask; + } + break; + case PAL_MODE_OUTPUT_PUSHPULL: + port->dir |= mask; + if ((port->sel0 & mask) && (port->sel1 & mask)) + port->selc = mask; + else { + port->sel0 &= ~mask; + port->sel1 &= ~mask; + } + break; + case PAL_MSP430X_ALTERNATE_1: + if (!(port->sel0 & mask) && (port->sel1 & mask)) + port->selc = mask; + else { + port->sel0 |= mask; + port->sel1 &= ~mask; + } + break; + case PAL_MSP430X_ALTERNATE_2: + if ((port->sel0 & mask) && !(port->sel1 & mask)) + port->selc = mask; + else { + port->sel0 &= ~mask; + port->sel1 |= mask; + } + break; + case PAL_MSP430X_ALTERNATE_3: + if (!(port->sel0 & mask) && !(port->sel1 & mask)) + port->selc = mask; + else { + port->sel0 |= mask; + port->sel1 |= mask; + } + break; + } +} + +#endif /* HAL_USE_PAL == TRUE */ + +/** @} */ diff --git a/os/hal/ports/MSP430X/hal_pal_lld.h b/os/hal/ports/MSP430X/hal_pal_lld.h new file mode 100644 index 0000000..8789ed1 --- /dev/null +++ b/os/hal/ports/MSP430X/hal_pal_lld.h @@ -0,0 +1,390 @@ +/* + ChibiOS - Copyright (C) 2016 Andrew Wygle aka awygle + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file MSP430X/hal_pal_lld.h + * @brief MSP430X PAL subsystem low level driver header. + * + * @addtogroup PAL + * @{ + */ + +#ifndef HAL_PAL_LLD_H +#define HAL_PAL_LLD_H + +#if (HAL_USE_PAL == TRUE) || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Unsupported modes and specific modes */ +/*===========================================================================*/ + +#undef PAL_MODE_INPUT_ANALOG /* configure this through the ALTERNATE macros */ +#undef PAL_MODE_OUTPUT_OPENDRAIN + +/** + * @name MSP430X-specific I/O mode flags + * @{ + */ + +/** + * @brief Alternate mode 1 + */ +#define PAL_MSP430X_ALTERNATE_1 8 + +/** + * @brief Alternate mode 2 + */ +#define PAL_MSP430X_ALTERNATE_2 9 + +/** + * @brief Alternate mode 3 + */ +#define PAL_MSP430X_ALTERNATE_3 10 + +#define ALTERNATE_HELP(n) (PAL_MSP430X_ALTERNATE_ ## n) +/** + * @brief Alternate function. + * + * @param[in] n alternate function selector - 1 through 3 + */ +#define PAL_MODE_ALTERNATE(n) (ALTERNATE_HELP(n)) + +/** @} */ + +/*===========================================================================*/ +/* I/O Ports Types and constants. */ +/*===========================================================================*/ + +/** + * @name Port related definitions + * @{ + */ +/** + * @brief Width, in bits, of an I/O port. + */ +#define PAL_IOPORTS_WIDTH 16U + +/** + * @brief Whole port mask. + * @details This macro specifies all the valid bits into a port. + */ +#define PAL_WHOLE_PORT ((ioportmask_t)0xFFFFU) + +/** @} */ + + +/** + * @name Line handling macros + * @{ + */ +/** + * @brief Forms a line identifier. + * @details A port/pad pair are encoded into an @p ioline_t type. The encoding + * of this type is platform-dependent. + * @note In this driver the pad number is encoded in the upper 4 bits of + * the GPIO address which are guaranteed to be zero. + */ +#define PAL_LINE(port, pad) \ + ((ioline_t)((uint16_t)(port)) | (((uint16_t)(pad)) << 12)) + +/** + * @brief Decodes a port identifier from a line identifier. + */ +#define PAL_PORT(line) \ + ((msp430x_gpio_registers_t *)(((uint16_t)(line)) & 0x0FFFU)) + +/** + * @brief Decodes a pad identifier from a line identifier. + */ +#define PAL_PAD(line) \ + ((uint16_t)((uint16_t)(line) >> 12)) + +/** + * @brief Value identifying an invalid line. + */ +#define PAL_NOLINE 0U +/** @} */ + +/** + * @brief MSP430X register initialization + */ +typedef struct { + /** Initial value for OUT register.*/ + uint16_t out; + /** Initial value for DIR register.*/ + uint16_t dir; + /** Initial value for REN register.*/ + uint16_t ren; + /** Initial value for SEL0 register.*/ + uint16_t sel0; + /** Initial value for SEL1 register.*/ + uint16_t sel1; + /** Initial value for IES register.*/ + uint16_t ies; + /** Initial value for IE register.*/ + uint16_t ie; +} msp430x_gpio_setup_t; + +/** + * @brief MSP430X registers block + * @note Some ports do not support all of these fields. + */ +typedef struct { + volatile uint16_t in; + volatile uint16_t out; + volatile uint16_t dir; + volatile uint16_t _padding; + volatile uint16_t ren; + volatile uint16_t sel0; + volatile uint16_t sel1; + volatile uint16_t _padding1; + volatile uint16_t _padding2; + volatile uint16_t _padding3; + volatile uint16_t _padding4; + volatile uint16_t selc; + volatile uint16_t ies; + volatile uint16_t ie; + volatile uint16_t ifg; +} msp430x_gpio_registers_t; + +/** + * @brief MSP430X I/O ports static initializer. + * @details An instance of this structure must be passed to @p palInit() at + * system startup time in order to initialized the digital I/O + * subsystem. This represents only the initial setup, specific pads + * or whole ports can be reprogrammed at later time. + */ +typedef struct { +#if defined(PA_BASE) || defined(__DOXYGEN__) + msp430x_gpio_setup_t porta; +#endif +#if defined(PB_BASE) || defined(__DOXYGEN__) + msp430x_gpio_setup_t portb; +#endif +#if defined(PC_BASE) || defined(__DOXYGEN__) + msp430x_gpio_setup_t portc; +#endif +#if defined(PD_BASE) || defined(__DOXYGEN__) + msp430x_gpio_setup_t portd; +#endif +#if defined(PE_BASE) || defined(__DOXYGEN__) + msp430x_gpio_setup_t porte; +#endif +#if defined(PF_BASE) || defined(__DOXYGEN__) + msp430x_gpio_setup_t portf; +#endif + msp430x_gpio_setup_t portj; +} PALConfig; + +/** + * @brief Digital I/O port sized unsigned type. + */ +typedef uint16_t ioportmask_t; + +/** + * @brief Digital I/O modes. + */ +typedef uint16_t iomode_t; + +/** + * @brief Type of an I/O line. + */ +typedef uint16_t ioline_t; + +/** + * @brief Port Identifier. + * @details This type can be a scalar or some kind of pointer, do not make + * any assumption about it, use the provided macros when populating + * variables of this type. + */ +typedef msp430x_gpio_registers_t * ioportid_t; + +/*===========================================================================*/ +/* I/O Ports Identifiers. */ +/*===========================================================================*/ + +/** + * @brief GPIO port A identifier. + */ +#if defined(PA_BASE) || defined(__DOXYGEN__) +#define IOPORT1 ((volatile msp430x_gpio_registers_t *)PA_BASE) +#endif + +/** + * @brief GPIO port B identifier. + */ +#if defined(PB_BASE) || defined(__DOXYGEN__) +#define IOPORT2 ((volatile msp430x_gpio_registers_t *)PB_BASE) +#endif + +/** + * @brief GPIO port C identifier. + */ +#if defined(PC_BASE) || defined(__DOXYGEN__) +#define IOPORT3 ((volatile msp430x_gpio_registers_t *)PC_BASE) +#endif + +/** + * @brief GPIO port D identifier. + */ +#if defined(PD_BASE) || defined(__DOXYGEN__) +#define IOPORT4 ((volatile msp430x_gpio_registers_t *)PD_BASE) +#endif + +/** + * @brief GPIO port E identifier. + */ +#if defined(PE_BASE) || defined(__DOXYGEN__) +#define IOPORT5 ((volatile msp430x_gpio_registers_t *)PE_BASE) +#endif + +/** + * @brief GPIO port F identifier. + */ +#if defined(PF_BASE) || defined(__DOXYGEN__) +#define IOPORT6 ((volatile msp430x_gpio_registers_t *)PF_BASE +#endif + +/** + * @brief GPIO port J identifier. + */ +#define IOPORT0 ((volatile msp430x_gpio_registers_t *)PJ_BASE) + +/*===========================================================================*/ +/* Implementation, some of the following macros could be implemented as */ +/* functions, if so please put them in pal_lld.c. */ +/*===========================================================================*/ + +/** + * @brief Low level PAL subsystem initialization. + * + * @param[in] config architecture-dependent ports configuration + * + * @notapi + */ +#define pal_lld_init(config) _pal_lld_init(config) + +/** + * @brief Reads the physical I/O port states. + * + * @param[in] port port identifier + * @return The port bits. + * + * @notapi + */ +#define pal_lld_readport(port) ((port)->in) + +/** + * @brief Reads the output latch. + * @details The purpose of this function is to read back the latched output + * value. + * + * @param[in] port port identifier + * @return The latched logical states. + * + * @notapi + */ +#define pal_lld_readlatch(port) ((port)->out) + +/** + * @brief Writes a bits mask on a I/O port. + * + * @param[in] port port identifier + * @param[in] bits bits to be written on the specified port + * + * @notapi + */ +#define pal_lld_writeport(port, bits) ((port)->out = (bits)) + + +/** + * @brief Sets a bits mask on a I/O port. + * + * @param[in] port port identifier + * @param[in] bits bits to be ORed on the specified port + * + * @notapi + */ +#define pal_lld_setport(port, bits) ((port)->out |= (bits)) + +/** + * @brief Clears a bits mask on a I/O port. + * + * @param[in] port port identifier + * @param[in] bits bits to be cleared on the specified port + * + * @notapi + */ +#define pal_lld_clearport(port, bits) ((port)->out &= ~(bits)) + +/** + * @brief Toggles a bits mask on a I/O port. + * + * @param[in] port port identifier + * @param[in] bits bits to be XORed on the specified port + * + * @notapi + */ +#define pal_lld_toggleport(port, bits) ((port)->out ^= (bits)) + +/** + * @brief Pads group mode setup. + * @details This function programs a pads group belonging to the same port + * with the specified mode. + * @note Programming an unknown or unsupported mode is silently ignored. + * + * @param[in] port port identifier + * @param[in] mask group mask + * @param[in] offset group bit offset within the port + * @param[in] mode group mode + * + * @notapi + */ +#define pal_lld_setgroupmode(port, mask, offset, mode) \ + _pal_lld_setgroupmode(port, mask << offset, mode) + +/** + * @brief Clears a pad logical state to @p PAL_LOW. + * @details This function is implemented in a way which should + * produce a BIC instruction rather than an AND + * + * @param[in] port port identifier + * @param[in] pad pad number within the port + * + * @notapi + */ +#define pal_lld_clearpad(port, pad) ((port)->out &= ~(BIT ## pad)) + +#if !defined(__DOXYGEN__) +extern const PALConfig pal_default_config; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void _pal_lld_init(const PALConfig *config); + void _pal_lld_setgroupmode(ioportid_t port, + ioportmask_t mask, + iomode_t mode); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_PAL == TRUE */ + +#endif /* _PAL_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/MSP430X/hal_serial_lld.c b/os/hal/ports/MSP430X/hal_serial_lld.c new file mode 100644 index 0000000..277f6d2 --- /dev/null +++ b/os/hal/ports/MSP430X/hal_serial_lld.c @@ -0,0 +1,657 @@ +/* + ChibiOS - Copyright (C) 2016 Andrew Wygle aka awygle + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file MSP430X/hal_serial_lld.c + * @brief MSP430X serial subsystem low level driver source. + * + * @addtogroup SERIAL + * @{ + */ + +#include "hal.h" + +#if (HAL_USE_SERIAL == TRUE) || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** @brief USART0 serial driver identifier.*/ +#if (MSP430X_SERIAL_USE_USART0 == TRUE) || defined(__DOXYGEN__) +#ifndef __MSP430_HAS_EUSCI_A0__ + #error "Cannot find USCI module to use for SD0" +#endif +SerialDriver SD0; +#endif + +/** @brief USART1 serial driver identifier.*/ +#if (MSP430X_SERIAL_USE_USART1 == TRUE) || defined(__DOXYGEN__) +#ifndef __MSP430_HAS_EUSCI_A1__ + #error "Cannot find USCI module to use for SD1" +#endif +SerialDriver SD1; +#endif + +/** @brief USART2 serial driver identifier.*/ +#if (MSP430X_SERIAL_USE_USART2 == TRUE) || defined(__DOXYGEN__) +#ifndef __MSP430_HAS_EUSCI_A2__ + #error "Cannot find USCI module to use for SD2" +#endif +SerialDriver SD2; +#endif + +/** @brief USART3 serial driver identifier.*/ +#if (MSP430X_SERIAL_USE_USART3 == TRUE) || defined(__DOXYGEN__) +#ifndef __MSP430_HAS_EUSCI_A3__ + #error "Cannot find USCI module to use for SD3" +#endif +SerialDriver SD3; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/** + * @brief Driver default configuration. + */ +static const SerialConfig default_config = { + SERIAL_DEFAULT_BITRATE +}; + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/** + * @brief UCBRS calculation. + * @details This function calculates the UCBRS value for oversampled baud + * rates. + * + * @param[in] frac Fractional part of baud rate division, times 10000. + */ +static uint8_t UCBRS(uint16_t frac) { + /* TODO there must be a better way */ + if (frac < 529) + return 0x00; + else if (frac < 715) + return 0x01; + else if (frac < 835) + return 0x02; + else if (frac < 1001) + return 0x04; + else if (frac < 1252) + return 0x08; + else if (frac < 1430) + return 0x10; + else if (frac < 1670) + return 0x20; + else if (frac < 2147) + return 0x11; + else if (frac < 2224) + return 0x21; + else if (frac < 2503) + return 0x22; + else if (frac < 3000) + return 0x44; + else if (frac < 3335) + return 0x25; + else if (frac < 3575) + return 0x49; + else if (frac < 3753) + return 0x4A; + else if (frac < 4003) + return 0x52; + else if (frac < 4286) + return 0x92; + else if (frac < 4378) + return 0x53; + else if (frac < 5002) + return 0x55; + else if (frac < 5715) + return 0xAA; + else if (frac < 6003) + return 0x6B; + else if (frac < 6254) + return 0xAD; + else if (frac < 6432) + return 0xB5; + else if (frac < 6667) + return 0xB6; + else if (frac < 7001) + return 0xD6; + else if (frac < 7147) + return 0xB7; + else if (frac < 7503) + return 0xBB; + else if (frac < 7861) + return 0xDD; + else if (frac < 8004) + return 0xED; + else if (frac < 8333) + return 0xEE; + else if (frac < 8464) + return 0xBF; + else if (frac < 8572) + return 0xDF; + else if (frac < 8751) + return 0xEF; + else if (frac < 9004) + return 0xF7; + else if (frac < 9170) + return 0xFB; + else if (frac < 9288) + return 0xFD; + else + return 0xFE; +} + +/** + * @brief Modulation control word calculator. + * @details This function calculates the modulation control word from the + * input clock frequency and the requested baud rate. + * + * @param[in] baud Requested baud rate + * @param[in] freq Frequency of the clock driving the USCI module + */ +static uint16_t UCAxMCTLW(uint32_t baud, uint32_t freq) { + + uint16_t n = freq/baud; + /*uint16_t frac = (freq * 10000 / baud) - ((freq / baud) * 10000);*/ + uint16_t frac = (freq - (n * baud)) * 10000 / baud; + if (n > 16) { + while (n > 16) { + n -= 16; + } + return (UCBRS(frac) << 8) | (n << 4) | UCOS16; + } + return UCBRS(frac) << 8; +} + +/** + * @brief UCBRW calculation. + * @details This function calculates the UCBRW value for all baud + * rates. + * + * @param[in] baud Requested baud rate + * @param[in] freq Frequency of the clock driving the USCI module + */ +static uint16_t UCAxBRW(uint32_t baud, uint32_t freq) { + uint16_t n = freq/baud; + if (n > 16) { + return n >> 4; + } + return n; +} + +#if (MSP430X_SERIAL_USE_USART0 == TRUE) || defined(__DOXYGEN__) +static void usart0_init(const SerialConfig *config) { + UCA0BRW = UCAxBRW(config->sc_bitrate, MSP430X_USART0_CLK_FREQ); + UCA0MCTLW = UCAxMCTLW(config->sc_bitrate, MSP430X_USART0_CLK_FREQ); + UCA0STATW = 0; + UCA0ABCTL = 0; + UCA0IRCTL = 0; + UCA0CTLW0 = (MSP430X_USART0_PARITY << 14) | (MSP430X_USART0_ORDER << 13) | \ + (MSP430X_USART0_SIZE << 12) | (MSP430X_USART0_STOP << 11) | \ + (MSP430X_USART0_UCSSEL); + UCA0IE = UCRXIE; +} +#endif + +#if (MSP430X_SERIAL_USE_USART1 == TRUE) || defined(__DOXYGEN__) +static void usart1_init(const SerialConfig *config) { + UCA1BRW = UCAxBRW(config->sc_bitrate, MSP430X_USART1_CLK_FREQ); + UCA1MCTLW = UCAxMCTLW(config->sc_bitrate, MSP430X_USART1_CLK_FREQ); + UCA1STATW = 0; + UCA1ABCTL = 0; + UCA1IRCTL = 0; + UCA1IE = UCRXIE; + UCA1CTLW0 = (MSP430X_USART1_PARITY << 14) | (MSP430X_USART1_ORDER << 13) | \ + (MSP430X_USART1_SIZE << 12) | (MSP430X_USART1_STOP << 11) | \ + (MSP430X_USART1_UCSSEL); +} +#endif + +#if (MSP430X_SERIAL_USE_USART2 == TRUE) || defined(__DOXYGEN__) +static void usart2_init(const SerialConfig *config) { + UCA2BRW = UCAxBRW(config->sc_bitrate, MSP430X_USART2_CLK_FREQ); + UCA2MCTLW = UCAxMCTLW(config->sc_bitrate); + UCA2STATW = 0; + UCA2ABCTL = 0; + UCA2IRCTL = 0; + UCA2IE = UCRXIE; + UCA2CTLW0 = (MSP430X_USART2_PARITY << 14) | (MSP430X_USART2_ORDER << 13) | \ + (MSP430X_USART2_SIZE << 12) | (MSP430X_USART2_STOP << 11) | \ + (MSP430X_USART2_UCSSEL); +} +#endif + +#if (MSP430X_SERIAL_USE_USART3 == TRUE) || defined(__DOXYGEN__) +static void usart3_init(const SerialConfig *config) { + UCA3BRW = UCAxBRW(config->sc_bitrate, MSP430X_USART3_CLK_FREQ); + UCA3MCTLW = UCAxMCTLW(config->sc_bitrate, MSP430X_USART3_CLK_FREQ); + UCA3STATW = 0; + UCA3ABCTL = 0; + UCA3IRCTL = 0; + UCA3IE = UCRXIE; + UCA3CTLW0 = (MSP430X_USART3_PARITY << 14) | (MSP430X_USART3_ORDER << 13) | \ + (MSP430X_USART3_SIZE << 12) | (MSP430X_USART3_STOP << 11) | \ + (MSP430X_USART3_UCSSEL); +} +#endif + +#if (MSP430X_SERIAL_USE_USART0 == TRUE) || defined(__DOXYGEN__) +static void notify0(io_queue_t *qp) { + + (void)qp; + UCA0IE |= UCTXIE; +} +#endif + +#if (MSP430X_SERIAL_USE_USART1 == TRUE) || defined(__DOXYGEN__) +static void notify1(io_queue_t *qp) { + + (void)qp; + UCA1IE |= UCTXIE; +} +#endif + +#if (MSP430X_SERIAL_USE_USART2 == TRUE) || defined(__DOXYGEN__) +static void notify2(io_queue_t *qp) { + + (void)qp; + UCA2IE |= UCTXIE; +} +#endif + +#if (MSP430X_SERIAL_USE_USART3 == TRUE) || defined(__DOXYGEN__) +static void notify3(io_queue_t *qp) { + + (void)qp; + UCA3IE |= UCTXIE; +} +#endif + +/** + * @brief Error handling routine. + * + * @param[in] sra USCI status register containing errors + * @param[in] sdp pointer to a @p SerialDriver object + */ +static void set_error(uint16_t sra, SerialDriver *sdp) { + eventflags_t sts = 0; + + if (sra & UCOE) + sts |= SD_OVERRUN_ERROR; + if (sra & UCPE) + sts |= SD_PARITY_ERROR; + if (sra & UCFE) + sts |= SD_FRAMING_ERROR; + osalSysLockFromISR(); + chnAddFlagsI(sdp, sts); + osalSysUnlockFromISR(); +} + + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if MSP430X_SERIAL_USE_USART0 || defined(__DOXYGEN__) +/** + * @brief USART0 interrupt handler. + * + * @isr + */ +PORT_IRQ_HANDLER(USCI_A0_VECTOR) { + msg_t b; + + OSAL_IRQ_PROLOGUE(); + + switch (__even_in_range(UCA0IV,USCI_UART_UCTXCPTIFG)) { + case USCI_UART_UCRXIFG: /* RX interrupt */ + + /* Detect errors */ + if (UCA0STATW & UCRXERR) + set_error(UCA0STATW, &SD0); + + /* Data available */ + osalSysLockFromISR(); + sdIncomingDataI(&SD0, UCA0RXBUF); + osalSysUnlockFromISR(); + break; + + case USCI_UART_UCTXIFG: /* TX interrupt */ + + /* Transmission buffer empty */ + osalSysLockFromISR(); + b = sdRequestDataI(&SD0); + if (b < Q_OK) { + chnAddFlagsI(&SD0, CHN_OUTPUT_EMPTY); + UCA0IE = (UCA0IE & ~UCTXIE) | UCTXCPTIE; + UCA0IFG |= UCTXIFG; /* If we don't write to TXBUF, IFG won't get set */ + } + else + UCA0TXBUF = b; + osalSysUnlockFromISR(); + break; + + case USCI_UART_UCTXCPTIFG: /* TX complete interrupt */ + + /* Physical transmission end */ + osalSysLockFromISR(); + if (oqIsEmptyI(&SD0.oqueue)) + chnAddFlagsI(&SD0, CHN_TRANSMISSION_END); + UCA0IE &= ~UCTXCPTIE; + break; + + default: /* other interrupts */ + while (1); + break; + } + + OSAL_IRQ_EPILOGUE(); + +} +#endif + +#if MSP430X_SERIAL_USE_USART1 || defined(__DOXYGEN__) +/** + * @brief USART1 interrupt handler. + * + * @isr + */ +PORT_IRQ_HANDLER(USCI_A1_VECTOR) { + msg_t b; + + OSAL_IRQ_PROLOGUE(); + + switch (__even_in_range(UCA1IV,USCI_UART_UCTXCPTIFG)) { + case USCI_UART_UCRXIFG: /* RX interrupt */ + + /* Detect errors */ + if (UCA1STATW & UCRXERR) + set_error(UCA1STATW, &SD1); + + /* Data available */ + osalSysLockFromISR(); + sdIncomingDataI(&SD1, UCA1RXBUF); + osalSysUnlockFromISR(); + break; + + case USCI_UART_UCTXIFG: /* TX interrupt */ + + /* Transmission buffer empty */ + osalSysLockFromISR(); + b = sdRequestDataI(&SD1); + if (b < Q_OK) { + chnAddFlagsI(&SD1, CHN_OUTPUT_EMPTY); + UCA1IE = (UCA1IE & ~UCTXIE) | UCTXCPTIE; + UCA1IFG |= UCTXIFG; /* If we don't write to TXBUF, IFG won't get set */ + } + else + UCA1TXBUF = b; + osalSysUnlockFromISR(); + break; + + case USCI_UART_UCTXCPTIFG: /* TX complete interrupt */ + + /* Physical transmission end */ + osalSysLockFromISR(); + if (oqIsEmptyI(&SD1.oqueue)) + chnAddFlagsI(&SD1, CHN_TRANSMISSION_END); + UCA1IE &= ~UCTXCPTIE; + break; + + default: /* other interrupts */ + while (1); + break; + } + + OSAL_IRQ_EPILOGUE(); + +} +#endif + +#if MSP430X_SERIAL_USE_USART2 || defined(__DOXYGEN__) +/** + * @brief USART2 interrupt handler. + * + * @isr + */ +PORT_IRQ_HANDLER(USCI_A2_VECTOR) { + msg_t b; + + OSAL_IRQ_PROLOGUE(); + + switch (__even_in_range(UCA2IV,USCI_UART_UCTXCPTIFG)) { + case USCI_UART_UCRXIFG: /* RX interrupt */ + + /* Detect errors */ + if (UCA2STATW & UCRXERR) + set_error(UCA2STATW, &SD2); + + /* Data available */ + osalSysLockFromISR(); + sdIncomingDataI(&SD2, UCA2RXBUF); + osalSysUnlockFromISR(); + break; + + case USCI_UART_UCTXIFG: /* TX interrupt */ + + /* Transmission buffer empty */ + osalSysLockFromISR(); + b = sdRequestDataI(&SD2); + if (b < Q_OK) { + chnAddFlagsI(&SD2, CHN_OUTPUT_EMPTY); + UCA2IE = (UCA2IE & ~UCTXIE) | UCTXCPTIE; + UCA2IFG |= UCTXIFG; /* If we don't write to TXBUF, IFG won't get set */ + } + else + UCA2TXBUF = b; + osalSysUnlockFromISR(); + break; + + case USCI_UART_UCTXCPTIFG: /* TX complete interrupt */ + + /* Physical transmission end */ + osalSysLockFromISR(); + if (oqIsEmptyI(&SD2.oqueue)) + chnAddFlagsI(&SD2, CHN_TRANSMISSION_END); + UCA2IE &= ~UCTXCPTIE; + break; + + default: /* other interrupts */ + while (1); + break; + } + + OSAL_IRQ_EPILOGUE(); + +} +#endif + +#if MSP430X_SERIAL_USE_USART3 || defined(__DOXYGEN__) +/** + * @brief USART3 interrupt handler. + * + * @isr + */ +PORT_IRQ_HANDLER(USCI_A3_VECTOR) { + msg_t b; + + OSAL_IRQ_PROLOGUE(); + + switch (__even_in_range(UCA3IV,USCI_UART_UCTXCPTIFG)) { + case USCI_UART_UCRXIFG: /* RX interrupt */ + + /* Detect errors */ + if (UCA3STATW & UCRXERR) + set_error(UCA3STATW, &SD3); + + /* Data available */ + osalSysLockFromISR(); + sdIncomingDataI(&SD3, UCA3RXBUF); + osalSysUnlockFromISR(); + break; + + case USCI_UART_UCTXIFG: /* TX interrupt */ + + /* Transmission buffer empty */ + osalSysLockFromISR(); + b = sdRequestDataI(&SD3); + if (b < Q_OK) { + chnAddFlagsI(&SD3, CHN_OUTPUT_EMPTY); + UCA3IE = (UCA3IE & ~UCTXIE) | UCTXCPTIE; + UCA3IFG |= UCTXIFG; /* If we don't write to TXBUF, IFG won't get set */ + } + else + UCA3TXBUF = b; + osalSysUnlockFromISR(); + break; + + case USCI_UART_UCTXCPTIFG: /* TX complete interrupt */ + + /* Physical transmission end */ + osalSysLockFromISR(); + if (oqIsEmptyI(&SD3.oqueue)) + chnAddFlagsI(&SD3, CHN_TRANSMISSION_END); + UCA3IE &= ~UCTXCPTIE; + break; + + default: /* other interrupts */ + while (1); + break; + } + + OSAL_IRQ_EPILOGUE(); + +} +#endif + + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level serial driver initialization. + * + * @notapi + */ +void sd_lld_init(void) { + +#if MSP430X_SERIAL_USE_USART0 == TRUE + sdObjectInit(&SD0, NULL, notify0); +#endif + +#if MSP430X_SERIAL_USE_USART1 == TRUE + sdObjectInit(&SD1, NULL, notify1); +#endif + +#if MSP430X_SERIAL_USE_USART2 == TRUE + sdObjectInit(&SD2, NULL, notify2); +#endif + +#if MSP430X_SERIAL_USE_USART3 == TRUE + sdObjectInit(&SD3, NULL, notify3); +#endif +} + +/** + * @brief Low level serial driver configuration and (re)start. + * + * @param[in] sdp pointer to a @p SerialDriver object + * @param[in] config the architecture-dependent serial driver configuration. + * If this parameter is set to @p NULL then a default + * configuration is used. + * + * @notapi + */ +void sd_lld_start(SerialDriver *sdp, const SerialConfig *config) { + + if (config == NULL) { + config = &default_config; + } + + + if (sdp->state == SD_STOP) { +#if MSP430X_SERIAL_USE_USART0 == TRUE + if (&SD0 == sdp) { + usart0_init(config); + } +#endif +#if MSP430X_SERIAL_USE_USART1 == TRUE + if (&SD1 == sdp) { + usart1_init(config); + } +#endif +#if MSP430X_SERIAL_USE_USART2 == TRUE + if (&SD2 == sdp) { + usart2_init(config); + } +#endif +#if MSP430X_SERIAL_USE_USART3 == TRUE + if (&SD3 == sdp) { + usart3_init(config); + } +#endif + } +} + +/** + * @brief Low level serial driver stop. + * @details De-initializes the USART, stops the associated clock, resets the + * interrupt vector. + * + * @param[in] sdp pointer to a @p SerialDriver object + * + * @notapi + */ +void sd_lld_stop(SerialDriver *sdp) { + + if (sdp->state == SD_READY) { +#if MSP430X_SERIAL_USE_USART0 == TRUE + if (&SD0 == sdp) { + UCA0CTLW0 = UCSWRST; + } +#endif +#if MSP430X_SERIAL_USE_USART1 == TRUE + if (&SD1 == sdp) { + UCA1CTLW0 = UCSWRST; + } +#endif +#if MSP430X_SERIAL_USE_USART2 == TRUE + if (&SD2 == sdp) { + UCA2CTLW0 = UCSWRST; + } +#endif +#if MSP430X_SERIAL_USE_USART3 == TRUE + if (&SD3 == sdp) { + UCA3CTLW0 = UCSWRST; + } +#endif + } +} + +#endif /* HAL_USE_SERIAL == TRUE */ + +/** @} */ diff --git a/os/hal/ports/MSP430X/hal_serial_lld.h b/os/hal/ports/MSP430X/hal_serial_lld.h new file mode 100644 index 0000000..389e5c8 --- /dev/null +++ b/os/hal/ports/MSP430X/hal_serial_lld.h @@ -0,0 +1,320 @@ +/* + ChibiOS - Copyright (C) 2016 Andrew Wygle aka awygle + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file MSP430X/hal_serial_lld.h + * @brief MSP430X serial subsystem low level driver header. + * + * @addtogroup SERIAL + * @{ + */ + +#ifndef _SERIAL_LLD_H_ +#define _SERIAL_LLD_H_ + +#if (HAL_USE_SERIAL == TRUE) || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +#define NONE 0 +#define ODD 2 +#define EVEN 3 + +#define MSB 1 +#define LSB 0 + +#define SEVEN 1 +#define EIGHT 0 + +#define ONE 0 +#define TWO 1 + +#define MSP430X_SERIAL_SMCLK UCSSEL__SMCLK +#define MSP430X_SERIAL_UCLK UCSSEL__UCLK +#define MSP430X_SERIAL_ACLK UCSSEL__ACLK + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name USART0 configuration options + * @{ + */ +/** + * @brief USART0 driver enable switch. + * @details If set to @p TRUE the support for USART1 is included. + * @note The default is @p FALSE. + */ +#if !defined(MSP430X_SERIAL_USE_USART0) || defined(__DOXYGEN__) +#define MSP430X_SERIAL_USE_USART0 FALSE +#endif +/** @} */ + +/** + * @name USART1 configuration options + * @{ + */ +/** + * @brief USART1 driver enable switch. + * @details If set to @p TRUE the support for USART1 is included. + * @note The default is @p FALSE. + */ +#if !defined(MSP430X_SERIAL_USE_USART1) || defined(__DOXYGEN__) +#define MSP430X_SERIAL_USE_USART1 FALSE +#endif +/** @} */ + +/** + * @name USART2 configuration options + * @{ + */ +/** + * @brief USART2 driver enable switch. + * @details If set to @p TRUE the support for USART1 is included. + * @note The default is @p FALSE. + */ +#if !defined(MSP430X_SERIAL_USE_USART2) || defined(__DOXYGEN__) +#define MSP430X_SERIAL_USE_USART2 FALSE +#endif +/** @} */ + +/** + * @name USART3 configuration options + * @{ + */ +/** + * @brief USART3 driver enable switch. + * @details If set to @p TRUE the support for USART1 is included. + * @note The default is @p FALSE. + */ +#if !defined(MSP430X_SERIAL_USE_USART3) || defined(__DOXYGEN__) +#define MSP430X_SERIAL_USE_USART3 FALSE +#endif + +#if MSP430X_SERIAL_USE_USART0 + #if !defined(MSP430X_USART0_PARITY) + #define MSP430X_USART0_PARITY NONE + #endif + #if !defined(MSP430X_USART0_ORDER) + #define MSP430X_USART0_ORDER LSB + #endif + #if !defined(MSP430X_USART0_SIZE) + #define MSP430X_USART0_SIZE EIGHT + #endif + #if !defined(MSP430X_USART0_STOP) + #define MSP430X_USART0_STOP ONE + #endif + #if !defined(MSP430X_USART0_CLK_SRC) + #define MSP430X_USART0_CLK_SRC MSP430X_UCLK_SRC + #ifndef MSP430X_USART0_CLK_FREQ + #error "Requested external UART0 clock but no frequency given" + #endif + #define MSP430X_USART0_UCSSEL UCSSEL__UCLK + #elif MSP430X_USART0_CLK_SRC == MSP430X_ACLK_SRC + #define MSP430X_USART0_CLK_SRC MSP430X_ACLK_SRC + #define MSP430X_USART0_CLK_FREQ MSP430X_ACLK_FREQ + #define MSP430X_USART0_UCSSEL UCSSEL__ACLK + #elif MSP430X_USART0_CLK_SRC == MSP430X_SMCLK_SRC + #define MSP430X_USART0_CLK_SRC MSP430X_SMCLK_SRC + #define MSP430X_USART0_CLK_FREQ MSP430X_SMCLK_FREQ + #define MSP430X_USART0_UCSSEL UCSSEL__SMCLK + #else + #error "MSP430X_USART0_CLK_SRC invalid" + #endif +#endif + +#if MSP430X_SERIAL_USE_USART1 + #if !defined(MSP430X_USART1_PARITY) + #define MSP430X_USART1_PARITY NONE + #endif + #if !defined(MSP430X_USART1_ORDER) + #define MSP430X_USART1_ORDER LSB + #endif + #if !defined(MSP430X_USART1_SIZE) + #define MSP430X_USART1_SIZE EIGHT + #endif + #if !defined(MSP430X_USART1_STOP) + #define MSP430X_USART1_STOP ONE + #endif + #if !defined(MSP430X_USART1_CLK_SRC) + #define MSP430X_USART1_CLK_SRC MSP430X_UCLK_SRC + #ifndef MSP430X_USART1_CLK_FREQ + #error "Requested external UART0 clock but no frequency given" + #endif + #define MSP430X_USART1_UCSSEL UCSSEL__UCLK + #elif MSP430X_USART1_CLK_SRC == MSP430X_ACLK_SRC + #define MSP430X_USART1_CLK_SRC MSP430X_ACLK_SRC + #define MSP430X_USART1_CLK_FREQ MSP430X_ACLK_FREQ + #define MSP430X_USART1_UCSSEL UCSSEL__ACLK + #elif MSP430X_USART1_CLK_SRC == MSP430X_SMCLK_SRC + #define MSP430X_USART1_CLK_SRC MSP430X_SMCLK_SRC + #define MSP430X_USART1_CLK_FREQ MSP430X_SMCLK_FREQ + #define MSP430X_USART1_UCSSEL UCSSEL__SMCLK + #else + #error "MSP430X_USART1_CLK_SRC invalid" + #endif +#endif + +#if MSP430X_SERIAL_USE_USART2 + #if !defined(MSP430X_USART2_PARITY) + #define MSP430X_USART2_PARITY NONE + #endif + #if !defined(MSP430X_USART2_ORDER) + #define MSP430X_USART2_ORDER LSB + #endif + #if !defined(MSP430X_USART2_SIZE) + #define MSP430X_USART2_SIZE EIGHT + #endif + #if !defined(MSP430X_USART2_STOP) + #define MSP430X_USART2_STOP ONE + #endif + #if !defined(MSP430X_USART2_CLK_SRC) + #define MSP430X_USART2_CLK_SRC MSP430X_UCLK_SRC + #ifndef MSP430X_USART2_CLK_FREQ + #error "Requested external UART0 clock but no frequency given" + #endif + #define MSP430X_USART2_UCSSEL UCSSEL__UCLK + #elif MSP430X_USART2_CLK_SRC == MSP430X_ACLK_SRC + #define MSP430X_USART2_CLK_SRC MSP430X_ACLK_SRC + #define MSP430X_USART2_CLK_FREQ MSP430X_ACLK_FREQ + #define MSP430X_USART2_UCSSEL UCSSEL__ACLK + #elif MSP430X_USART2_CLK_SRC == MSP430X_SMCLK_SRC + #define MSP430X_USART2_CLK_SRC MSP430X_SMCLK_SRC + #define MSP430X_USART2_CLK_FREQ MSP430X_SMCLK_FREQ + #define MSP430X_USART2_UCSSEL UCSSEL__SMCLK + #else + #error "MSP430X_USART2_CLK_SRC invalid" + #endif +#endif + +#if MSP430X_SERIAL_USE_USART3 + #if !defined(MSP430X_USART3_PARITY) + #define MSP430X_USART3_PARITY NONE + #endif + #if !defined(MSP430X_USART3_ORDER) + #define MSP430X_USART3_ORDER LSB + #endif + #if !defined(MSP430X_USART3_SIZE) + #define MSP430X_USART3_SIZE EIGHT + #endif + #if !defined(MSP430X_USART3_STOP) + #define MSP430X_USART3_STOP ONE + #endif + #if !defined(MSP430X_USART3_CLK_SRC) + #define MSP430X_USART3_CLK_SRC MSP430X_UCLK_SRC + #ifndef MSP430X_USART3_CLK_FREQ + #error "Requested external UART0 clock but no frequency given" + #endif + #define MSP430X_USART3_UCSSEL UCSSEL__UCLK + #elif MSP430X_USART3_CLK_SRC == MSP430X_ACLK_SRC + #define MSP430X_USART3_CLK_SRC MSP430X_ACLK_SRC + #define MSP430X_USART3_CLK_FREQ MSP430X_ACLK_FREQ + #define MSP430X_USART3_UCSSEL UCSSEL__ACLK + #elif MSP430X_USART3_CLK_SRC == MSP430X_SMCLK_SRC + #define MSP430X_USART3_CLK_SRC MSP430X_SMCLK_SRC + #define MSP430X_USART3_CLK_FREQ MSP430X_SMCLK_FREQ + #define MSP430X_USART3_UCSSEL UCSSEL__SMCLK + #else + #error "MSP430X_USART3_CLK_SRC invalid" + #endif +#endif + +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief MSP430X Serial Driver configuration structure. + * @details An insance of this structure must be passed to @p sdStart() + * in order to configure and start a serial driver operations. + * @note This structure content is architecture dependent, each driver + * implementation defines its own version and the custom static + * initializers. + */ +typedef struct { + /** + * @brief Bit rate. + */ + uint32_t sc_bitrate; + + /* End of the mandatory fields.*/ +} SerialConfig; + +/** + * @brief @p SerialDriver specific data. + */ +#define _serial_driver_data \ + _base_asynchronous_channel_data \ + /* Driver state.*/ \ + sdstate_t state; \ + /* Input queue.*/ \ + input_queue_t iqueue; \ + /* Output queue.*/ \ + output_queue_t oqueue; \ + /* Input circular buffer.*/ \ + uint8_t ib[SERIAL_BUFFERS_SIZE]; \ + /* Output circular buffer.*/ \ + uint8_t ob[SERIAL_BUFFERS_SIZE]; \ + /* End of the mandatory fields.*/ + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if (MSP430X_SERIAL_USE_USART0 == TRUE) && !defined(__DOXYGEN__) +extern SerialDriver SD0; +#endif + +#if (MSP430X_SERIAL_USE_USART1 == TRUE) && !defined(__DOXYGEN__) +extern SerialDriver SD1; +#endif + +#if (MSP430X_SERIAL_USE_USART2 == TRUE) && !defined(__DOXYGEN__) +extern SerialDriver SD2; +#endif + +#if (MSP430X_SERIAL_USE_USART3 == TRUE) && !defined(__DOXYGEN__) +extern SerialDriver SD3; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void sd_lld_init(void); + void sd_lld_start(SerialDriver *sdp, const SerialConfig *config); + void sd_lld_stop(SerialDriver *sdp); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_SERIAL == TRUE */ + +#endif /* _SERIAL_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/MSP430X/hal_st_lld.c b/os/hal/ports/MSP430X/hal_st_lld.c new file mode 100644 index 0000000..8ea1b9d --- /dev/null +++ b/os/hal/ports/MSP430X/hal_st_lld.c @@ -0,0 +1,206 @@ +/* + ChibiOS - Copyright (C) 2016 Andrew Wygle aka awygle + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file MSP430X/hal_st_lld.c + * @brief MSP430X ST subsystem low level driver source. + * + * @addtogroup ST + * @{ + */ + +#include "hal.h" +#include + +#if (OSAL_ST_MODE != OSAL_ST_MODE_NONE) || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#if (OSAL_ST_MODE == OSAL_ST_MODE_FREERUNNING) || defined(__DOXYGEN__) + #define MSP430X_ST_DIV_CALC(x) ((MSP430X_ST_CLK_FREQ / OSAL_ST_FREQUENCY) == x) +#endif + + +#if (OSAL_ST_MODE == OSAL_ST_MODE_PERIODIC) || defined(__DOXYGEN__) + #if ((MSP430X_ST_CLK_FREQ / OSAL_ST_FREQUENCY / 64) > MSP_TIMER_COUNTER_MAX) + #error "Frequency too low for timer - please set OSAL_ST_FREQUENCY to a higher value" + #endif + + #define MSP430X_ST_DIV_CALC(x) ((MSP430X_ST_CLK_FREQ / OSAL_ST_FREQUENCY / x) <= MSP_TIMER_COUNTER_MAX) +#endif + +/* Find suitable prescaler setting */ +#if MSP430X_ST_DIV_CALC(1) + #define MSP430X_ST_DIV 1 + #define MSP430X_ST_DIV_BITS ID__1 + #define MSP430X_ST_DIV_EX_BITS TAIDEX_0 +#elif MSP430X_ST_DIV_CALC(2) + #define MSP430X_ST_DIV 2 + #define MSP430X_ST_DIV_BITS ID__1 + #define MSP430X_ST_DIV_EX_BITS TAIDEX_1 +#elif MSP430X_ST_DIV_CALC(3) + #define MSP430X_ST_DIV 3 + #define MSP430X_ST_DIV_BITS ID__1 + #define MSP430X_ST_DIV_EX_BITS TAIDEX_2 +#elif MSP430X_ST_DIV_CALC(4) + #define MSP430X_ST_DIV 4 + #define MSP430X_ST_DIV_BITS ID__1 + #define MSP430X_ST_DIV_EX_BITS TAIDEX_3 +#elif MSP430X_ST_DIV_CALC(5) + #define MSP430X_ST_DIV 5 + #define MSP430X_ST_DIV_BITS ID__1 + #define MSP430X_ST_DIV_EX_BITS TAIDEX_4 +#elif MSP430X_ST_DIV_CALC(6) + #define MSP430X_ST_DIV 6 + #define MSP430X_ST_DIV_BITS ID__1 + #define MSP430X_ST_DIV_EX_BITS TAIDEX_5 +#elif MSP430X_ST_DIV_CALC(7) + #define MSP430X_ST_DIV 7 + #define MSP430X_ST_DIV_BITS ID__1 + #define MSP430X_ST_DIV_EX_BITS TAIDEX_6 +#elif MSP430X_ST_DIV_CALC(8) + #define MSP430X_ST_DIV 8 + #define MSP430X_ST_DIV_BITS ID__1 + #define MSP430X_ST_DIV_EX_BITS TAIDEX_7 +#elif MSP430X_ST_DIV_CALC(10) + #define MSP430X_ST_DIV 10 + #define MSP430X_ST_DIV_BITS ID__2 + #define MSP430X_ST_DIV_EX_BITS TAIDEX_4 +#elif MSP430X_ST_DIV_CALC(12) + #define MSP430X_ST_DIV 12 + #define MSP430X_ST_DIV_BITS ID__2 + #define MSP430X_ST_DIV_EX_BITS TAIDEX_5 +#elif MSP430X_ST_DIV_CALC(14) + #define MSP430X_ST_DIV 14 + #define MSP430X_ST_DIV_BITS ID__2 + #define MSP430X_ST_DIV_EX_BITS TAIDEX_6 +#elif MSP430X_ST_DIV_CALC(16) + #define MSP430X_ST_DIV 16 + #define MSP430X_ST_DIV_BITS ID__2 + #define MSP430X_ST_DIV_EX_BITS TAIDEX_7 +#elif MSP430X_ST_DIV_CALC(20) + #define MSP430X_ST_DIV 20 + #define MSP430X_ST_DIV_BITS ID__4 + #define MSP430X_ST_DIV_EX_BITS TAIDEX_4 +#elif MSP430X_ST_DIV_CALC(24) + #define MSP430X_ST_DIV 24 + #define MSP430X_ST_DIV_BITS ID__4 + #define MSP430X_ST_DIV_EX_BITS TAIDEX_5 +#elif MSP430X_ST_DIV_CALC(28) + #define MSP430X_ST_DIV 28 + #define MSP430X_ST_DIV_BITS ID__4 + #define MSP430X_ST_DIV_EX_BITS TAIDEX_6 +#elif MSP430X_ST_DIV_CALC(32) + #define MSP430X_ST_DIV 32 + #define MSP430X_ST_DIV_BITS ID__4 + #define MSP430X_ST_DIV_EX_BITS TAIDEX_7 +#elif MSP430X_ST_DIV_CALC(40) + #define MSP430X_ST_DIV 40 + #define MSP430X_ST_DIV_BITS ID__8 + #define MSP430X_ST_DIV_EX_BITS TAIDEX_4 +#elif MSP430X_ST_DIV_CALC(48) + #define MSP430X_ST_DIV 48 + #define MSP430X_ST_DIV_BITS ID__8 + #define MSP430X_ST_DIV_EX_BITS TAIDEX_5 +#elif MSP430X_ST_DIV_CALC(56) + #define MSP430X_ST_DIV 56 + #define MSP430X_ST_DIV_BITS ID__8 + #define MSP430X_ST_DIV_EX_BITS TAIDEX_6 +#elif MSP430X_ST_DIV_CALC(64) + #define MSP430X_ST_DIV 64 + #define MSP430X_ST_DIV_BITS ID__8 + #define MSP430X_ST_DIV_EX_BITS TAIDEX_7 +#else + #error "Error in calculating dividers - check OSAL_ST_FREQUENCY and frequency of input clock" +#endif +/* ugh never again*/ + +#if (OSAL_ST_MODE == OSAL_ST_MODE_PERIODIC) || defined(__DOXYGEN__) + #define MSP_TIMER_COUNTER (MSP430X_ST_CLK_FREQ / OSAL_ST_FREQUENCY / MSP430X_ST_DIV) + #define MSP430X_ST_CLK_FREQ_ (MSP_TIMER_COUNTER * MSP430X_ST_DIV * OSAL_ST_FREQUENCY) + #if (MSP430X_ST_CLK_FREQ != MSP430X_ST_CLK_FREQ_) + #warning "OSAL_ST_FREQUENCY cannot be generated exactly using timer" + #endif + #undef MSP430X_ST_CLK_FREQ_ +#endif + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +/** + * @brief Timer handler for both modes + */ + +PORT_IRQ_HANDLER( MSP430X_ST_ISR ) { + + OSAL_IRQ_PROLOGUE(); + + osalSysLockFromISR(); + osalOsTimerHandlerI(); + osalSysUnlockFromISR(); + + OSAL_IRQ_EPILOGUE(); +} + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level ST driver initialization. + * + * @notapi + */ +void st_lld_init(void) { + #if (OSAL_ST_MODE == OSAL_ST_MODE_FREERUNNING) || defined (__DOXYGEN__) + /* Start disabled */ + MSP430X_ST_CCR(MSP430X_ST_TIMER) = 0; + MSP430X_ST_CCTL(MSP430X_ST_TIMER) = 0; + MSP430X_ST_EX(MSP430X_ST_TIMER) = MSP430X_ST_DIV_EX_BITS; + MSP430X_ST_CTL(MSP430X_ST_TIMER) = (TACLR | MC_2 | MSP430X_ST_DIV_BITS | MSP430X_ST_TASSEL); + #endif /* OSAL_ST_MODE == OSAL_ST_MODE_FREERUNNING */ + + #if (OSAL_ST_MODE == OSAL_ST_MODE_PERIODIC) || defined (__DOXYGEN__) + /* Start enabled */ + MSP430X_ST_CCR(MSP430X_ST_TIMER) = MSP_TIMER_COUNTER - 1; + MSP430X_ST_CCTL(MSP430X_ST_TIMER) = CCIE; + MSP430X_ST_EX(MSP430X_ST_TIMER) = MSP430X_ST_DIV_EX_BITS; + MSP430X_ST_CTL(MSP430X_ST_TIMER) = (TACLR | MC_1 | MSP430X_ST_DIV_BITS | MSP430X_ST_TASSEL); + #endif /* OSAL_ST_MODE == OSAL_ST_MODE_PERIODIC */ +} + +#endif /* OSAL_ST_MODE != OSAL_ST_MODE_NONE */ + +/** @} */ diff --git a/os/hal/ports/MSP430X/hal_st_lld.h b/os/hal/ports/MSP430X/hal_st_lld.h new file mode 100644 index 0000000..32ad970 --- /dev/null +++ b/os/hal/ports/MSP430X/hal_st_lld.h @@ -0,0 +1,216 @@ +/* + ChibiOS - Copyright (C) 2016 Andrew Wygle aka awygle + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file MSP430X/hal_st_lld.h + * @brief MSP430X ST subsystem low level driver header. + * @details This header is designed to be include-able without having to + * include other files from the HAL. + * + * @addtogroup MSP430X + * @{ + */ + +#ifndef _ST_LLD_H_ +#define _ST_LLD_H_ + +#include + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/** + * @brief Timer maximum value + */ +#define MSP_TIMER_COUNTER_MAX 65535 + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief System timer clock source. + * + * @note Legal values are undefined, MSP430X_ACLK_SRC, and + * MSP430X_SMCLK_SRC. + * @note If undefined, must define MSP430X_ST_CLK_FREQ as frequency of + * external clock and configure PAL appropriately. + */ +#if !defined (MSP430X_ST_CLK_SRC) + #ifndef MSP430X_ST_CLK_FREQ + #warning "Requested external source for ST but no frequency given" + #warning "- assuming OSAL_ST_FREQUENCY" + #define MSP430X_ST_CLK_FREQ OSAL_ST_FREQUENCY + #endif + #define MSP430X_ST_TASSEL TASSEL__TACLK +#elif MSP430X_ST_CLK_SRC == MSP430X_ACLK_SRC + #define MSP430X_ST_CLK_FREQ MSP430X_ACLK_FREQ + #define MSP430X_ST_TASSEL TASSEL__ACLK +#elif MSP430X_ST_CLK_SRC == MSP430X_SMCLK_SRC + #define MSP430X_ST_CLK_FREQ MSP430X_SMCLK_FREQ + #define MSP430X_ST_TASSEL TASSEL__SMCLK +#endif + +/* Timers */ +/** + * @brief Timer type (by letter) to be used for ST. + * @note Legal values are A and B. D support not yet implemented. + * @note Defaults to A + */ +#if !defined(MSP430X_ST_TIMER_TYPE) + #define MSP430X_ST_TIMER_TYPE A +#endif +/** + * @brief Timer instance (by number) to be used for ST. + * @note Defaults to 0 + */ +#if !defined (MSP430X_ST_TIMER_INDEX) + #define MSP430X_ST_TIMER_INDEX 0 +#endif + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#define TIMER_HELPER2(x, y) x ## y +#define TIMER_HELPER(x, y) TIMER_HELPER2(x, y) +#define MSP430X_ST_TIMER TIMER_HELPER(MSP430X_ST_TIMER_TYPE, MSP430X_ST_TIMER_INDEX) +#define CCR_HELPER(x) T ## x ## CCR0 +#define MSP430X_ST_CCR(x) CCR_HELPER(x) +#define CCTL_HELPER(x) T ## x ## CCTL0 +#define MSP430X_ST_CCTL(x) CCTL_HELPER(x) +#define EX_HELPER(x) T ## x ## EX0 +#define MSP430X_ST_EX(x) EX_HELPER(x) +#define CTL_HELPER(x) T ## x ## CTL +#define MSP430X_ST_CTL(x) CTL_HELPER(x) +#define R_HELPER(x) T ## x ## R +#define MSP430X_ST_R(x) R_HELPER(x) +#define ISR_HELPER2(x, y) TIMER ## y ## _ ## x ## 0_VECTOR +#define ISR_HELPER(x, y) ISR_HELPER2(x, y) +#define MSP430X_ST_ISR ISR_HELPER(MSP430X_ST_TIMER_TYPE, MSP430X_ST_TIMER_INDEX) + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + void st_lld_init(void); +#ifdef __cplusplus +} +#endif + +/*===========================================================================*/ +/* Driver inline functions. */ +/*===========================================================================*/ + +/** + * @brief Returns the time counter value. + * + * @return The counter value. + * + * @notapi + */ +static inline systime_t st_lld_get_counter(void) { + + return (systime_t)MSP430X_ST_R(MSP430X_ST_TIMER); +} + +/** + * @brief Starts the alarm. + * @note Makes sure that no spurious alarms are triggered after + * this call. + * + * @param[in] abstime the time to be set for the first alarm + * + * @notapi + */ +static inline void st_lld_start_alarm(systime_t abstime) { + + MSP430X_ST_CCR(MSP430X_ST_TIMER) = abstime; + + /* Reset pending interrupt */ + MSP430X_ST_CCTL(MSP430X_ST_TIMER) &= (~CCIFG); + + /* Enable interrupt */ + MSP430X_ST_CCTL(MSP430X_ST_TIMER) |= CCIE; +} + +/** + * @brief Stops the alarm interrupt. + * + * @notapi + */ +static inline void st_lld_stop_alarm(void) { + + MSP430X_ST_CCTL(MSP430X_ST_TIMER) &= (~CCIE); +} + +/** + * @brief Sets the alarm time. + * + * @param[in] abstime the time to be set for the next alarm + * + * @notapi + */ +static inline void st_lld_set_alarm(systime_t abstime) { + + MSP430X_ST_CCR(MSP430X_ST_TIMER) = abstime; +} + +/** + * @brief Returns the current alarm time. + * + * @return The currently set alarm time. + * + * @notapi + */ +static inline systime_t st_lld_get_alarm(void) { + + return MSP430X_ST_CCR(MSP430X_ST_TIMER); +} + +/** + * @brief Determines if the alarm is active. + * + * @return The alarm status. + * @retval false if the alarm is not active. + * @retval true is the alarm is active + * + * @notapi + */ +static inline bool st_lld_is_alarm_active(void) { + + return (bool)((MSP430X_ST_CCTL(MSP430X_ST_TIMER) & CCIE) != 0); +} + +#endif /* _ST_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/MSP430X/platform.mk b/os/hal/ports/MSP430X/platform.mk new file mode 100644 index 0000000..b0f256e --- /dev/null +++ b/os/hal/ports/MSP430X/platform.mk @@ -0,0 +1,8 @@ +# List of all the MSP430X platform files. +PLATFORMSRC = ${CHIBIOS_CONTRIB}/os/hal/ports/MSP430X/hal_lld.c \ + ${CHIBIOS_CONTRIB}/os/hal/ports/MSP430X/hal_st_lld.c \ + ${CHIBIOS_CONTRIB}/os/hal/ports/MSP430X/hal_serial_lld.c \ + ${CHIBIOS_CONTRIB}/os/hal/ports/MSP430X/hal_pal_lld.c + +# Required include directories +PLATFORMINC = ${CHIBIOS_CONTRIB}/os/hal/ports/MSP430X -- cgit v1.2.3