subrepo:
  subdir:   "libopencm3"
  merged:   "f5813a54"
upstream:
  origin:   "https://github.com/libopencm3/libopencm3"
  branch:   "master"
  commit:   "f5813a54"
git-subrepo:
  version:  "0.4.3"
  origin:   "???"
  commit:   "???"
This commit is contained in:
2021-09-30 16:34:10 +03:00
parent 1a441e5806
commit 244fdbc35c
1125 changed files with 185440 additions and 0 deletions

View File

@@ -0,0 +1,45 @@
##
## This file is part of the libopencm3 project.
##
## Copyright (C) 2019 Icenowy Zheng <icenowy@aosc.io>
##
## This library is free software: you can redistribute it and/or modify
## it under the terms of the GNU Lesser General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## This library 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 Lesser General Public License for more details.
##
## You should have received a copy of the GNU Lesser General Public License
## along with this library. If not, see <http://www.gnu.org/licenses/>.
##
LIBNAME = libopencm3_swm050
SRCLIBDIR ?= ..
CC = $(PREFIX)gcc
AR = $(PREFIX)ar
TGT_CFLAGS = -Os \
-Wall -Wextra -Wimplicit-function-declaration \
-Wredundant-decls -Wmissing-prototypes -Wstrict-prototypes \
-Wundef -Wshadow \
-I../../include -fno-common \
-mcpu=cortex-m0 $(FP_FLAGS) -mthumb -Wstrict-prototypes \
-ffunction-sections -fdata-sections -MD -DSWM050
TGT_CFLAGS += $(DEBUG_FLAGS)
TGT_CFLAGS += $(STANDARD_FLAGS)
ARFLAGS = rcs
OBJS += clk.o
OBJS += flash.o
OBJS += gpio.o
OBJS += pwr.o
OBJS += syscon.o
OBJS += timer.o
OBJS += wdt.o
VPATH += ../cm3
include ../Makefile.include

View File

@@ -0,0 +1,85 @@
/** @defgroup clk_file Clock peripheral API
* @ingroup peripheral_apis
* @brief SWM050 Clock API.
* LGPL License Terms @ref lgpl_license
* @author @htmlonly &copy; @endhtmlonly 2019
* Caleb Szalacinski <contact@skiboy.net>
*/
/*
* This file is part of the libopencm3 project.
*
* Copyright (C) 2019 Caleb Szalacinski <contact@skiboy.net>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
/**@{*/
#include <libopencm3/swm050/clk.h>
#include <libopencm3/swm050/sysctl.h>
/*---------------------------------------------------------------------------*/
/** @brief Setup and change the system clock multiplier and divider
Change system clock speed and wait for the clock to stabilize. The clock only
needs time to stabilize on the first invocation of this function. This should be
run at startup if you want to have a stable clock before doing anything.
@param[in] mhz Base clock speed @ref clk_speeds
The base clock speed, before the clock divider
@param[in] div Clock divider
Takes values from 0 to 1023 (in reality the possible values are the even
numbers from 2 to 1022, as well as the number 1). Anything more than the
first 10 bits is stripped off of the value. If the value is 0, it will
be treated as a 1. All odd values other than 1 are rounded down to the
closest even value, due to the fact that all odd values are treated by
the register as a 1, which would likely be unexpected. A value of 0
would also normally be treated as a 2, which would also be unexpected
behavior.
*/
void clk_speed(enum clk_speeds mhz, uint16_t div)
{
static bool first_run = true;
if (first_run) {
first_run = false;
clk_speed(CLK_18MHZ, 1);
for (uint16_t i = 0; i < 10000; ++i) {
__asm__("nop");
}
/* The speed doesn't need to be changed
a second time if the user wants 18Mhz. */
if ((mhz == CLK_18MHZ) && (div <= 1)) {
return;
}
if ((mhz == CLK_36MHZ) && (div == 2)) {
return;
}
}
if (mhz == CLK_36MHZ) {
SYSCTL_SYS_DBLF |= BIT0;
} else {
SYSCTL_SYS_DBLF &= ~BIT0;
}
if (div <= 1) {
SYSCTL_SYS_CFG_0 |= BIT0;
} else {
uint32_t masked_reg32 = SYSCTL_SYS_CFG_0 & CLK_MASK;
SYSCTL_SYS_CFG_0 = masked_reg32 | (div & ~(CLK_MASK | 0x1));
}
}
/**@}*/

View File

@@ -0,0 +1,97 @@
/** @defgroup flash_file Flash peripheral API
* @ingroup peripheral_apis
* @brief SWM050 Flash API.
* LGPL License Terms @ref lgpl_license
* @author @htmlonly &copy; @endhtmlonly 2019
* Caleb Szalacinski <contact@skiboy.net>
*/
/*
* This file is part of the libopencm3 project.
*
* Copyright (C) 2019 Caleb Szalacinski <contact@skiboy.net>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
/**@{*/
#include <libopencm3/swm050/flash.h>
/* Internal function pointers to the ROM flash API */
#define IAP_WR (void *)(0x1000AB)
#define IAP_E (void *)(0x100127)
uint32_t (*iap_write_read)(uint32_t *, uint32_t *, uint8_t, uint8_t) = IAP_WR;
uint32_t (*iap_erase)(void) = IAP_E;
/*---------------------------------------------------------------------------*/
/** @brief Write to the user flash
Writes words to the 0.5k user flash area.
Must be performed only when the system clock is 18Mhz.
@param[in] dest Destination address
The memory area to copy to.
From 0x00 - 0x1FC, as long as it is word-aligned
@param[in] src Source address
The memory area to copy from.
@param[in] cnt Number of words to write
From 1-128 as long as (dest + (cnt * 4)) < 0x200
@return 1 if successful, 0 if error
*/
uint32_t flash_write(uint32_t *dest, uint32_t *src, uint8_t cnt)
{
return iap_write_read(dest, src, cnt, 1);
}
/*---------------------------------------------------------------------------*/
/** @brief Read from the user flash
Reads words from the 0.5k user flash area.
Must be performed only when the system clock is 18Mhz.
@param[in] src Source address
The memory area to copy from.
From 0x00 - 0x1FC, as long as it is word-aligned
@param[out] dest Destination address
The memory area to copy to.
@param[in] cnt Number of words to read
From 1 - 128 as long as (src + (cnt * 4)) < 0x200
@return 1 if successful, 0 if error
*/
uint32_t flash_read(uint32_t *src, uint32_t *dest, uint8_t cnt)
{
return iap_write_read(src, dest, cnt, 0);
}
/*---------------------------------------------------------------------------*/
/** @brief Erase the user flash
Erases the entire 0.5k user flash area.
Must be performed only when the system clock is 18Mhz.
@return 1 if successful, 0 if error
*/
uint32_t flash_erase(void)
{
return iap_erase();
}
/**@}*/

View File

@@ -0,0 +1,254 @@
/** @defgroup gpio_file GPIO peripheral API
* @brief SWM050 GPIO API.
* @ingroup peripheral_apis
* LGPL License Terms @ref lgpl_license
* @author @htmlonly &copy; @endhtmlonly 2019
* Icenowy Zheng <icenowy@aosc.io>
* @author @htmlonly &copy; @endhtmlonly 2019
* Caleb Szalacinski <contact@skiboy.net>
*/
/*
* This file is part of the libopencm3 project.
*
* Copyright (C) 2019 Icenowy Zheng <icenowy@aosc.io>
* Copyright (C) 2019 Caleb Szalacinski <contact@skiboy.net>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
/**@{*/
#include <libopencm3/swm050/gpio.h>
/*---------------------------------------------------------------------------*/
/** @brief Set a Group of Pins
Set one or more pins of GPIO to 1. Please note that this chip doesn't support
atomic pin setting.
@param[in] gpios Pin identifiers @ref gpio_pin_id
If multiple pins are to be changed, use bitwise OR '|' to separate
them.
*/
void gpio_set(uint16_t gpios)
{
GPIO_ADATA |= gpios;
}
/*---------------------------------------------------------------------------*/
/** @brief Clear a Group of Pins
Set one or more pins of GPIO to 0. Please note that this chip doesn't support
atomic pin setting.
@param[in] gpios Pin identifiers @ref gpio_pin_id
If multiple pins are to be changed, use bitwise OR '|' to separate
them.
*/
void gpio_clear(uint16_t gpios)
{
GPIO_ADATA &= ~gpios;
}
/*---------------------------------------------------------------------------*/
/** @brief Read a Group of Pins.
@param[in] gpios Pin identifiers @ref gpio_pin_id
If multiple pins are to be read, use bitwise OR '|' to separate
them.
@return The pin values as a bitfield. The bit position of the pin
value returned corresponds to the pin number.
*/
uint16_t gpio_get(uint16_t gpios)
{
return GPIO_AEXT & gpios;
}
/*---------------------------------------------------------------------------*/
/** @brief Toggle a Group of Pins
Toggle one or more pins of GPIO. The non-toggled pins are not affected.
@param[in] gpios Pin identifiers @ref gpio_pin_id
If multiple pins are to be changed, use bitwise OR '|' to separate
them.
*/
void gpio_toggle(uint16_t gpios)
{
uint32_t curr_status = GPIO_ADATA & gpios;
GPIO_ADATA = (GPIO_ADATA & (~gpios)) | (~curr_status);
}
/*---------------------------------------------------------------------------*/
/** @brief Set the direction of a Group of Pins to Input
Set the direction of one or more pins of GPIO to input.
@param[in] gpios Pin identifiers @ref gpio_pin_id
If multiple pins are to be changed, use bitwise OR '|' to separate
them.
*/
void gpio_input(uint16_t gpios)
{
GPIO_ADIR &= ~gpios;
}
/*---------------------------------------------------------------------------*/
/** @brief Set the direction of a Group of Pins to Output
Set the direction of one or more pins of GPIO to output.
@param[in] gpios Pin identifiers @ref gpio_pin_id
If multiple pins are to be changed, use bitwise OR '|' to separate
them.
*/
void gpio_output(uint16_t gpios)
{
GPIO_ADIR |= gpios;
}
/*---------------------------------------------------------------------------*/
/** @brief Sets the pins as external interrupts, rather than normal GPIO
Enable interrupts on the selected pins. If you want to quickly
switch on and off interrupts, use gpio_int_mask() after calling this.
@param[in] gpios Pin identifiers @ref gpio_pin_id
If multiple pins are to be changed, use bitwise OR '|' to separate
them.
@param[in] en True to enable, false to disable.
*/
void gpio_int_enable(uint16_t gpios, bool en)
{
if (en) {
GPIO_INTEN_A |= gpios;
} else {
GPIO_INTEN_A &= ~gpios;
}
}
/*---------------------------------------------------------------------------*/
/** @brief Sets bits in the interrupt mask
When interrupts are masked, it prevents them from being received, which is a
quicker way to turn on and off GPIO interrupts (after calling gpio_int_en()).
@param[in] gpios Pin identifiers @ref gpio_pin_id
If multiple pins are to be changed, use bitwise OR '|' to separate
them.
@param[in] masked Pin mask selection @ref gpio_int_masked
Whether to mask or unmask pins.
*/
void gpio_int_mask(uint16_t gpios, enum gpio_int_masked masked)
{
if (masked) {
GPIO_INTMASK_A |= gpios;
} else {
GPIO_INTMASK_A &= ~gpios;
}
}
/*---------------------------------------------------------------------------*/
/** @brief Sets whether the pins are edge triggered or level triggered
Sets whether the pins are edge triggered or level triggered. Edge-triggered
interrupt bits must be cleared by software.
@param[in] gpios Pin identifiers @ref gpio_pin_id
If multiple pins are to be changed, use bitwise OR '|' to separate
them.
@param[in] type Trigger Type @ref gpio_trig_type
Level or edge triggered
*/
void gpio_int_type(uint16_t gpios, enum gpio_trig_type type)
{
if (type) {
GPIO_INTLEVEL_A |= gpios;
} else {
GPIO_INTLEVEL_A &= ~gpios;
}
}
/*---------------------------------------------------------------------------*/
/** @brief Sets the interrupt trigger polarity
Sets whether the interrupt is triggered by a high or low level/edge.
@param[in] gpios Pin identifiers @ref gpio_pin_id
If multiple pins are to be changed, use bitwise OR '|' to separate
them.
@param[in] pol Polarity @ref gpio_pol
High or low level/edge
*/
void gpio_int_pol(uint16_t gpios, enum gpio_pol pol)
{
if (pol) {
GPIO_INTPOLARITY_A |= gpios;
} else {
GPIO_INTPOLARITY_A &= ~gpios;
}
}
/*---------------------------------------------------------------------------*/
/** @brief Gets the masked interrupt status
Returns the pin interrupt status masked with the mask set
in @ref gpio_int_mask().
@return The masked pin interrupt status as a bitfield. The bit position of the
pin value returned corresponds to the pin number.
*/
uint16_t gpio_int_status(void)
{
return GPIO_INTSTAT_A;
}
/*---------------------------------------------------------------------------*/
/** @brief Gets the raw unmasked interrupt status
Returns the raw unmasked interrupt status.
@return The unmasked pin interrupt status as a bitfield. The bit position of the
pin value returned corresponds to the pin number.
*/
uint16_t gpio_int_raw_status(void)
{
return GPIO_RAWINTSTAT_A;
}
/*---------------------------------------------------------------------------*/
/** @brief Clear the specified pin interrupts
Clears the specified pin interrupts. Edge-triggered interrupts must be cleared
by software.
@param[in] gpios Pin identifiers @ref gpio_pin_id
If multiple pins are to be changed, use bitwise OR '|' to separate
them.
*/
void gpio_int_clear(uint16_t gpios)
{
GPIO_INTEOI_A |= gpios;
}
/**@}*/

View File

@@ -0,0 +1,42 @@
/** @defgroup pwr_file Power/Sleep API
* @brief SWM050 Power API.
* @ingroup peripheral_apis
* LGPL License Terms @ref lgpl_license
* @author @htmlonly &copy; @endhtmlonly 2019
* Caleb Szalacinski <contact@skiboy.net>
*/
/*
* This file is part of the libopencm3 project.
*
* Copyright (C) 2019 Caleb Szalacinski <contact@skiboy.net>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
/**@{*/
#include <libopencm3/swm050/sysctl.h>
#include <libopencm3/swm050/pwr.h>
/*---------------------------------------------------------------------------*/
/** @brief Go into sleep mode
Goes to sleep and wakes up on when GPIO pin 9 is pulled low. Please give
yourself enough time to connect the debugger before calling this, in order to
not get locked out of the MCU.
*/
void pwr_sleep(void)
{
SYSCTL_SYS_CFG_2 |= SYSCTL_SYS_CFG_2_SLEEP;
}
/**@}*/

View File

@@ -0,0 +1,116 @@
/** @defgroup syscon_file SYSCON peripheral API
* @ingroup peripheral_apis
* @brief SWM050 SYSCON API.
* LGPL License Terms @ref lgpl_license
* @author @htmlonly &copy; @endhtmlonly 2019
* Icenowy Zheng <icenowy@aosc.io>
* @author @htmlonly &copy; @endhtmlonly 2019
* Caleb Szalacinski <contact@skiboy.net>
*/
/*
* This file is part of the libopencm3 project.
*
* Copyright (C) 2019 Icenowy Zheng <icenowy@aosc.io>
* Copyright (C) 2019 Caleb Szalacinski <contact@skiboy.net>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
/**@{*/
#include <libopencm3/swm050/syscon.h>
#include <libopencm3/swm050/gpio.h>
/*---------------------------------------------------------------------------*/
/** @brief Select the alternative function of a Group of Pins
Select the alternative function of one or more pins of GPIO.
@param[in] gpios Pin identifiers @ref gpio_pin_id
If multiple pins are to be changed, use bitwise OR '|' to separate
them.
@param[in] af_en Whether alternative function is selected
*/
void syscon_sel_af(uint16_t gpios, bool af_en)
{
uint32_t masked_reg32;
if (gpios & GPIO0) {
masked_reg32 = SYSCON_PORTA_SEL & (~0x3);
SYSCON_PORTA_SEL = masked_reg32 | (af_en ? 0x1 : 0x0);
}
if (gpios & GPIO1) {
masked_reg32 = SYSCON_PORTA_SEL & (~0xc);
SYSCON_PORTA_SEL = masked_reg32 | (af_en ? 0x4 : 0x0);
}
if (gpios & GPIO2) {
masked_reg32 = SYSCON_PORTA_SEL & (~0x30);
SYSCON_PORTA_SEL = masked_reg32 | (af_en ? 0x10 : 0x0);
}
if (gpios & GPIO7) {
masked_reg32 = SYSCON_PORTA_SEL & (~0xc000);
SYSCON_PORTA_SEL = masked_reg32 | (af_en ? 0x4000 : 0x0);
}
}
/*---------------------------------------------------------------------------*/
/** @brief Enable the internal pull-up of a Group of Pins
Enable or disable the internal pull-up of one or more pins of GPIO.
@param[in] gpios Pin identifiers @ref gpio_pin_id
If multiple pins are to be changed, use bitwise OR '|' to separate
them.
@param[in] en True to enable pull-up, false to disable.
*/
void syscon_pullup(uint16_t gpios, bool en)
{
if (en) {
SYSCON_PORTA_PULLUP |= gpios;
} else {
SYSCON_PORTA_PULLUP &= ~gpios;
}
}
/*---------------------------------------------------------------------------*/
/** @brief Enable the input function of a Group of Pins
Enable or disable the input function of one or more pins of GPIO. Disabling
the input function of pins decreases the power usage of the MCU.
@param[in] gpios Pin identifiers @ref gpio_pin_id
If multiple pins are to be changed, use bitwise OR '|' to separate
them.
@param[in] en True to enable input function.
*/
void syscon_input_enable(uint16_t gpios, bool en)
{
if (en) {
SYSCON_PORTA_INEN &= ~gpios;
} else {
SYSCON_PORTA_INEN |= gpios;
}
}
/*---------------------------------------------------------------------------*/
/** @brief Select the SWD function of GPIO 1/2
Enable or disable the SWD debugging port at GPIO 1/2. When SWD debugging port
is enabled, GPIO and AF of the SWD pins will be both unavailable.
@param[in] en True to enable SWD.
*/
void syscon_sel_swd(bool en)
{
SYSCON_SWD_SEL = en;
}
/**@}*/

View File

@@ -0,0 +1,454 @@
/** @defgroup timer_file Timer peripheral API
* @brief SWM050 Timer API.
* @ingroup peripheral_apis
* LGPL License Terms @ref lgpl_license
* @author @htmlonly &copy; @endhtmlonly 2020
* Caleb Szalacinski <contact@skiboy.net>
*/
/*
* This file is part of the libopencm3 project.
*
* Copyright (C) 2020 Caleb Szalacinski <contact@skiboy.net>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
/**@{*/
#include <libopencm3/swm050/timer.h>
#include <libopencm3/swm050/sysctl.h>
#include <libopencm3/swm050/syscon.h>
/**
* Internal function for timer setup.
* @param timer Select timer @ref timer_select
* @param timer_int_en Passed to @ref timer_int_enable()
* @param op_mode Passed to @ref timer_operation_mode()
* @param edge_mode Passed to @ref timer_edge_mode()
* @param loop_mode Passed to @ref timer_loop_mode()
* @param clk_src Passed to @ref timer_clock_source()
* @param output_mode Passed to @ref timer_output_mode()
* @param output_level Passed to @ref timer_output_level()
*/
static void timer_setup_internal(uint32_t timer,
bool timer_int_en,
enum timer_operation_modes op_mode,
enum timer_edge_modes edge_mode,
enum timer_loop_modes loop_mode,
enum timer_clk_src clk_src,
enum timer_output_modes output_mode,
enum timer_level output_level)
{
timer_enable(timer, false);
/* Conserve power by shutting off the unneeded clock */
timer_clock_enable(timer, (clk_src == TIMER_CLK_INTERNAL));
timer_loop_mode(timer, loop_mode);
timer_output_mode(timer, output_mode);
timer_output_level(timer, output_level);
timer_clock_source(timer, clk_src);
timer_operation_mode(timer, op_mode);
timer_edge_mode(timer, edge_mode);
timer_int_enable(timer, timer_int_en);
timer_int_mask(timer, TIMER_UNMASKED);
}
/**
* Setup the timer in counter mode.
* @note Call @ref timer_enable() when you are ready to start the timer.
* @note Be sure to set the alternate functions of the timer pins
* with @ref syscon_sel_af() and disable SWD on those pins
* with @ref syscon_sel_swd() as needed.
* @note If interrupts are enabled here, the interrupt should also be enabled
* using the NVIC before enabling the timer.
* @param timer Select timer @ref timer_select
* @param timer_int_en Passed to @ref timer_int_enable()
* @param edge_mode Passed to @ref timer_edge_mode()
* @param loop_mode Passed to @ref timer_loop_mode()
* @param clk_src Passed to @ref timer_clock_source()
* @param output_mode Passed to @ref timer_output_mode()
* @param output_level Passed to @ref timer_output_level()
* @param target Passed to @ref timer_counter_target_value()
*/
void timer_counter_setup(uint32_t timer,
bool timer_int_en,
enum timer_edge_modes edge_mode,
enum timer_loop_modes loop_mode,
enum timer_clk_src clk_src,
enum timer_output_modes output_mode,
enum timer_level output_level,
uint32_t target)
{
timer_setup_internal(timer, timer_int_en, TIMER_MODE_COUNTER, edge_mode,
loop_mode, clk_src, output_mode, output_level);
timer_counter_target_value(timer, target);
}
/**
* Setup the timer in PWM mode.
* @note Call @ref timer_enable() when you are ready to start the timer.
* @note Be sure to set the alternate functions of the timer pins
* with @ref syscon_sel_af() and disable SWD on those pins
* with @ref syscon_sel_swd() as needed.
* @note If interrupts are enabled here, the interrupt should also be enabled
* using the NVIC before enabling the timer.
* @param timer Select timer @ref timer_select
* @param timer_int_en Passed to @ref timer_int_enable()
* @param edge_mode Passed to @ref timer_edge_mode()
* @param clk_src Passed to @ref timer_clock_source()
* @param output_level Passed to @ref timer_output_level()
* @param period0 Passed to @ref timer_pwm_target_value()
* @param period1 Passed to @ref timer_pwm_target_value()
*/
void timer_pwm_setup(uint32_t timer,
bool timer_int_en,
enum timer_edge_modes edge_mode,
enum timer_clk_src clk_src,
enum timer_level output_level,
uint16_t period0,
uint16_t period1)
{
timer_setup_internal(timer, timer_int_en, TIMER_MODE_PWM, edge_mode,
TIMER_LOOP_MODE, clk_src, TIMER_OUTPUT_NONE,
output_level);
timer_pwm_target_value(timer, period0, period1);
}
/**
* Setup the timer in pulse capture mode.
* @note Call @ref timer_enable() when you are ready to start the timer.
* @note Be sure to set the alternate functions of the timer pins
* with @ref syscon_sel_af() and disable SWD on those pins
* with @ref syscon_sel_swd() as needed.
* @note If interrupts are enabled here, the interrupt should also be enabled
* using the NVIC before enabling the timer.
* @param timer Select timer @ref timer_select
* @param timer_int_en Passed to @ref timer_int_enable()
* @param edge_mode Passed to @ref timer_edge_mode()
* @param loop_mode Passed to @ref timer_loop_mode()
*/
void timer_pulse_capture_setup(uint32_t timer,
bool timer_int_en,
enum timer_edge_modes edge_mode,
enum timer_loop_modes loop_mode)
{
timer_setup_internal(timer, timer_int_en, TIMER_MODE_PULSE_CAPTURE,
edge_mode, loop_mode, TIMER_CLK_INTERNAL,
TIMER_OUTPUT_NONE, TIMER_LEVEL_LOW);
}
/**
* Setup the timer in duty cycle capture mode.
* @note Call @ref timer_enable() when you are ready to start the timer.
* @note Be sure to set the alternate functions of the timer pins
* with @ref syscon_sel_af() and disable SWD on those pins
* with @ref syscon_sel_swd() as needed.
* @note If interrupts are enabled here, the interrupt should also be enabled
* using the NVIC before enabling the timer.
* @param timer Select timer @ref timer_select
* @param timer_int_en Passed to @ref timer_int_enable()
* @param edge_mode Passed to @ref timer_edge_mode()
* @param loop_mode Passed to @ref timer_loop_mode()
*/
void timer_duty_cycle_capture_setup(uint32_t timer,
bool timer_int_en,
enum timer_edge_modes edge_mode,
enum timer_loop_modes loop_mode)
{
timer_setup_internal(timer, timer_int_en, TIMER_MODE_DUTY_CYCLE_CAPTURE,
edge_mode, loop_mode, TIMER_CLK_INTERNAL,
TIMER_OUTPUT_NONE, TIMER_LEVEL_LOW);
}
/**
* Set the timer clock divider, based off of the 18MHz oscillator
* @param div Timer clock divider. Only the 6 least-significant bits are used,
* Takes values from 0 to 63 (in reality the possible values are the even
* numbers from 2 to 62, as well as the number 1). Anything after the 6
* least-significant bits are stripped off of the value. If the value is 0,
* it will be treated as a 1. All odd values other than 1 are rounded down
* to the closest even value, due to the fact that all odd values are
* treated by the register as a 1, which would likely be unexpected. A
* value of 0 would also normally be treated as a 2, which would also be
* unexpected behavior.
*/
void timer_clock_div(uint8_t div)
{
/* If the value is 0 or 1, make it odd, meaning no divide. */
/* Otherwise, drop div to the closest even value. */
div = (div <= 1) ? 1 : (div & ~0x1);
SYSCTL_SYS_CFG_0 = (~TIMER_DIV_MASK & SYSCTL_SYS_CFG_0) | (div << 16);
}
/**
* Enables or disables the timer.
* @param timer Select timer @ref timer_select
* @param en Enable or disable the timer
*/
void timer_enable(uint32_t timer, bool en)
{
if (en) {
TIMER_CTRL(timer) |= TIMER_CTRL_EN;
} else {
TIMER_CTRL(timer) &= ~TIMER_CTRL_EN;
}
}
/**
* Enables or disables the timer's internal clock.
* @param timer Select timer @ref timer_select
* @param en Enable or disable the internal clock
*/
void timer_clock_enable(uint32_t timer, bool en)
{
if (timer == TIMER_SE1) {
if (en) {
SYSCTL_SYS_CFG_1 |= SYSCTL_SYS_CFG_1_TIMERSE1;
} else {
SYSCTL_SYS_CFG_1 &= ~SYSCTL_SYS_CFG_1_TIMERSE1;
}
} else {
if (en) {
SYSCTL_SYS_CFG_1 |= SYSCTL_SYS_CFG_1_TIMERSE0;
} else {
SYSCTL_SYS_CFG_1 &= ~SYSCTL_SYS_CFG_1_TIMERSE0;
}
}
}
/**
* Selects the mode of operation.
* @param timer Select timer @ref timer_select
* @param mode The mode of operation @ref timer_operation_modes
*/
void timer_operation_mode(uint32_t timer, enum timer_operation_modes mode)
{
uint32_t reg = TIMER_CTRL(timer) & ~(TIMER_CTRL_OUTMOD_MASK << TIMER_CTRL_OUTMOD_SHIFT);
TIMER_CTRL(timer) = reg | (mode << TIMER_CTRL_OUTMOD_SHIFT);
}
/**
* Selects the output mode.
* Only used in counter mode.
* When done counting, the pin can be set to no output,
* to invert the current pin level, to set the pin high,
* or to set the pin low.
* @note Be sure to set the alternate functions of the timer pins
* with @ref syscon_sel_af() and disable SWD on those pins
* with @ref syscon_sel_swd() as needed.
* @param timer Select timer @ref timer_select
* @param mode The output mode @ref timer_output_modes
*/
void timer_output_mode(uint32_t timer, enum timer_output_modes mode)
{
uint32_t reg = TIMER_CTRL(timer) & ~(TIMER_CTRL_WMOD_MASK << TIMER_CTRL_WMOD_SHIFT);
TIMER_CTRL(timer) = reg | (mode << TIMER_CTRL_WMOD_SHIFT);
}
/**
* Selects the initial output level.
* Only used in counter and PWM modes.
* @param timer Select timer @ref timer_select
* @param level The initial output level @ref timer_level
*/
void timer_output_level(uint32_t timer, enum timer_level level)
{
TIMER_OUTPVAL(timer) = level;
}
/**
* Selects the edge mode.
* @param timer Select timer @ref timer_select
* @param mode The edge mode @ref timer_edge_modes
*/
void timer_edge_mode(uint32_t timer, enum timer_edge_modes mode)
{
if (mode) {
TIMER_CTRL(timer) |= TIMER_CTRL_TMOD;
} else {
TIMER_CTRL(timer) &= ~TIMER_CTRL_TMOD;
}
}
/**
* Selects the loop mode.
* This has no use in PWM mode.
* In loop mode with counter mode, the counter will constantly loop.
* In loop mode with the capture modes, the values will be captured
* again and again. In single mode, these operations happen only once.
* @param timer Select timer @ref timer_select
* @param mode The loop mode @ref timer_loop_modes
*/
void timer_loop_mode(uint32_t timer, enum timer_loop_modes mode)
{
if (mode) {
TIMER_CTRL(timer) |= TIMER_CTRL_LMOD;
} else {
TIMER_CTRL(timer) &= ~TIMER_CTRL_LMOD;
}
}
/**
* Selects the clock source for the timer.
* @note Be sure to set the alternate functions of the timer pins
* with @ref syscon_sel_af() and disable SWD on those pins
* with @ref syscon_sel_swd() as needed.
* @note If not using the internal clock, you can disable it
* with @ref timer_clock_enable() for power savings.
* @param timer Select timer @ref timer_select
* @param src Select the internal or external clock source @ref timer_clk_src
*/
void timer_clock_source(uint32_t timer, enum timer_clk_src src)
{
if (src) {
TIMER_CTRL(timer) |= TIMER_CTRL_OSCMOD;
} else {
TIMER_CTRL(timer) &= ~TIMER_CTRL_OSCMOD;
}
}
/**
* Sets the target values for counter mode.
* @param timer Select timer @ref timer_select
* @param target The value to count up to
*/
void timer_counter_target_value(uint32_t timer, uint32_t target)
{
TIMER_TARVAL(timer) = target;
}
/**
* Sets the target values for PWM mode.
* @param timer Select timer @ref timer_select
* @param period0 length of period 0 in clock cycles. Whether
* it is high or low is set in @ref timer_output_level()
* @param period1 length of period 1
*/
void timer_pwm_target_value(uint32_t timer, uint16_t period0, uint16_t period1)
{
timer_counter_target_value(timer, (period1 << 16) | period0);
}
/**
* Enable or disable the interrupt.
* In counter mode, when the count has been completed,
* an interrupt is generated.
* In PWM mode, on a level change, an interupt is generated.
* In either capture mode, when a capture is complete,
* an interrupt is generated.
* @note If interrupts are enabled here, the interrupt should also be enabled
* using the NVIC before enabling the timer.
* @param timer Select timer @ref timer_select
* @param en Enable or disable the interrupt
*/
void timer_int_enable(uint32_t timer, bool en)
{
if (en) {
TIMER_INTCTL(timer) |= TIMER_INTCTL_INTEN;
} else {
TIMER_INTCTL(timer) &= ~TIMER_INTCTL_INTEN;
}
}
/**
* Sets the interrupt mask.
* @param timer Select timer @ref timer_select
* @param masked Whether or not to mask the interrupt @ref timer_int_masked
*/
void timer_int_mask(uint32_t timer, enum timer_int_masked masked)
{
if (masked) {
TIMER_INTCTL(timer) &= ~TIMER_INTCTL_INTMSK;
} else {
TIMER_INTCTL(timer) |= TIMER_INTCTL_INTMSK;
}
}
/**
* Gets the current counter value, and clears the interrupt/interrupt overflow.
* If in PWM mode, this is only used for clearing the interrupt.
* @param timer Select timer @ref timer_select
* @return The current counter value
*/
uint32_t timer_get_current_value(uint32_t timer)
{
return TIMER_CURVAL(timer);
}
/**
* Gets the cycle width.
* Only used in duty cycle capture mode.
* @note See the datasheet for more concise diagrams.
* @param timer Select timer @ref timer_select
* @return The cycle width
*/
uint32_t timer_get_cycle_width(uint32_t timer)
{
return TIMER_CAPW(timer);
}
/**
* Gets the pulse width in pulse capture mode,
* or gets the period width in duty cycle capture mode.
* @note See the datasheet for more concise diagrams.
* @param timer Select timer @ref timer_select
* @return The pulse width
*/
uint32_t timer_get_pulse_width(uint32_t timer)
{
return TIMER_CAPLH(timer);
}
/**
* Gets the current output period in PWM mode.
* @param timer Select timer @ref timer_select
* @return The current output period @ref timer_pwm_period
*/
enum timer_pwm_period timer_get_pwm_period(uint32_t timer)
{
return TIMER_MOD2LF(timer) & 0x1;
}
/**
* Gets the interrupt status after masking.
* @param timer Select timer @ref timer_select
* @return The interrupt status after masking
*/
bool timer_int_status(uint32_t timer)
{
return TIMER_INTMSKSTAT(timer) & 0x1;
}
/**
* Gets the interrupt status before masking.
* @param timer Select timer @ref timer_select
* @return The interrupt status before masking
*/
bool timer_int_raw_status(uint32_t timer)
{
return TIMER_INTSTAT(timer) & 0x1;
}
/**
* Gets the interrupt overflow status.
* Overflow will occur if the interrupt has not been cleared when a second
* interrupt happens.
* @param timer Select timer @ref timer_select
* @return The interrupt overflow status
*/
bool timer_int_overflow_status(uint32_t timer)
{
return TIMER_INTFLAG(timer) & 0x1;
}
/**@}*/

160
libopencm3/lib/swm050/wdt.c Normal file
View File

@@ -0,0 +1,160 @@
/** @defgroup wdg_file Watchdog peripheral API
* @brief SWM050 WDT API.
* @ingroup peripheral_apis
* LGPL License Terms @ref lgpl_license
* @author @htmlonly &copy; @endhtmlonly 2019
* Caleb Szalacinski <contact@skiboy.net>
*/
/*
* This file is part of the libopencm3 project.
*
* Copyright (C) 2019 Caleb Szalacinski <contact@skiboy.net>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
/**@{*/
#include <libopencm3/swm050/wdt.h>
#include <libopencm3/swm050/sysctl.h>
/**
* Sets up the WDT before the call to @ref wdt_enable().
* This only prepares the watchdog, it will not start counting until
* it is enabled.
* @param mode passed to @ref wdt_mode()
* @param time1 passed to @ref wdt_set_time()
* @param time2 passed to @ref wdt_set_time()
*/
void wdt_setup(enum wdt_modes mode, uint8_t time1, uint8_t time2)
{
wdt_clock_enable(1);
wdt_set_time(time1, time2);
wdt_mode(mode);
wdt_reset();
}
/**
* Enables the WDT.
* This actually starts the watchdog, you should have already selected
* modes and set timeouts.
* If @ref WDT_MODE_INT is used, the interrupt should also be enabled
* using the NVIC before enabling the WDT.
* @sa wdt_setup
* @param en enable
*/
void wdt_enable(bool en)
{
if (en) {
WDT_CR |= 0x1;
} else {
WDT_CR &= ~0x1;
}
}
/**
* Sets the WDT's mode of operation.
* @param mode The mode of operation @ref wdt_modes
*/
void wdt_mode(enum wdt_modes mode)
{
if (mode == WDT_MODE_INT) {
WDT_CR |= (1 << 1);
} else {
WDT_CR &= ~(1 << 1);
}
}
/**
* Reset the WDT's counter.
* The "feed the dog" operation. Must be called periodically to avoid a
* timeout. Calling this also clears any WDT interrupts.
*/
void wdt_reset(void)
{
WDT_CRR = 0x76;
}
/**
* Gets the WDT's interrupt status.
* @note Only useful with @ref WDT_MODE_INT
* @return The WDT's interrupt status. True if an interrupt has not been cleared.
*/
bool wdt_int_status(void)
{
return WDT_STAT & 0x1;
}
/**
* Clears the WDT's interrupt.
* @note Only useful with @ref WDT_MODE_INT
*/
void wdt_clear_int(void)
{
/* Read register to clear the interrupt */
uint32_t dummy = WDT_EOI;
/* Does nothing, but suppresses a -Wunused-variable warning */
(void)dummy;
}
/**
* Enables the WDT's clock.
* This only enables the clock to the peripheral.
* @sa wdt_setup
* @param en True to enable, false to disable
*/
void wdt_clock_enable(bool en)
{
if (en) {
SYSCTL_SYS_CFG_1 |= SYSCTL_SYS_CFG_1_WDT;
} else {
SYSCTL_SYS_CFG_1 &= ~SYSCTL_SYS_CFG_1_WDT;
}
}
/**
* Gets the current WDT counter value.
* The vendor-supplied documentation for the @ref WDT_CCVR register appears to be
* incorrect, and does not seem to be 1 bit wide, which would make no sense.
* @return The current WDT counter value
*/
uint32_t wdt_get_value(void)
{
return WDT_CCVR;
}
/**
* Sets the WDT's initial counter values.
* Both time1 and time2 follow the equation 2^(8 + i), where i is a value from
* 0 to 15, and where the result is in clock cycles.
* For example:
* time1 = 15
* 2^(8 + time1) / 18Mhz = 0.466s
* The majority of the vendor-supplied documentation appears to be completely
* incorrect about the equation used for these counters.
*
* Only the least significant 4 bits are used for both values, eg 0 to 15.
*
* @param time1 The timer value used in both modes. In @ref WDT_MODE_RESET, this
* value counts down to 0 and resets the system. In @ref WDT_MODE_INT, this value
* counts down to 0, generates a WDT interrupt, loads time2 into the counter,
* and counts down.
* @param time2 The timer value used after time1 in mode @ref WDT_MODE_INT. If
* this counts down to 0, and the WDT interrupt has not been cleared, the
* system resets. This has no use in mode @ref WDT_MODE_RESET.
*/
void wdt_set_time(uint8_t time1, uint8_t time2)
{
WDT_TORR = ((0xF & time1) << 4) | (0xF & time2);
}
/**@}*/