pico-tracker/firmware/src/system/events.c

178 wiersze
5.4 KiB
C

/*
* SAM D20/D21/R21 Event System Controller Driver
*
* Copyright (C) 2013-2014 Atmel Corporation. All rights reserved.
*
* \asf_license_start
*
* \page License
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. The name of Atmel may not be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 4. This software may only be redistributed and used in connection with an
* Atmel microcontroller product.
*
* THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* \asf_license_stop
*
*/
#include "system/events.h"
#include "system/system.h"
#include "samd20.h"
#define EVENTS_INVALID_CHANNEL 0xff
#define _EVENTS_START_OFFSET_BUSY_BITS 8
#define _EVENTS_START_OFFSET_USER_READY_BIT 0
#define _EVENTS_START_OFFSET_DETECTION_BIT 8
#define _EVENTS_START_OFFSET_OVERRUN_BIT 0
/**
* internal
*
*/
uint32_t _events_find_bit_position(uint8_t channel, uint8_t start_offset)
{
uint8_t byte_offset = channel >> 3;
uint32_t pos;
pos = (((channel % 8) + 1) << start_offset) * ((0xffff * byte_offset) + 1);
return pos;
}
void system_events_init(void)
{
/* Enable EVSYS register interface */
system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, PM_APBCMASK_EVSYS);
/* Make sure the EVSYS module is properly reset */
EVSYS->CTRL.reg = EVSYS_CTRL_SWRST;
while (EVSYS->CTRL.reg & EVSYS_CTRL_SWRST);
}
enum status_code events_allocate(uint8_t channel,
enum events_edge_detect edge_detect, /** edge detection mode */
enum events_path_selection path, /** events channel path */
uint8_t generator, /** event generator for the channel */
uint8_t clock_source) /** clock source for the event channel */
{
if (path != EVENTS_PATH_ASYNCHRONOUS) {
/* Set up a GLCK channel to use with the specific channel */
system_gclk_chan_set_config(EVSYS_GCLK_ID_0 + channel, (enum gclk_generator)clock_source);
system_gclk_chan_enable(EVSYS_GCLK_ID_0 + channel);
}
EVSYS->CHANNEL.reg = EVSYS_CHANNEL_CHANNEL(channel) |
EVSYS_CHANNEL_EVGEN(generator) |
EVSYS_CHANNEL_PATH(path) |
EVSYS_CHANNEL_EDGSEL(edge_detect);
return STATUS_OK;
}
enum status_code events_trigger(uint8_t channel)
{
/* Because of indirect access the channel must be set first */
((uint8_t*)&EVSYS->CHANNEL)[0] = EVSYS_CHANNEL_CHANNEL(channel);
/* Assert if event path is asynchronous */
if (EVSYS->CHANNEL.reg & EVSYS_CHANNEL_PATH(EVENTS_PATH_ASYNCHRONOUS)) {
return STATUS_ERR_DENIED;
}
/* Assert if event edge detection is not set to RISING */
if (!(EVSYS->CHANNEL.reg & EVSYS_CHANNEL_EDGSEL(EVENTS_EDGE_DETECT_RISING))) {
return STATUS_ERR_DENIED;
}
/* The GCLKREQ bit has to be set while triggering the software event */
EVSYS->CTRL.reg = EVSYS_CTRL_GCLKREQ;
((uint16_t*)&EVSYS->CHANNEL)[0] = EVSYS_CHANNEL_CHANNEL(channel) |
EVSYS_CHANNEL_SWEVT;
EVSYS->CTRL.reg &= ~EVSYS_CTRL_GCLKREQ;
return STATUS_OK;
}
bool events_is_busy(uint8_t channel)
{
return EVSYS->CHSTATUS.reg & (_events_find_bit_position(channel, _EVENTS_START_OFFSET_BUSY_BITS));
}
bool events_is_users_ready(uint8_t channel)
{
return EVSYS->CHSTATUS.reg & (_events_find_bit_position(channel, _EVENTS_START_OFFSET_USER_READY_BIT));
}
bool events_is_detected(uint8_t channel)
{
uint32_t flag = _events_find_bit_position(channel, _EVENTS_START_OFFSET_DETECTION_BIT);
/* Clear flag when read */
if (EVSYS->INTFLAG.reg & flag) {
EVSYS->INTFLAG.reg = flag;
return true;
}
return false;
}
bool events_is_overrun(uint8_t channel)
{
uint32_t flag = _events_find_bit_position(channel, _EVENTS_START_OFFSET_OVERRUN_BIT);
/* Clear flag when read */
if (EVSYS->INTFLAG.reg & flag) {
EVSYS->INTFLAG.reg = flag;
return true;
}
return false;
}
enum status_code events_attach_user(uint8_t channel, uint8_t user_id)
{
/* Channel number is n + 1 */
EVSYS->USER.reg = EVSYS_USER_CHANNEL(channel + 1) |
EVSYS_USER_USER(user_id);
return STATUS_OK;
}
enum status_code events_detach_user(uint8_t channel, uint8_t user_id)
{
/* Write 0 to the channel bit field to select no input */
EVSYS->USER.reg = EVSYS_USER_USER(user_id);
return STATUS_OK;
}