Revert "Move libmaple/* from variants to cores"
This reverts commit a3065b0c00
which resulted in Maple and maple mini boards not running / starting code after upload
This commit is contained in:
parent
d188c06504
commit
90e6ae40e9
|
@ -0,0 +1,724 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2011 LeafLabs LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/usb/stm32f1/usb_cdcacm.c
|
||||
* @brief USB CDC ACM (a.k.a. virtual serial terminal, VCOM).
|
||||
*
|
||||
* FIXME: this works on the STM32F1 USB peripherals, and probably no
|
||||
* place else. Nonportable bits really need to be factored out, and
|
||||
* the result made cleaner.
|
||||
*/
|
||||
|
||||
#include <libmaple/usb_cdcacm.h>
|
||||
|
||||
#include <libmaple/usb.h>
|
||||
#include <libmaple/nvic.h>
|
||||
#include <libmaple/delay.h>
|
||||
|
||||
/* Private headers */
|
||||
#include "usb_lib_globals.h"
|
||||
#include "usb_reg_map.h"
|
||||
|
||||
/* usb_lib headers */
|
||||
#include "usb_type.h"
|
||||
#include "usb_core.h"
|
||||
#include "usb_def.h"
|
||||
|
||||
/******************************************************************************
|
||||
******************************************************************************
|
||||
***
|
||||
*** HACK ALERT! FIXME FIXME FIXME FIXME!
|
||||
***
|
||||
*** A bunch of LeafLabs-specific configuration lives in here for
|
||||
*** now. This mess REALLY needs to get teased apart, with
|
||||
*** appropriate pieces moved into Wirish.
|
||||
***
|
||||
******************************************************************************
|
||||
*****************************************************************************/
|
||||
|
||||
#if !(defined(BOARD_maple) || defined(BOARD_maple_RET6) || \
|
||||
defined(BOARD_maple_mini) || defined(BOARD_maple_native))
|
||||
#warning USB CDC ACM relies on LeafLabs board-specific configuration.\
|
||||
You may have problems on non-LeafLabs boards.
|
||||
#endif
|
||||
|
||||
static void vcomDataTxCb(void);
|
||||
static void vcomDataRxCb(void);
|
||||
static uint8* vcomGetSetLineCoding(uint16);
|
||||
|
||||
static void usbInit(void);
|
||||
static void usbReset(void);
|
||||
static RESULT usbDataSetup(uint8 request);
|
||||
static RESULT usbNoDataSetup(uint8 request);
|
||||
static RESULT usbGetInterfaceSetting(uint8 interface, uint8 alt_setting);
|
||||
static uint8* usbGetDeviceDescriptor(uint16 length);
|
||||
static uint8* usbGetConfigDescriptor(uint16 length);
|
||||
static uint8* usbGetStringDescriptor(uint16 length);
|
||||
static void usbSetConfiguration(void);
|
||||
static void usbSetDeviceAddress(void);
|
||||
|
||||
/*
|
||||
* Descriptors
|
||||
*/
|
||||
|
||||
/* FIXME move to Wirish */
|
||||
#define LEAFLABS_ID_VENDOR 0x1EAF
|
||||
#define MAPLE_ID_PRODUCT 0x0004
|
||||
static const usb_descriptor_device usbVcomDescriptor_Device =
|
||||
USB_CDCACM_DECLARE_DEV_DESC(LEAFLABS_ID_VENDOR, MAPLE_ID_PRODUCT);
|
||||
|
||||
typedef struct {
|
||||
usb_descriptor_config_header Config_Header;
|
||||
usb_descriptor_interface CCI_Interface;
|
||||
CDC_FUNCTIONAL_DESCRIPTOR(2) CDC_Functional_IntHeader;
|
||||
CDC_FUNCTIONAL_DESCRIPTOR(2) CDC_Functional_CallManagement;
|
||||
CDC_FUNCTIONAL_DESCRIPTOR(1) CDC_Functional_ACM;
|
||||
CDC_FUNCTIONAL_DESCRIPTOR(2) CDC_Functional_Union;
|
||||
usb_descriptor_endpoint ManagementEndpoint;
|
||||
usb_descriptor_interface DCI_Interface;
|
||||
usb_descriptor_endpoint DataOutEndpoint;
|
||||
usb_descriptor_endpoint DataInEndpoint;
|
||||
} __packed usb_descriptor_config;
|
||||
|
||||
#define MAX_POWER (100 >> 1)
|
||||
static const usb_descriptor_config usbVcomDescriptor_Config = {
|
||||
.Config_Header = {
|
||||
.bLength = sizeof(usb_descriptor_config_header),
|
||||
.bDescriptorType = USB_DESCRIPTOR_TYPE_CONFIGURATION,
|
||||
.wTotalLength = sizeof(usb_descriptor_config),
|
||||
.bNumInterfaces = 0x02,
|
||||
.bConfigurationValue = 0x01,
|
||||
.iConfiguration = 0x00,
|
||||
.bmAttributes = (USB_CONFIG_ATTR_BUSPOWERED |
|
||||
USB_CONFIG_ATTR_SELF_POWERED),
|
||||
.bMaxPower = MAX_POWER,
|
||||
},
|
||||
|
||||
.CCI_Interface = {
|
||||
.bLength = sizeof(usb_descriptor_interface),
|
||||
.bDescriptorType = USB_DESCRIPTOR_TYPE_INTERFACE,
|
||||
.bInterfaceNumber = 0x00,
|
||||
.bAlternateSetting = 0x00,
|
||||
.bNumEndpoints = 0x01,
|
||||
.bInterfaceClass = USB_INTERFACE_CLASS_CDC,
|
||||
.bInterfaceSubClass = USB_INTERFACE_SUBCLASS_CDC_ACM,
|
||||
.bInterfaceProtocol = 0x01, /* Common AT Commands */
|
||||
.iInterface = 0x00,
|
||||
},
|
||||
|
||||
.CDC_Functional_IntHeader = {
|
||||
.bLength = CDC_FUNCTIONAL_DESCRIPTOR_SIZE(2),
|
||||
.bDescriptorType = 0x24,
|
||||
.SubType = 0x00,
|
||||
.Data = {0x01, 0x10},
|
||||
},
|
||||
|
||||
.CDC_Functional_CallManagement = {
|
||||
.bLength = CDC_FUNCTIONAL_DESCRIPTOR_SIZE(2),
|
||||
.bDescriptorType = 0x24,
|
||||
.SubType = 0x01,
|
||||
.Data = {0x03, 0x01},
|
||||
},
|
||||
|
||||
.CDC_Functional_ACM = {
|
||||
.bLength = CDC_FUNCTIONAL_DESCRIPTOR_SIZE(1),
|
||||
.bDescriptorType = 0x24,
|
||||
.SubType = 0x02,
|
||||
.Data = {0x06},
|
||||
},
|
||||
|
||||
.CDC_Functional_Union = {
|
||||
.bLength = CDC_FUNCTIONAL_DESCRIPTOR_SIZE(2),
|
||||
.bDescriptorType = 0x24,
|
||||
.SubType = 0x06,
|
||||
.Data = {0x00, 0x01},
|
||||
},
|
||||
|
||||
.ManagementEndpoint = {
|
||||
.bLength = sizeof(usb_descriptor_endpoint),
|
||||
.bDescriptorType = USB_DESCRIPTOR_TYPE_ENDPOINT,
|
||||
.bEndpointAddress = (USB_DESCRIPTOR_ENDPOINT_IN |
|
||||
USB_CDCACM_MANAGEMENT_ENDP),
|
||||
.bmAttributes = USB_EP_TYPE_INTERRUPT,
|
||||
.wMaxPacketSize = USB_CDCACM_MANAGEMENT_EPSIZE,
|
||||
.bInterval = 0xFF,
|
||||
},
|
||||
|
||||
.DCI_Interface = {
|
||||
.bLength = sizeof(usb_descriptor_interface),
|
||||
.bDescriptorType = USB_DESCRIPTOR_TYPE_INTERFACE,
|
||||
.bInterfaceNumber = 0x01,
|
||||
.bAlternateSetting = 0x00,
|
||||
.bNumEndpoints = 0x02,
|
||||
.bInterfaceClass = USB_INTERFACE_CLASS_DIC,
|
||||
.bInterfaceSubClass = 0x00, /* None */
|
||||
.bInterfaceProtocol = 0x00, /* None */
|
||||
.iInterface = 0x00,
|
||||
},
|
||||
|
||||
.DataOutEndpoint = {
|
||||
.bLength = sizeof(usb_descriptor_endpoint),
|
||||
.bDescriptorType = USB_DESCRIPTOR_TYPE_ENDPOINT,
|
||||
.bEndpointAddress = (USB_DESCRIPTOR_ENDPOINT_OUT |
|
||||
USB_CDCACM_RX_ENDP),
|
||||
.bmAttributes = USB_EP_TYPE_BULK,
|
||||
.wMaxPacketSize = USB_CDCACM_RX_EPSIZE,
|
||||
.bInterval = 0x00,
|
||||
},
|
||||
|
||||
.DataInEndpoint = {
|
||||
.bLength = sizeof(usb_descriptor_endpoint),
|
||||
.bDescriptorType = USB_DESCRIPTOR_TYPE_ENDPOINT,
|
||||
.bEndpointAddress = (USB_DESCRIPTOR_ENDPOINT_IN | USB_CDCACM_TX_ENDP),
|
||||
.bmAttributes = USB_EP_TYPE_BULK,
|
||||
.wMaxPacketSize = USB_CDCACM_TX_EPSIZE,
|
||||
.bInterval = 0x00,
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
String Descriptors:
|
||||
|
||||
we may choose to specify any or none of the following string
|
||||
identifiers:
|
||||
|
||||
iManufacturer: LeafLabs
|
||||
iProduct: Maple
|
||||
iSerialNumber: NONE
|
||||
iConfiguration: NONE
|
||||
iInterface(CCI): NONE
|
||||
iInterface(DCI): NONE
|
||||
|
||||
*/
|
||||
|
||||
/* Unicode language identifier: 0x0409 is US English */
|
||||
/* FIXME move to Wirish */
|
||||
static const usb_descriptor_string usbVcomDescriptor_LangID = {
|
||||
.bLength = USB_DESCRIPTOR_STRING_LEN(1),
|
||||
.bDescriptorType = USB_DESCRIPTOR_TYPE_STRING,
|
||||
.bString = {0x09, 0x04},
|
||||
};
|
||||
|
||||
/* FIXME move to Wirish */
|
||||
static const usb_descriptor_string usbVcomDescriptor_iManufacturer = {
|
||||
.bLength = USB_DESCRIPTOR_STRING_LEN(8),
|
||||
.bDescriptorType = USB_DESCRIPTOR_TYPE_STRING,
|
||||
.bString = {'L', 0, 'e', 0, 'a', 0, 'f', 0,
|
||||
'L', 0, 'a', 0, 'b', 0, 's', 0},
|
||||
};
|
||||
|
||||
/* FIXME move to Wirish */
|
||||
static const usb_descriptor_string usbVcomDescriptor_iProduct = {
|
||||
.bLength = USB_DESCRIPTOR_STRING_LEN(5),
|
||||
.bDescriptorType = USB_DESCRIPTOR_TYPE_STRING,
|
||||
.bString = {'M', 0, 'a', 0, 'p', 0, 'l', 0, 'e', 0},
|
||||
};
|
||||
|
||||
static ONE_DESCRIPTOR Device_Descriptor = {
|
||||
(uint8*)&usbVcomDescriptor_Device,
|
||||
sizeof(usb_descriptor_device)
|
||||
};
|
||||
|
||||
static ONE_DESCRIPTOR Config_Descriptor = {
|
||||
(uint8*)&usbVcomDescriptor_Config,
|
||||
sizeof(usb_descriptor_config)
|
||||
};
|
||||
|
||||
#define N_STRING_DESCRIPTORS 3
|
||||
static ONE_DESCRIPTOR String_Descriptor[N_STRING_DESCRIPTORS] = {
|
||||
{(uint8*)&usbVcomDescriptor_LangID, USB_DESCRIPTOR_STRING_LEN(1)},
|
||||
{(uint8*)&usbVcomDescriptor_iManufacturer,USB_DESCRIPTOR_STRING_LEN(8)},
|
||||
{(uint8*)&usbVcomDescriptor_iProduct, USB_DESCRIPTOR_STRING_LEN(5)}
|
||||
};
|
||||
|
||||
/*
|
||||
* Etc.
|
||||
*/
|
||||
|
||||
/* I/O state */
|
||||
|
||||
/* Received data */
|
||||
static volatile uint8 vcomBufferRx[USB_CDCACM_RX_EPSIZE];
|
||||
/* Read index into vcomBufferRx */
|
||||
static volatile uint32 rx_offset = 0;
|
||||
/* Number of bytes left to transmit */
|
||||
static volatile uint32 n_unsent_bytes = 0;
|
||||
/* Are we currently sending an IN packet? */
|
||||
static volatile uint8 transmitting = 0;
|
||||
/* Number of unread bytes */
|
||||
static volatile uint32 n_unread_bytes = 0;
|
||||
|
||||
/* Other state (line coding, DTR/RTS) */
|
||||
|
||||
static volatile usb_cdcacm_line_coding line_coding = {
|
||||
/* This default is 115200 baud, 8N1. */
|
||||
.dwDTERate = 115200,
|
||||
.bCharFormat = USB_CDCACM_STOP_BITS_1,
|
||||
.bParityType = USB_CDCACM_PARITY_NONE,
|
||||
.bDataBits = 8,
|
||||
};
|
||||
|
||||
/* DTR in bit 0, RTS in bit 1. */
|
||||
static volatile uint8 line_dtr_rts = 0;
|
||||
|
||||
/*
|
||||
* Endpoint callbacks
|
||||
*/
|
||||
|
||||
static void (*ep_int_in[7])(void) =
|
||||
{vcomDataTxCb,
|
||||
NOP_Process,
|
||||
NOP_Process,
|
||||
NOP_Process,
|
||||
NOP_Process,
|
||||
NOP_Process,
|
||||
NOP_Process};
|
||||
|
||||
static void (*ep_int_out[7])(void) =
|
||||
{NOP_Process,
|
||||
NOP_Process,
|
||||
vcomDataRxCb,
|
||||
NOP_Process,
|
||||
NOP_Process,
|
||||
NOP_Process,
|
||||
NOP_Process};
|
||||
|
||||
/*
|
||||
* Globals required by usb_lib/
|
||||
*
|
||||
* Mark these weak so they can be overriden to implement other USB
|
||||
* functionality.
|
||||
*/
|
||||
|
||||
#define NUM_ENDPTS 0x04
|
||||
__weak DEVICE Device_Table = {
|
||||
.Total_Endpoint = NUM_ENDPTS,
|
||||
.Total_Configuration = 1
|
||||
};
|
||||
|
||||
#define MAX_PACKET_SIZE 0x40 /* 64B, maximum for USB FS Devices */
|
||||
__weak DEVICE_PROP Device_Property = {
|
||||
.Init = usbInit,
|
||||
.Reset = usbReset,
|
||||
.Process_Status_IN = NOP_Process,
|
||||
.Process_Status_OUT = NOP_Process,
|
||||
.Class_Data_Setup = usbDataSetup,
|
||||
.Class_NoData_Setup = usbNoDataSetup,
|
||||
.Class_Get_Interface_Setting = usbGetInterfaceSetting,
|
||||
.GetDeviceDescriptor = usbGetDeviceDescriptor,
|
||||
.GetConfigDescriptor = usbGetConfigDescriptor,
|
||||
.GetStringDescriptor = usbGetStringDescriptor,
|
||||
.RxEP_buffer = NULL,
|
||||
.MaxPacketSize = MAX_PACKET_SIZE
|
||||
};
|
||||
|
||||
__weak USER_STANDARD_REQUESTS User_Standard_Requests = {
|
||||
.User_GetConfiguration = NOP_Process,
|
||||
.User_SetConfiguration = usbSetConfiguration,
|
||||
.User_GetInterface = NOP_Process,
|
||||
.User_SetInterface = NOP_Process,
|
||||
.User_GetStatus = NOP_Process,
|
||||
.User_ClearFeature = NOP_Process,
|
||||
.User_SetEndPointFeature = NOP_Process,
|
||||
.User_SetDeviceFeature = NOP_Process,
|
||||
.User_SetDeviceAddress = usbSetDeviceAddress
|
||||
};
|
||||
|
||||
/*
|
||||
* User hooks
|
||||
*/
|
||||
|
||||
static void (*rx_hook)(unsigned, void*) = 0;
|
||||
static void (*iface_setup_hook)(unsigned, void*) = 0;
|
||||
|
||||
void usb_cdcacm_set_hooks(unsigned hook_flags, void (*hook)(unsigned, void*)) {
|
||||
if (hook_flags & USB_CDCACM_HOOK_RX) {
|
||||
rx_hook = hook;
|
||||
}
|
||||
if (hook_flags & USB_CDCACM_HOOK_IFACE_SETUP) {
|
||||
iface_setup_hook = hook;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* CDC ACM interface
|
||||
*/
|
||||
|
||||
void usb_cdcacm_enable(gpio_dev *disc_dev, uint8 disc_bit) {
|
||||
/* Present ourselves to the host. Writing 0 to "disc" pin must
|
||||
* pull USB_DP pin up while leaving USB_DM pulled down by the
|
||||
* transceiver. See USB 2.0 spec, section 7.1.7.3. */
|
||||
gpio_set_mode(disc_dev, disc_bit, GPIO_OUTPUT_PP);
|
||||
gpio_write_bit(disc_dev, disc_bit, 0);
|
||||
|
||||
/* Initialize the USB peripheral. */
|
||||
usb_init_usblib(USBLIB, ep_int_in, ep_int_out);
|
||||
}
|
||||
|
||||
void usb_cdcacm_disable(gpio_dev *disc_dev, uint8 disc_bit) {
|
||||
/* Turn off the interrupt and signal disconnect (see e.g. USB 2.0
|
||||
* spec, section 7.1.7.3). */
|
||||
nvic_irq_disable(NVIC_USB_LP_CAN_RX0);
|
||||
gpio_write_bit(disc_dev, disc_bit, 1);
|
||||
}
|
||||
|
||||
void usb_cdcacm_putc(char ch) {
|
||||
while (!usb_cdcacm_tx((uint8*)&ch, 1))
|
||||
;
|
||||
}
|
||||
|
||||
/* This function is non-blocking.
|
||||
*
|
||||
* It copies data from a usercode buffer into the USB peripheral TX
|
||||
* buffer, and returns the number of bytes copied. */
|
||||
uint32 usb_cdcacm_tx(const uint8* buf, uint32 len) {
|
||||
/* Last transmission hasn't finished, so abort. */
|
||||
if (usb_cdcacm_is_transmitting()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We can only put USB_CDCACM_TX_EPSIZE bytes in the buffer. */
|
||||
if (len > USB_CDCACM_TX_EPSIZE) {
|
||||
len = USB_CDCACM_TX_EPSIZE;
|
||||
}
|
||||
|
||||
/* Queue bytes for sending. */
|
||||
if (len) {
|
||||
usb_copy_to_pma(buf, len, USB_CDCACM_TX_ADDR);
|
||||
}
|
||||
// We still need to wait for the interrupt, even if we're sending
|
||||
// zero bytes. (Sending zero-size packets is useful for flushing
|
||||
// host-side buffers.)
|
||||
usb_set_ep_tx_count(USB_CDCACM_TX_ENDP, len);
|
||||
n_unsent_bytes = len;
|
||||
transmitting = 1;
|
||||
usb_set_ep_tx_stat(USB_CDCACM_TX_ENDP, USB_EP_STAT_TX_VALID);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
uint32 usb_cdcacm_data_available(void) {
|
||||
return n_unread_bytes;
|
||||
}
|
||||
|
||||
uint8 usb_cdcacm_is_transmitting(void) {
|
||||
return transmitting;
|
||||
}
|
||||
|
||||
uint16 usb_cdcacm_get_pending(void) {
|
||||
return n_unsent_bytes;
|
||||
}
|
||||
|
||||
/* Nonblocking byte receive.
|
||||
*
|
||||
* Copies up to len bytes from our private data buffer (*NOT* the PMA)
|
||||
* into buf and deq's the FIFO. */
|
||||
uint32 usb_cdcacm_rx(uint8* buf, uint32 len) {
|
||||
/* Copy bytes to buffer. */
|
||||
uint32 n_copied = usb_cdcacm_peek(buf, len);
|
||||
|
||||
/* Mark bytes as read. */
|
||||
n_unread_bytes -= n_copied;
|
||||
rx_offset += n_copied;
|
||||
|
||||
/* If all bytes have been read, re-enable the RX endpoint, which
|
||||
* was set to NAK when the current batch of bytes was received. */
|
||||
if (n_unread_bytes == 0) {
|
||||
usb_set_ep_rx_count(USB_CDCACM_RX_ENDP, USB_CDCACM_RX_EPSIZE);
|
||||
usb_set_ep_rx_stat(USB_CDCACM_RX_ENDP, USB_EP_STAT_RX_VALID);
|
||||
rx_offset = 0;
|
||||
}
|
||||
|
||||
return n_copied;
|
||||
}
|
||||
|
||||
/* Nonblocking byte lookahead.
|
||||
*
|
||||
* Looks at unread bytes without marking them as read. */
|
||||
uint32 usb_cdcacm_peek(uint8* buf, uint32 len) {
|
||||
int i;
|
||||
|
||||
if (len > n_unread_bytes) {
|
||||
len = n_unread_bytes;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
buf[i] = vcomBufferRx[i + rx_offset];
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Roger Clark. Added. for Arduino 1.0 API support of Serial.peek() */
|
||||
int usb_cdcacm_peek_char()
|
||||
{
|
||||
return 2;
|
||||
/*
|
||||
if (n_unread_bytes == 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return vcomBufferRx[rx_offset];
|
||||
*/
|
||||
}
|
||||
|
||||
uint8 usb_cdcacm_get_dtr() {
|
||||
return ((line_dtr_rts & USB_CDCACM_CONTROL_LINE_DTR) != 0);
|
||||
}
|
||||
|
||||
uint8 usb_cdcacm_get_rts() {
|
||||
return ((line_dtr_rts & USB_CDCACM_CONTROL_LINE_RTS) != 0);
|
||||
}
|
||||
|
||||
void usb_cdcacm_get_line_coding(usb_cdcacm_line_coding *ret) {
|
||||
ret->dwDTERate = line_coding.dwDTERate;
|
||||
ret->bCharFormat = line_coding.bCharFormat;
|
||||
ret->bParityType = line_coding.bParityType;
|
||||
ret->bDataBits = line_coding.bDataBits;
|
||||
}
|
||||
|
||||
int usb_cdcacm_get_baud(void) {
|
||||
return line_coding.dwDTERate;
|
||||
}
|
||||
|
||||
int usb_cdcacm_get_stop_bits(void) {
|
||||
return line_coding.bCharFormat;
|
||||
}
|
||||
|
||||
int usb_cdcacm_get_parity(void) {
|
||||
return line_coding.bParityType;
|
||||
}
|
||||
|
||||
int usb_cdcacm_get_n_data_bits(void) {
|
||||
return line_coding.bDataBits;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Callbacks
|
||||
*/
|
||||
|
||||
static void vcomDataTxCb(void) {
|
||||
n_unsent_bytes = 0;
|
||||
transmitting = 0;
|
||||
}
|
||||
|
||||
static void vcomDataRxCb(void) {
|
||||
usb_set_ep_rx_stat(USB_CDCACM_RX_ENDP, USB_EP_STAT_RX_NAK);
|
||||
n_unread_bytes = usb_get_ep_rx_count(USB_CDCACM_RX_ENDP);
|
||||
/* This copy won't overwrite unread bytes, since we've set the RX
|
||||
* endpoint to NAK, and will only set it to VALID when all bytes
|
||||
* have been read. */
|
||||
usb_copy_from_pma((uint8*)vcomBufferRx, n_unread_bytes,
|
||||
USB_CDCACM_RX_ADDR);
|
||||
|
||||
|
||||
if (n_unread_bytes == 0) {
|
||||
usb_set_ep_rx_count(USB_CDCACM_RX_ENDP, USB_CDCACM_RX_EPSIZE);
|
||||
usb_set_ep_rx_stat(USB_CDCACM_RX_ENDP, USB_EP_STAT_RX_VALID);
|
||||
rx_offset = 0;
|
||||
}
|
||||
|
||||
if (rx_hook) {
|
||||
rx_hook(USB_CDCACM_HOOK_RX, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static uint8* vcomGetSetLineCoding(uint16 length) {
|
||||
if (length == 0) {
|
||||
pInformation->Ctrl_Info.Usb_wLength = sizeof(struct usb_cdcacm_line_coding);
|
||||
}
|
||||
return (uint8*)&line_coding;
|
||||
}
|
||||
|
||||
static void usbInit(void) {
|
||||
pInformation->Current_Configuration = 0;
|
||||
|
||||
USB_BASE->CNTR = USB_CNTR_FRES;
|
||||
|
||||
USBLIB->irq_mask = 0;
|
||||
USB_BASE->CNTR = USBLIB->irq_mask;
|
||||
USB_BASE->ISTR = 0;
|
||||
USBLIB->irq_mask = USB_CNTR_RESETM | USB_CNTR_SUSPM | USB_CNTR_WKUPM;
|
||||
USB_BASE->CNTR = USBLIB->irq_mask;
|
||||
|
||||
USB_BASE->ISTR = 0;
|
||||
USBLIB->irq_mask = USB_ISR_MSK;
|
||||
USB_BASE->CNTR = USBLIB->irq_mask;
|
||||
|
||||
nvic_irq_enable(NVIC_USB_LP_CAN_RX0);
|
||||
USBLIB->state = USB_UNCONNECTED;
|
||||
}
|
||||
|
||||
#define BTABLE_ADDRESS 0x00
|
||||
static void usbReset(void) {
|
||||
pInformation->Current_Configuration = 0;
|
||||
|
||||
/* current feature is current bmAttributes */
|
||||
pInformation->Current_Feature = (USB_CONFIG_ATTR_BUSPOWERED |
|
||||
USB_CONFIG_ATTR_SELF_POWERED);
|
||||
|
||||
USB_BASE->BTABLE = BTABLE_ADDRESS;
|
||||
|
||||
/* setup control endpoint 0 */
|
||||
usb_set_ep_type(USB_EP0, USB_EP_EP_TYPE_CONTROL);
|
||||
usb_set_ep_tx_stat(USB_EP0, USB_EP_STAT_TX_STALL);
|
||||
usb_set_ep_rx_addr(USB_EP0, USB_CDCACM_CTRL_RX_ADDR);
|
||||
usb_set_ep_tx_addr(USB_EP0, USB_CDCACM_CTRL_TX_ADDR);
|
||||
usb_clear_status_out(USB_EP0);
|
||||
|
||||
usb_set_ep_rx_count(USB_EP0, pProperty->MaxPacketSize);
|
||||
usb_set_ep_rx_stat(USB_EP0, USB_EP_STAT_RX_VALID);
|
||||
|
||||
/* setup management endpoint 1 */
|
||||
usb_set_ep_type(USB_CDCACM_MANAGEMENT_ENDP, USB_EP_EP_TYPE_INTERRUPT);
|
||||
usb_set_ep_tx_addr(USB_CDCACM_MANAGEMENT_ENDP,
|
||||
USB_CDCACM_MANAGEMENT_ADDR);
|
||||
usb_set_ep_tx_stat(USB_CDCACM_MANAGEMENT_ENDP, USB_EP_STAT_TX_NAK);
|
||||
usb_set_ep_rx_stat(USB_CDCACM_MANAGEMENT_ENDP, USB_EP_STAT_RX_DISABLED);
|
||||
|
||||
/* TODO figure out differences in style between RX/TX EP setup */
|
||||
|
||||
/* set up data endpoint OUT (RX) */
|
||||
usb_set_ep_type(USB_CDCACM_RX_ENDP, USB_EP_EP_TYPE_BULK);
|
||||
usb_set_ep_rx_addr(USB_CDCACM_RX_ENDP, USB_CDCACM_RX_ADDR);
|
||||
usb_set_ep_rx_count(USB_CDCACM_RX_ENDP, USB_CDCACM_RX_EPSIZE);
|
||||
usb_set_ep_rx_stat(USB_CDCACM_RX_ENDP, USB_EP_STAT_RX_VALID);
|
||||
|
||||
/* set up data endpoint IN (TX) */
|
||||
usb_set_ep_type(USB_CDCACM_TX_ENDP, USB_EP_EP_TYPE_BULK);
|
||||
usb_set_ep_tx_addr(USB_CDCACM_TX_ENDP, USB_CDCACM_TX_ADDR);
|
||||
usb_set_ep_tx_stat(USB_CDCACM_TX_ENDP, USB_EP_STAT_TX_NAK);
|
||||
usb_set_ep_rx_stat(USB_CDCACM_TX_ENDP, USB_EP_STAT_RX_DISABLED);
|
||||
|
||||
USBLIB->state = USB_ATTACHED;
|
||||
SetDeviceAddress(0);
|
||||
|
||||
/* Reset the RX/TX state */
|
||||
n_unread_bytes = 0;
|
||||
n_unsent_bytes = 0;
|
||||
rx_offset = 0;
|
||||
transmitting = 0;
|
||||
}
|
||||
|
||||
static RESULT usbDataSetup(uint8 request) {
|
||||
uint8* (*CopyRoutine)(uint16) = 0;
|
||||
|
||||
if (Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT)) {
|
||||
switch (request) {
|
||||
case USB_CDCACM_GET_LINE_CODING:
|
||||
CopyRoutine = vcomGetSetLineCoding;
|
||||
break;
|
||||
case USB_CDCACM_SET_LINE_CODING:
|
||||
CopyRoutine = vcomGetSetLineCoding;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Call the user hook. */
|
||||
if (iface_setup_hook) {
|
||||
uint8 req_copy = request;
|
||||
iface_setup_hook(USB_CDCACM_HOOK_IFACE_SETUP, &req_copy);
|
||||
}
|
||||
}
|
||||
|
||||
if (CopyRoutine == NULL) {
|
||||
return USB_UNSUPPORT;
|
||||
}
|
||||
|
||||
pInformation->Ctrl_Info.CopyData = CopyRoutine;
|
||||
pInformation->Ctrl_Info.Usb_wOffset = 0;
|
||||
(*CopyRoutine)(0);
|
||||
return USB_SUCCESS;
|
||||
}
|
||||
|
||||
static RESULT usbNoDataSetup(uint8 request) {
|
||||
RESULT ret = USB_UNSUPPORT;
|
||||
|
||||
if (Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT)) {
|
||||
switch (request) {
|
||||
case USB_CDCACM_SET_COMM_FEATURE:
|
||||
/* We support set comm. feature, but don't handle it. */
|
||||
ret = USB_SUCCESS;
|
||||
break;
|
||||
case USB_CDCACM_SET_CONTROL_LINE_STATE:
|
||||
/* Track changes to DTR and RTS. */
|
||||
line_dtr_rts = (pInformation->USBwValues.bw.bb0 &
|
||||
(USB_CDCACM_CONTROL_LINE_DTR |
|
||||
USB_CDCACM_CONTROL_LINE_RTS));
|
||||
ret = USB_SUCCESS;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Call the user hook. */
|
||||
if (iface_setup_hook) {
|
||||
uint8 req_copy = request;
|
||||
iface_setup_hook(USB_CDCACM_HOOK_IFACE_SETUP, &req_copy);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static RESULT usbGetInterfaceSetting(uint8 interface, uint8 alt_setting) {
|
||||
if (alt_setting > 0) {
|
||||
return USB_UNSUPPORT;
|
||||
} else if (interface > 1) {
|
||||
return USB_UNSUPPORT;
|
||||
}
|
||||
|
||||
return USB_SUCCESS;
|
||||
}
|
||||
|
||||
static uint8* usbGetDeviceDescriptor(uint16 length) {
|
||||
return Standard_GetDescriptorData(length, &Device_Descriptor);
|
||||
}
|
||||
|
||||
static uint8* usbGetConfigDescriptor(uint16 length) {
|
||||
return Standard_GetDescriptorData(length, &Config_Descriptor);
|
||||
}
|
||||
|
||||
static uint8* usbGetStringDescriptor(uint16 length) {
|
||||
uint8 wValue0 = pInformation->USBwValue0;
|
||||
|
||||
if (wValue0 > N_STRING_DESCRIPTORS) {
|
||||
return NULL;
|
||||
}
|
||||
return Standard_GetDescriptorData(length, &String_Descriptor[wValue0]);
|
||||
}
|
||||
|
||||
static void usbSetConfiguration(void) {
|
||||
if (pInformation->Current_Configuration != 0) {
|
||||
USBLIB->state = USB_CONFIGURED;
|
||||
}
|
||||
}
|
||||
|
||||
static void usbSetDeviceAddress(void) {
|
||||
USBLIB->state = USB_ADDRESSED;
|
||||
}
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Perry Hung.
|
||||
* Copyright (c) 2011, 2012 LeafLabs, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/adc.c
|
||||
* @author Marti Bolivar <mbolivar@leaflabs.com>,
|
||||
* Perry Hung <perry@leaflabs.com>
|
||||
* @brief Analog to digital converter routines
|
||||
*/
|
||||
|
||||
#include <libmaple/adc.h>
|
||||
#include <libmaple/libmaple.h>
|
||||
#include <libmaple/rcc.h>
|
||||
|
||||
/**
|
||||
* @brief Initialize an ADC peripheral.
|
||||
*
|
||||
* Initializes the RCC clock line for the given peripheral. Resets
|
||||
* ADC device registers.
|
||||
*
|
||||
* @param dev ADC peripheral to initialize
|
||||
*/
|
||||
void adc_init(const adc_dev *dev) {
|
||||
rcc_clk_enable(dev->clk_id);
|
||||
rcc_reset_dev(dev->clk_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set external event select for regular group
|
||||
* @param dev ADC device
|
||||
* @param event Event used to trigger the start of conversion.
|
||||
* @see adc_extsel_event
|
||||
*/
|
||||
void adc_set_extsel(const adc_dev *dev, adc_extsel_event event) {
|
||||
uint32 cr2 = dev->regs->CR2;
|
||||
cr2 &= ~ADC_CR2_EXTSEL;
|
||||
cr2 |= event;
|
||||
dev->regs->CR2 = cr2;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the sample rate for all channels on an ADC device.
|
||||
*
|
||||
* Don't call this during conversion.
|
||||
*
|
||||
* @param dev adc device
|
||||
* @param smp_rate sample rate to set
|
||||
* @see adc_smp_rate
|
||||
*/
|
||||
void adc_set_sample_rate(const adc_dev *dev, adc_smp_rate smp_rate) {
|
||||
uint32 adc_smpr1_val = 0, adc_smpr2_val = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
if (i < 8) {
|
||||
/* ADC_SMPR1 determines sample time for channels [10,17] */
|
||||
adc_smpr1_val |= smp_rate << (i * 3);
|
||||
}
|
||||
/* ADC_SMPR2 determines sample time for channels [0,9] */
|
||||
adc_smpr2_val |= smp_rate << (i * 3);
|
||||
}
|
||||
|
||||
dev->regs->SMPR1 = adc_smpr1_val;
|
||||
dev->regs->SMPR2 = adc_smpr2_val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Perform a single synchronous software triggered conversion on a
|
||||
* channel.
|
||||
* @param dev ADC device to use for reading.
|
||||
* @param channel channel to convert
|
||||
* @return conversion result
|
||||
*/
|
||||
uint16 adc_read(const adc_dev *dev, uint8 channel) {
|
||||
adc_reg_map *regs = dev->regs;
|
||||
|
||||
adc_set_reg_seqlen(dev, 1);
|
||||
|
||||
regs->SQR3 = channel;
|
||||
regs->CR2 |= ADC_CR2_SWSTART;
|
||||
while (!(regs->SR & ADC_SR_EOC))
|
||||
;
|
||||
|
||||
return (uint16)(regs->DR & ADC_DR_DATA);
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2012 LeafLabs, LLC.
|
||||
* Copyright (c) 2010 Perry Hung.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/stm32f1/adc.c
|
||||
* @author Marti Bolivar <mbolivar@leaflabs.com>,
|
||||
* Perry Hung <perry@leaflabs.com>
|
||||
* @brief STM32F1 ADC support.
|
||||
*/
|
||||
|
||||
#include <libmaple/adc.h>
|
||||
#include <libmaple/gpio.h>
|
||||
|
||||
/*
|
||||
* Devices
|
||||
*/
|
||||
|
||||
static adc_dev adc1 = {
|
||||
.regs = ADC1_BASE,
|
||||
.clk_id = RCC_ADC1,
|
||||
};
|
||||
/** ADC1 device. */
|
||||
const adc_dev *ADC1 = &adc1;
|
||||
|
||||
static adc_dev adc2 = {
|
||||
.regs = ADC2_BASE,
|
||||
.clk_id = RCC_ADC2,
|
||||
};
|
||||
/** ADC2 device. */
|
||||
const adc_dev *ADC2 = &adc2;
|
||||
|
||||
#if defined(STM32_HIGH_DENSITY) || defined(STM32_XL_DENSITY)
|
||||
static adc_dev adc3 = {
|
||||
.regs = ADC3_BASE,
|
||||
.clk_id = RCC_ADC3,
|
||||
};
|
||||
/** ADC3 device. */
|
||||
const adc_dev *ADC3 = &adc3;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* STM32F1 routines
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Calibrate an ADC peripheral
|
||||
*
|
||||
* Availability: STM32F1.
|
||||
*
|
||||
* @param dev adc device
|
||||
*/
|
||||
void adc_calibrate(const adc_dev *dev) {
|
||||
__io uint32 *rstcal_bit = bb_perip(&(dev->regs->CR2), 3);
|
||||
__io uint32 *cal_bit = bb_perip(&(dev->regs->CR2), 2);
|
||||
|
||||
*rstcal_bit = 1;
|
||||
while (*rstcal_bit)
|
||||
;
|
||||
|
||||
*cal_bit = 1;
|
||||
while (*cal_bit)
|
||||
;
|
||||
}
|
||||
|
||||
/*
|
||||
* Common routines
|
||||
*/
|
||||
|
||||
void adc_set_prescaler(adc_prescaler pre) {
|
||||
rcc_set_prescaler(RCC_PRESCALER_ADC, (uint32)pre);
|
||||
}
|
||||
|
||||
void adc_foreach(void (*fn)(const adc_dev*)) {
|
||||
fn(ADC1);
|
||||
fn(ADC2);
|
||||
#if defined(STM32_HIGH_DENSITY) || defined(STM32_XL_DENSITY)
|
||||
fn(ADC3);
|
||||
#endif
|
||||
}
|
||||
|
||||
void adc_config_gpio(const adc_dev *ignored, gpio_dev *gdev, uint8 bit) {
|
||||
gpio_set_mode(gdev, bit, GPIO_INPUT_ANALOG);
|
||||
}
|
||||
|
||||
void adc_enable_single_swstart(const adc_dev *dev) {
|
||||
adc_init(dev);
|
||||
adc_set_extsel(dev, ADC_SWSTART);
|
||||
adc_set_exttrig(dev, 1);
|
||||
adc_enable(dev);
|
||||
adc_calibrate(dev);
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 LeafLabs, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/stm32f1/bkp.c
|
||||
* @brief STM32F1 Backup register support.
|
||||
*/
|
||||
|
||||
#include <libmaple/bkp.h>
|
||||
#include <libmaple/pwr.h>
|
||||
#include <libmaple/rcc.h>
|
||||
#include <libmaple/bitband.h>
|
||||
|
||||
static inline __io uint32* data_register(uint8 reg);
|
||||
|
||||
bkp_dev bkp = {
|
||||
.regs = BKP_BASE,
|
||||
};
|
||||
/** Backup device. */
|
||||
const bkp_dev *BKP = &bkp;
|
||||
|
||||
/**
|
||||
* @brief Initialize backup interface.
|
||||
*
|
||||
* Enables the power and backup interface clocks, and resets the
|
||||
* backup device.
|
||||
*/
|
||||
void bkp_init(void) {
|
||||
/* Don't call pwr_init(), or you'll reset the device. We just
|
||||
* need the clock. */
|
||||
rcc_clk_enable(RCC_PWR);
|
||||
rcc_clk_enable(RCC_BKP);
|
||||
rcc_reset_dev(RCC_BKP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable write access to the backup registers. Backup interface must
|
||||
* be initialized for subsequent register writes to work.
|
||||
* @see bkp_init()
|
||||
*/
|
||||
void bkp_enable_writes(void) {
|
||||
*bb_perip(&PWR_BASE->CR, PWR_CR_DBP_BIT) = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable write access to the backup registers.
|
||||
*/
|
||||
void bkp_disable_writes(void) {
|
||||
*bb_perip(&PWR_BASE->CR, PWR_CR_DBP_BIT) = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a value from given backup data register.
|
||||
* @param reg Data register to read, from 1 to BKP_NR_DATA_REGS (10 on
|
||||
* medium-density devices, 42 on high-density devices).
|
||||
*/
|
||||
uint16 bkp_read(uint8 reg) {
|
||||
__io uint32* dr = data_register(reg);
|
||||
if (!dr) {
|
||||
ASSERT(0); /* nonexistent register */
|
||||
return 0;
|
||||
}
|
||||
return (uint16)*dr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write a value to given data register.
|
||||
*
|
||||
* Write access to backup registers must be enabled.
|
||||
*
|
||||
* @param reg Data register to write, from 1 to BKP_NR_DATA_REGS (10
|
||||
* on medium-density devices, 42 on high-density devices).
|
||||
* @param val Value to write into the register.
|
||||
* @see bkp_enable_writes()
|
||||
*/
|
||||
void bkp_write(uint8 reg, uint16 val) {
|
||||
__io uint32* dr = data_register(reg);
|
||||
if (!dr) {
|
||||
ASSERT(0); /* nonexistent register */
|
||||
return;
|
||||
}
|
||||
*dr = (uint32)val;
|
||||
}
|
||||
|
||||
/*
|
||||
* Data register memory layout is not contiguous. It's split up from
|
||||
* 1--NR_LOW_DRS, beginning at BKP_BASE->DR1, through to
|
||||
* (NR_LOW_DRS+1)--BKP_NR_DATA_REGS, beginning at BKP_BASE->DR11.
|
||||
*/
|
||||
#define NR_LOW_DRS 10
|
||||
|
||||
static inline __io uint32* data_register(uint8 reg) {
|
||||
if (reg < 1 || reg > BKP_NR_DATA_REGS) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if BKP_NR_DATA_REGS == NR_LOW_DRS
|
||||
return (uint32*)BKP_BASE + reg;
|
||||
#else
|
||||
if (reg <= NR_LOW_DRS) {
|
||||
return (uint32*)BKP_BASE + reg;
|
||||
} else {
|
||||
return (uint32*)&(BKP_BASE->DR11) + (reg - NR_LOW_DRS - 1);
|
||||
}
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Bryan Newbold.
|
||||
* Copyright (c) 2011, 2012 LeafLabs, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/dac.c
|
||||
* @brief Digital to analog converter support.
|
||||
*/
|
||||
|
||||
#include <libmaple/dac.h>
|
||||
#include <libmaple/libmaple.h>
|
||||
#include <libmaple/gpio.h>
|
||||
|
||||
#if STM32_HAVE_DAC
|
||||
dac_dev dac = {
|
||||
.regs = DAC_BASE,
|
||||
};
|
||||
const dac_dev *DAC = &dac;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Initialize the digital to analog converter
|
||||
* @param dev DAC device
|
||||
* @param flags Flags:
|
||||
* DAC_CH1: Enable channel 1
|
||||
* DAC_CH2: Enable channel 2
|
||||
* @sideeffect May set PA4 or PA5 to INPUT_ANALOG
|
||||
*/
|
||||
void dac_init(const dac_dev *dev, uint32 flags) {
|
||||
/* First turn on the clock */
|
||||
rcc_clk_enable(RCC_DAC);
|
||||
rcc_reset_dev(RCC_DAC);
|
||||
|
||||
if (flags & DAC_CH1) {
|
||||
dac_enable_channel(dev, 1);
|
||||
}
|
||||
|
||||
if (flags & DAC_CH2) {
|
||||
dac_enable_channel(dev, 2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write a 12-bit value to the DAC to output
|
||||
* @param dev DAC device
|
||||
* @param channel channel to select (1 or 2)
|
||||
* @param val value to write
|
||||
*/
|
||||
void dac_write_channel(const dac_dev *dev, uint8 channel, uint16 val) {
|
||||
switch(channel) {
|
||||
case 1:
|
||||
dev->regs->DHR12R1 = DAC_DHR12R1_DACC1DHR & val;
|
||||
break;
|
||||
case 2:
|
||||
dev->regs->DHR12R2 = DAC_DHR12R2_DACC2DHR & val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable a DAC channel
|
||||
* @param dev DAC device
|
||||
* @param channel channel to enable, either 1 or 2
|
||||
* @sideeffect May change pin mode of PA4 or PA5
|
||||
*/
|
||||
void dac_enable_channel(const dac_dev *dev, uint8 channel) {
|
||||
/*
|
||||
* Setup ANALOG mode on PA4 and PA5. This mapping is consistent
|
||||
* across all supported STM32s with a DAC.
|
||||
*/
|
||||
switch (channel) {
|
||||
case 1:
|
||||
gpio_set_mode(GPIOA, 4, GPIO_MODE_ANALOG);
|
||||
dev->regs->CR |= DAC_CR_EN1;
|
||||
break;
|
||||
case 2:
|
||||
gpio_set_mode(GPIOA, 5, GPIO_MODE_ANALOG);
|
||||
dev->regs->CR |= DAC_CR_EN2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disable a DAC channel
|
||||
* @param dev DAC device
|
||||
* @param channel channel to disable, either 1 or 2
|
||||
*/
|
||||
void dac_disable_channel(const dac_dev *dev, uint8 channel) {
|
||||
switch (channel) {
|
||||
case 1:
|
||||
dev->regs->CR &= ~DAC_CR_EN1;
|
||||
break;
|
||||
case 2:
|
||||
dev->regs->CR &= ~DAC_CR_EN2;
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Michael Hope.
|
||||
* Copyright (c) 2012 LeafLabs, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/dma.c
|
||||
* @author Marti Bolivar <mbolivar@leaflabs.com>;
|
||||
* Original implementation by Michael Hope
|
||||
* @brief Portable DMA routines.
|
||||
*/
|
||||
|
||||
#include <libmaple/dma.h>
|
||||
#include "dma_private.h"
|
||||
#include "stm32_private.h"
|
||||
|
||||
/*
|
||||
* Convenience routines
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Initialize a DMA device.
|
||||
* @param dev Device to initialize.
|
||||
*/
|
||||
void dma_init(dma_dev *dev) {
|
||||
rcc_clk_enable(dev->clk_id);
|
||||
}
|
||||
|
||||
/*
|
||||
* Private API
|
||||
*/
|
||||
|
||||
enum dma_atype _dma_addr_type(__io void *addr) {
|
||||
switch (stm32_block_purpose((void*)addr)) {
|
||||
/* Notice we're treating the code block as memory here. That's
|
||||
* correct for addresses in Flash and in [0x0, 0x7FFFFFF]
|
||||
* (provided that those addresses are aliased to Flash, SRAM, or
|
||||
* FSMC, depending on BOOT[01] and possibly SYSCFG_MEMRMP). It's
|
||||
* not correct for other addresses in the code block, but those
|
||||
* will (hopefully) just fail-fast with transfer or bus errors. If
|
||||
* lots of people get confused, it might be worth being more
|
||||
* careful here. */
|
||||
case STM32_BLOCK_CODE: /* Fall through */
|
||||
case STM32_BLOCK_SRAM: /* ... */
|
||||
case STM32_BLOCK_FSMC_1_2: /* ... */
|
||||
case STM32_BLOCK_FSMC_3_4:
|
||||
return DMA_ATYPE_MEM;
|
||||
case STM32_BLOCK_PERIPH:
|
||||
return DMA_ATYPE_PER;
|
||||
case STM32_BLOCK_FSMC_REG: /* Fall through */
|
||||
/* Is this right? I can't think of a reason to DMA into or out
|
||||
* of the FSMC registers. [mbolivar] */
|
||||
case STM32_BLOCK_UNUSED: /* ... */
|
||||
case STM32_BLOCK_CORTEX_INTERNAL: /* ... */
|
||||
return DMA_ATYPE_OTHER;
|
||||
default:
|
||||
ASSERT(0); /* Can't happen */
|
||||
return DMA_ATYPE_OTHER;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,413 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Michael Hope.
|
||||
* Copyright (c) 2012 LeafLabs, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/stm32f1/dma.c
|
||||
* @author Marti Bolivar <mbolivar@leaflabs.com>;
|
||||
* Original implementation by Michael Hope
|
||||
* @brief STM32F1 DMA support.
|
||||
*/
|
||||
|
||||
#include <libmaple/dma.h>
|
||||
#include <libmaple/bitband.h>
|
||||
|
||||
/* Hack to ensure inlining in dma_irq_handler() */
|
||||
#define DMA_GET_HANDLER(dev, tube) (dev->handlers[tube - 1].handler)
|
||||
#include "dma_private.h"
|
||||
|
||||
/*
|
||||
* Devices
|
||||
*/
|
||||
|
||||
static dma_dev dma1 = {
|
||||
.regs = DMA1_BASE,
|
||||
.clk_id = RCC_DMA1,
|
||||
.handlers = {{ .handler = NULL, .irq_line = NVIC_DMA_CH1 },
|
||||
{ .handler = NULL, .irq_line = NVIC_DMA_CH2 },
|
||||
{ .handler = NULL, .irq_line = NVIC_DMA_CH3 },
|
||||
{ .handler = NULL, .irq_line = NVIC_DMA_CH4 },
|
||||
{ .handler = NULL, .irq_line = NVIC_DMA_CH5 },
|
||||
{ .handler = NULL, .irq_line = NVIC_DMA_CH6 },
|
||||
{ .handler = NULL, .irq_line = NVIC_DMA_CH7 }},
|
||||
};
|
||||
/** STM32F1 DMA1 device */
|
||||
dma_dev *DMA1 = &dma1;
|
||||
|
||||
#if defined(STM32_HIGH_DENSITY) || defined(STM32_XL_DENSITY)
|
||||
static dma_dev dma2 = {
|
||||
.regs = DMA2_BASE,
|
||||
.clk_id = RCC_DMA2,
|
||||
.handlers = {{ .handler = NULL, .irq_line = NVIC_DMA2_CH1 },
|
||||
{ .handler = NULL, .irq_line = NVIC_DMA2_CH2 },
|
||||
{ .handler = NULL, .irq_line = NVIC_DMA2_CH3 },
|
||||
{ .handler = NULL, .irq_line = NVIC_DMA2_CH_4_5 },
|
||||
{ .handler = NULL, .irq_line = NVIC_DMA2_CH_4_5 }}, /* !@#$ */
|
||||
};
|
||||
/** STM32F1 DMA2 device */
|
||||
dma_dev *DMA2 = &dma2;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Auxiliary routines
|
||||
*/
|
||||
|
||||
/* Can channel serve cfg->tube_req_src? */
|
||||
static int cfg_req_ok(dma_channel channel, dma_tube_config *cfg) {
|
||||
return (cfg->tube_req_src & 0x7) == channel;
|
||||
}
|
||||
|
||||
/* Can dev serve cfg->tube_req_src? */
|
||||
static int cfg_dev_ok(dma_dev *dev, dma_tube_config *cfg) {
|
||||
return (rcc_clk_id)(cfg->tube_req_src >> 3) == dev->clk_id;
|
||||
}
|
||||
|
||||
/* Is addr acceptable for use as DMA src/dst? */
|
||||
static int cfg_mem_ok(__io void *addr) {
|
||||
enum dma_atype atype = _dma_addr_type(addr);
|
||||
return atype == DMA_ATYPE_MEM || atype == DMA_ATYPE_PER;
|
||||
}
|
||||
|
||||
/* Is the direction implied by src->dst supported? */
|
||||
static int cfg_dir_ok(dma_tube_config *cfg) {
|
||||
/* We can't do peripheral->peripheral transfers. */
|
||||
return ((_dma_addr_type(cfg->tube_src) == DMA_ATYPE_MEM) ||
|
||||
(_dma_addr_type(cfg->tube_dst) == DMA_ATYPE_MEM));
|
||||
}
|
||||
|
||||
static int preconfig_check(dma_dev *dev, dma_channel channel,
|
||||
dma_tube_config *cfg) {
|
||||
if (!cfg_req_ok(channel, cfg)) {
|
||||
return -DMA_TUBE_CFG_EREQ;
|
||||
}
|
||||
if (cfg->tube_nr_xfers > 65535) {
|
||||
return -DMA_TUBE_CFG_ENDATA;
|
||||
}
|
||||
if (!cfg_dev_ok(dev, cfg)) {
|
||||
return -DMA_TUBE_CFG_EDEV;
|
||||
}
|
||||
if (!cfg_mem_ok(cfg->tube_src)) {
|
||||
return -DMA_TUBE_CFG_ESRC;
|
||||
}
|
||||
if (!cfg_mem_ok(cfg->tube_dst)) {
|
||||
return -DMA_TUBE_CFG_EDST;
|
||||
}
|
||||
if (!cfg_dir_ok(cfg)) {
|
||||
return -DMA_TUBE_CFG_EDIR;
|
||||
}
|
||||
return DMA_TUBE_CFG_SUCCESS;
|
||||
}
|
||||
|
||||
static inline void set_ccr(dma_tube_reg_map *chregs,
|
||||
dma_xfer_size msize, int minc,
|
||||
dma_xfer_size psize, int pinc,
|
||||
uint32 other_flags) {
|
||||
chregs->CCR = ((msize << 10) | (psize << 8) |
|
||||
(minc ? DMA_CCR_MINC : 0) | (pinc ? DMA_CCR_PINC : 0) |
|
||||
other_flags);
|
||||
}
|
||||
|
||||
static inline uint32 cfg_ccr_flags(unsigned tube_flags) {
|
||||
/* DMA_CFG_SRC_INC and DMA_CFG_DST_INC are special */
|
||||
return tube_flags & ~(DMA_CFG_SRC_INC | DMA_CFG_DST_INC);
|
||||
}
|
||||
|
||||
/* Configure chregs according to cfg, where cfg->tube_dst is peripheral. */
|
||||
static int config_to_per(dma_tube_reg_map *chregs, dma_tube_config *cfg) {
|
||||
/* Check that ->tube_src is memory (if it's anything else, we
|
||||
* shouldn't have been called). */
|
||||
ASSERT(_dma_addr_type(cfg->tube_src) == DMA_ATYPE_MEM);
|
||||
|
||||
set_ccr(chregs,
|
||||
cfg->tube_src_size, cfg->tube_flags & DMA_CFG_SRC_INC,
|
||||
cfg->tube_dst_size, cfg->tube_flags & DMA_CFG_DST_INC,
|
||||
(cfg_ccr_flags(cfg->tube_flags) | DMA_CCR_DIR_FROM_MEM));
|
||||
chregs->CNDTR = cfg->tube_nr_xfers;
|
||||
chregs->CMAR = (uint32)cfg->tube_src;
|
||||
chregs->CPAR = (uint32)cfg->tube_dst;
|
||||
return DMA_TUBE_CFG_SUCCESS;
|
||||
}
|
||||
|
||||
/* Configure chregs according to cfg, where cfg->tube_dst is memory. */
|
||||
static int config_to_mem(dma_tube_reg_map *chregs, dma_tube_config *cfg) {
|
||||
uint32 mem2mem;
|
||||
|
||||
if ((_dma_addr_type(cfg->tube_src) == DMA_ATYPE_MEM) &&
|
||||
(cfg->tube_flags & DMA_CFG_CIRC)) {
|
||||
/* Can't do mem-to-mem and circular mode */
|
||||
return -DMA_TUBE_CFG_ECFG;
|
||||
}
|
||||
|
||||
mem2mem = (_dma_addr_type(cfg->tube_src) == DMA_ATYPE_MEM ?
|
||||
DMA_CCR_MEM2MEM : 0);
|
||||
set_ccr(chregs,
|
||||
cfg->tube_dst_size, cfg->tube_flags & DMA_CFG_DST_INC,
|
||||
cfg->tube_src_size, cfg->tube_flags & DMA_CFG_SRC_INC,
|
||||
(cfg_ccr_flags(cfg->tube_flags) |
|
||||
DMA_CCR_DIR_FROM_PER |
|
||||
mem2mem));
|
||||
chregs->CNDTR = cfg->tube_nr_xfers;
|
||||
chregs->CMAR = (uint32)cfg->tube_dst;
|
||||
chregs->CPAR = (uint32)cfg->tube_src;
|
||||
return DMA_TUBE_CFG_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Routines
|
||||
*/
|
||||
|
||||
int dma_tube_cfg(dma_dev *dev, dma_channel channel, dma_tube_config *cfg) {
|
||||
dma_tube_reg_map *chregs;
|
||||
int ret = preconfig_check(dev, channel, cfg);
|
||||
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
dma_disable(dev, channel); /* Must disable before reconfiguring */
|
||||
dma_clear_isr_bits(dev, channel); /* For sanity and consistency
|
||||
* with STM32F2. */
|
||||
|
||||
chregs = dma_tube_regs(dev, channel);
|
||||
switch (_dma_addr_type(cfg->tube_dst)) {
|
||||
case DMA_ATYPE_PER:
|
||||
ret = config_to_per(chregs, cfg);
|
||||
break;
|
||||
case DMA_ATYPE_MEM:
|
||||
ret = config_to_mem(chregs, cfg);
|
||||
break;
|
||||
default:
|
||||
/* Can't happen */
|
||||
ASSERT(0);
|
||||
return -DMA_TUBE_CFG_ECFG;
|
||||
}
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
chregs->CNDTR = cfg->tube_nr_xfers;
|
||||
return DMA_TUBE_CFG_SUCCESS;
|
||||
}
|
||||
|
||||
void dma_set_priority(dma_dev *dev,
|
||||
dma_channel channel,
|
||||
dma_priority priority) {
|
||||
dma_channel_reg_map *channel_regs;
|
||||
uint32 ccr;
|
||||
|
||||
ASSERT_FAULT(!dma_is_channel_enabled(dev, channel));
|
||||
|
||||
channel_regs = dma_channel_regs(dev, channel);
|
||||
ccr = channel_regs->CCR;
|
||||
ccr &= ~DMA_CCR_PL;
|
||||
ccr |= (priority << 12);
|
||||
channel_regs->CCR = ccr;
|
||||
}
|
||||
|
||||
void dma_set_num_transfers(dma_dev *dev,
|
||||
dma_channel channel,
|
||||
uint16 num_transfers) {
|
||||
dma_channel_reg_map *channel_regs;
|
||||
|
||||
ASSERT_FAULT(!dma_is_channel_enabled(dev, channel));
|
||||
|
||||
channel_regs = dma_channel_regs(dev, channel);
|
||||
channel_regs->CNDTR = num_transfers;
|
||||
}
|
||||
|
||||
void dma_attach_interrupt(dma_dev *dev, dma_channel channel,
|
||||
void (*handler)(void)) {
|
||||
DMA_GET_HANDLER(dev, channel) = handler;
|
||||
nvic_irq_enable(dev->handlers[channel - 1].irq_line);
|
||||
}
|
||||
|
||||
void dma_detach_interrupt(dma_dev *dev, dma_channel channel) {
|
||||
/* Don't use nvic_irq_disable()! Think about DMA2 channels 4 and 5. */
|
||||
dma_channel_regs(dev, channel)->CCR &= ~0xF;
|
||||
DMA_GET_HANDLER(dev, channel) = NULL;
|
||||
}
|
||||
|
||||
void dma_enable(dma_dev *dev, dma_channel channel) {
|
||||
dma_channel_reg_map *chan_regs = dma_channel_regs(dev, channel);
|
||||
bb_peri_set_bit(&chan_regs->CCR, DMA_CCR_EN_BIT, 1);
|
||||
}
|
||||
|
||||
void dma_disable(dma_dev *dev, dma_channel channel) {
|
||||
dma_channel_reg_map *chan_regs = dma_channel_regs(dev, channel);
|
||||
bb_peri_set_bit(&chan_regs->CCR, DMA_CCR_EN_BIT, 0);
|
||||
}
|
||||
|
||||
dma_irq_cause dma_get_irq_cause(dma_dev *dev, dma_channel channel) {
|
||||
/* Grab and clear the ISR bits. */
|
||||
uint8 status_bits = dma_get_isr_bits(dev, channel);
|
||||
dma_clear_isr_bits(dev, channel);
|
||||
|
||||
/* If the channel global interrupt flag is cleared, then
|
||||
* something's very wrong. */
|
||||
ASSERT(status_bits & 0x1);
|
||||
/* If GIF is set, then some other flag should be set, barring
|
||||
* something unexpected (e.g. the user making an unforeseen IFCR
|
||||
* write). */
|
||||
ASSERT(status_bits != 0x1);
|
||||
|
||||
/* ISR flags get set even if the corresponding interrupt enable
|
||||
* bits in the channel's configuration register are cleared, so we
|
||||
* can't use a switch here.
|
||||
*
|
||||
* Don't change the order of these if statements. */
|
||||
if (status_bits & 0x8) {
|
||||
return DMA_TRANSFER_ERROR;
|
||||
} else if (status_bits & 0x2) {
|
||||
return DMA_TRANSFER_COMPLETE;
|
||||
} else if (status_bits & 0x4) {
|
||||
return DMA_TRANSFER_HALF_COMPLETE;
|
||||
}
|
||||
|
||||
/* If we get here, one of our assumptions has been violated, but
|
||||
* the debug level is too low for the above ASSERTs() to have had
|
||||
* any effect. In order to fail fast, mimic the DMA controller's
|
||||
* behavior when an error occurs. */
|
||||
dma_disable(dev, channel);
|
||||
return DMA_TRANSFER_ERROR;
|
||||
}
|
||||
|
||||
void dma_set_mem_addr(dma_dev *dev, dma_channel channel, __io void *addr) {
|
||||
dma_channel_reg_map *chan_regs;
|
||||
|
||||
ASSERT_FAULT(!dma_is_channel_enabled(dev, channel));
|
||||
|
||||
chan_regs = dma_channel_regs(dev, channel);
|
||||
chan_regs->CMAR = (uint32)addr;
|
||||
}
|
||||
|
||||
void dma_set_per_addr(dma_dev *dev, dma_channel channel, __io void *addr) {
|
||||
dma_channel_reg_map *chan_regs;
|
||||
|
||||
ASSERT_FAULT(!dma_is_channel_enabled(dev, channel));
|
||||
|
||||
chan_regs = dma_channel_regs(dev, channel);
|
||||
chan_regs->CPAR = (uint32)addr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deprecated. Use dma_tube_cfg() instead.
|
||||
*
|
||||
* Set up a DMA transfer.
|
||||
*
|
||||
* The channel will be disabled before being reconfigured. The
|
||||
* transfer will have low priority by default. You may choose another
|
||||
* priority before the transfer begins using dma_set_priority(), as
|
||||
* well as performing any other configuration you desire. When the
|
||||
* channel is configured to your liking, enable it using dma_enable().
|
||||
*
|
||||
* @param dev DMA device.
|
||||
* @param channel DMA channel.
|
||||
* @param peripheral_address Base address of peripheral data register
|
||||
* involved in the transfer.
|
||||
* @param peripheral_size Peripheral data transfer size.
|
||||
* @param memory_address Base memory address involved in the transfer.
|
||||
* @param memory_size Memory data transfer size.
|
||||
* @param mode Logical OR of dma_mode_flags
|
||||
*
|
||||
* @see dma_tube_cfg()
|
||||
*
|
||||
* @sideeffect Disables the given DMA channel.
|
||||
* @see dma_xfer_size
|
||||
* @see dma_mode_flags
|
||||
* @see dma_set_num_transfers()
|
||||
* @see dma_set_priority()
|
||||
* @see dma_attach_interrupt()
|
||||
* @see dma_enable()
|
||||
*/
|
||||
__deprecated
|
||||
void dma_setup_transfer(dma_dev *dev,
|
||||
dma_channel channel,
|
||||
__io void *peripheral_address,
|
||||
dma_xfer_size peripheral_size,
|
||||
__io void *memory_address,
|
||||
dma_xfer_size memory_size,
|
||||
uint32 mode) {
|
||||
dma_channel_reg_map *channel_regs = dma_channel_regs(dev, channel);
|
||||
|
||||
dma_disable(dev, channel); /* can't write to CMAR/CPAR otherwise */
|
||||
channel_regs->CCR = (memory_size << 10) | (peripheral_size << 8) | mode;
|
||||
channel_regs->CMAR = (uint32)memory_address;
|
||||
channel_regs->CPAR = (uint32)peripheral_address;
|
||||
}
|
||||
|
||||
/*
|
||||
* IRQ handlers
|
||||
*/
|
||||
|
||||
void __irq_dma1_channel1(void) {
|
||||
dma_irq_handler(DMA1, DMA_CH1);
|
||||
}
|
||||
|
||||
void __irq_dma1_channel2(void) {
|
||||
dma_irq_handler(DMA1, DMA_CH2);
|
||||
}
|
||||
|
||||
void __irq_dma1_channel3(void) {
|
||||
dma_irq_handler(DMA1, DMA_CH3);
|
||||
}
|
||||
|
||||
void __irq_dma1_channel4(void) {
|
||||
dma_irq_handler(DMA1, DMA_CH4);
|
||||
}
|
||||
|
||||
void __irq_dma1_channel5(void) {
|
||||
dma_irq_handler(DMA1, DMA_CH5);
|
||||
}
|
||||
|
||||
void __irq_dma1_channel6(void) {
|
||||
dma_irq_handler(DMA1, DMA_CH6);
|
||||
}
|
||||
|
||||
void __irq_dma1_channel7(void) {
|
||||
dma_irq_handler(DMA1, DMA_CH7);
|
||||
}
|
||||
|
||||
#if defined(STM32_HIGH_DENSITY) || defined(STM32_XL_DENSITY)
|
||||
void __irq_dma2_channel1(void) {
|
||||
dma_irq_handler(DMA2, DMA_CH1);
|
||||
}
|
||||
|
||||
void __irq_dma2_channel2(void) {
|
||||
dma_irq_handler(DMA2, DMA_CH2);
|
||||
}
|
||||
|
||||
void __irq_dma2_channel3(void) {
|
||||
dma_irq_handler(DMA2, DMA_CH3);
|
||||
}
|
||||
|
||||
void __irq_dma2_channel4_5(void) {
|
||||
if ((DMA2_BASE->CCR4 & DMA_CCR_EN) && (DMA2_BASE->ISR & DMA_ISR_GIF4)) {
|
||||
dma_irq_handler(DMA2, DMA_CH4);
|
||||
}
|
||||
if ((DMA2_BASE->CCR5 & DMA_CCR_EN) && (DMA2_BASE->ISR & DMA_ISR_GIF5)) {
|
||||
dma_irq_handler(DMA2, DMA_CH5);
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,101 @@
|
|||
/* *****************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Perry Hung.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
* ****************************************************************************/
|
||||
|
||||
# On an exception, push a fake stack thread mode stack frame and redirect
|
||||
# thread execution to a thread mode error handler
|
||||
|
||||
# From RM008:
|
||||
# The SP is decremented by eight words by the completion of the stack push.
|
||||
# Figure 5-1 shows the contents of the stack after an exception pre-empts the
|
||||
# current program flow.
|
||||
#
|
||||
# Old SP--> <previous>
|
||||
# xPSR
|
||||
# PC
|
||||
# LR
|
||||
# r12
|
||||
# r3
|
||||
# r2
|
||||
# r1
|
||||
# SP--> r0
|
||||
|
||||
.text
|
||||
.globl __exc_hardfault
|
||||
.globl __exc_nmi
|
||||
.globl __exc_hardfault
|
||||
.globl __exc_memmanage
|
||||
.globl __exc_busfault
|
||||
.globl __exc_usagefault
|
||||
|
||||
.code 16
|
||||
.thumb_func
|
||||
__exc_nmi:
|
||||
mov r0, #1
|
||||
b __default_exc
|
||||
|
||||
.thumb_func
|
||||
__exc_hardfault:
|
||||
mov r0, #2
|
||||
b __default_exc
|
||||
|
||||
.thumb_func
|
||||
__exc_memmanage:
|
||||
mov r0, #3
|
||||
b __default_exc
|
||||
|
||||
.thumb_func
|
||||
__exc_busfault:
|
||||
mov r0, #4
|
||||
b __default_exc
|
||||
|
||||
.thumb_func
|
||||
__exc_usagefault:
|
||||
mov r0, #5
|
||||
b __default_exc
|
||||
|
||||
.thumb_func
|
||||
__default_exc:
|
||||
ldr r2, NVIC_CCR @ Enable returning to thread mode even if there are
|
||||
mov r1 ,#1 @ pending exceptions. See flag NONEBASETHRDENA.
|
||||
str r1, [r2]
|
||||
cpsid i @ Disable global interrupts
|
||||
ldr r2, SYSTICK_CSR @ Disable systick handler
|
||||
mov r1, #0
|
||||
str r1, [r2]
|
||||
ldr r1, CPSR_MASK @ Set default CPSR
|
||||
push {r1}
|
||||
ldr r1, TARGET_PC @ Set target pc
|
||||
push {r1}
|
||||
sub sp, sp, #24 @ Don't care
|
||||
ldr r1, EXC_RETURN @ Return to thread mode
|
||||
mov lr, r1
|
||||
bx lr @ Exception exit
|
||||
|
||||
.align 4
|
||||
CPSR_MASK: .word 0x61000000
|
||||
EXC_RETURN: .word 0xFFFFFFF9
|
||||
TARGET_PC: .word __error
|
||||
NVIC_CCR: .word 0xE000ED14 @ NVIC configuration control register
|
||||
SYSTICK_CSR: .word 0xE000E010 @ Systick control register
|
||||
|
|
@ -0,0 +1,292 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Perry Hung.
|
||||
* Copyright (c) 2011, 2012 LeafLabs, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/exti.c
|
||||
* @brief External interrupt control routines
|
||||
*/
|
||||
|
||||
#include <libmaple/exti.h>
|
||||
#include <libmaple/libmaple.h>
|
||||
#include <libmaple/nvic.h>
|
||||
#include <libmaple/bitband.h>
|
||||
|
||||
static inline void dispatch_single_exti(uint32 exti_num);
|
||||
static inline void dispatch_extis(uint32 start, uint32 stop);
|
||||
|
||||
/*
|
||||
* Internal state
|
||||
*/
|
||||
|
||||
typedef struct exti_channel {
|
||||
void (*handler)(void *);
|
||||
void *arg;
|
||||
} exti_channel;
|
||||
|
||||
static exti_channel exti_channels[] = {
|
||||
{ .handler = NULL, .arg = NULL }, // EXTI0
|
||||
{ .handler = NULL, .arg = NULL }, // EXTI1
|
||||
{ .handler = NULL, .arg = NULL }, // EXTI2
|
||||
{ .handler = NULL, .arg = NULL }, // EXTI3
|
||||
{ .handler = NULL, .arg = NULL }, // EXTI4
|
||||
{ .handler = NULL, .arg = NULL }, // EXTI5
|
||||
{ .handler = NULL, .arg = NULL }, // EXTI6
|
||||
{ .handler = NULL, .arg = NULL }, // EXTI7
|
||||
{ .handler = NULL, .arg = NULL }, // EXTI8
|
||||
{ .handler = NULL, .arg = NULL }, // EXTI9
|
||||
{ .handler = NULL, .arg = NULL }, // EXTI10
|
||||
{ .handler = NULL, .arg = NULL }, // EXTI11
|
||||
{ .handler = NULL, .arg = NULL }, // EXTI12
|
||||
{ .handler = NULL, .arg = NULL }, // EXTI13
|
||||
{ .handler = NULL, .arg = NULL }, // EXTI14
|
||||
{ .handler = NULL, .arg = NULL }, // EXTI15
|
||||
};
|
||||
|
||||
/*
|
||||
* Portable routines
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Register a handler to run upon external interrupt.
|
||||
*
|
||||
* This function assumes that the interrupt request corresponding to
|
||||
* the given external interrupt is masked.
|
||||
*
|
||||
* @param num External interrupt line number.
|
||||
* @param port Port to use as source input for external interrupt.
|
||||
* @param handler Function handler to execute when interrupt is triggered.
|
||||
* @param mode Type of transition to trigger on, one of:
|
||||
* EXTI_RISING, EXTI_FALLING, EXTI_RISING_FALLING.
|
||||
* @see exti_num
|
||||
* @see exti_cfg
|
||||
* @see voidFuncPtr
|
||||
* @see exti_trigger_mode
|
||||
*/
|
||||
void exti_attach_interrupt(exti_num num,
|
||||
exti_cfg port,
|
||||
voidFuncPtr handler,
|
||||
exti_trigger_mode mode) {
|
||||
// Call callback version with arg being null
|
||||
exti_attach_callback(num, port, (voidArgumentFuncPtr)handler, NULL, mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Register a handler with an argument to run upon external interrupt.
|
||||
*
|
||||
* This function assumes that the interrupt request corresponding to
|
||||
* the given external interrupt is masked.
|
||||
*
|
||||
* @param num External interrupt line number.
|
||||
* @param port Port to use as source input for external interrupt.
|
||||
* @param handler Function handler to execute when interrupt is triggered.
|
||||
* @param arg Argument to pass to the interrupt handler.
|
||||
* @param mode Type of transition to trigger on, one of:
|
||||
* EXTI_RISING, EXTI_FALLING, EXTI_RISING_FALLING.
|
||||
* @see exti_num
|
||||
* @see exti_cfg
|
||||
* @see voidFuncPtr
|
||||
* @see exti_trigger_mode
|
||||
*/
|
||||
void exti_attach_callback(exti_num num,
|
||||
exti_cfg port,
|
||||
voidArgumentFuncPtr handler,
|
||||
void *arg,
|
||||
exti_trigger_mode mode) {
|
||||
ASSERT(handler);
|
||||
|
||||
/* Register the handler */
|
||||
exti_channels[num].handler = handler;
|
||||
exti_channels[num].arg = arg;
|
||||
|
||||
/* Set trigger mode */
|
||||
switch (mode) {
|
||||
case EXTI_RISING:
|
||||
bb_peri_set_bit(&EXTI_BASE->RTSR, num, 1);
|
||||
break;
|
||||
case EXTI_FALLING:
|
||||
bb_peri_set_bit(&EXTI_BASE->FTSR, num, 1);
|
||||
break;
|
||||
case EXTI_RISING_FALLING:
|
||||
bb_peri_set_bit(&EXTI_BASE->RTSR, num, 1);
|
||||
bb_peri_set_bit(&EXTI_BASE->FTSR, num, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Use the chip-specific exti_select() to map num to port */
|
||||
exti_select(num, port);
|
||||
|
||||
/* Unmask external interrupt request */
|
||||
bb_peri_set_bit(&EXTI_BASE->IMR, num, 1);
|
||||
|
||||
/* Enable the interrupt line */
|
||||
switch(num)
|
||||
{
|
||||
case EXTI0:
|
||||
nvic_irq_enable(NVIC_EXTI0);
|
||||
break;
|
||||
case EXTI1:
|
||||
nvic_irq_enable(NVIC_EXTI1);
|
||||
break;
|
||||
case EXTI2:
|
||||
nvic_irq_enable(NVIC_EXTI2);
|
||||
break;
|
||||
case EXTI3:
|
||||
nvic_irq_enable(NVIC_EXTI3);
|
||||
break;
|
||||
case EXTI4:
|
||||
nvic_irq_enable(NVIC_EXTI4);
|
||||
break;
|
||||
case EXTI5:
|
||||
case EXTI6:
|
||||
case EXTI7:
|
||||
case EXTI8:
|
||||
case EXTI9:
|
||||
nvic_irq_enable(NVIC_EXTI_9_5);
|
||||
break;
|
||||
case EXTI10:
|
||||
case EXTI11:
|
||||
case EXTI12:
|
||||
case EXTI13:
|
||||
case EXTI14:
|
||||
case EXTI15:
|
||||
nvic_irq_enable(NVIC_EXTI_15_10);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unregister an external interrupt handler
|
||||
* @param num External interrupt line to disable.
|
||||
* @see exti_num
|
||||
*/
|
||||
void exti_detach_interrupt(exti_num num) {
|
||||
/* First, mask the interrupt request */
|
||||
bb_peri_set_bit(&EXTI_BASE->IMR, num, 0);
|
||||
|
||||
/* Then, clear the trigger selection registers */
|
||||
bb_peri_set_bit(&EXTI_BASE->FTSR, num, 0);
|
||||
bb_peri_set_bit(&EXTI_BASE->RTSR, num, 0);
|
||||
|
||||
/* Finally, unregister the user's handler */
|
||||
exti_channels[num].handler = NULL;
|
||||
exti_channels[num].arg = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Private routines
|
||||
*/
|
||||
|
||||
void exti_do_select(__io uint32 *exti_cr, exti_num num, exti_cfg port) {
|
||||
uint32 shift = 4 * (num % 4);
|
||||
uint32 cr = *exti_cr;
|
||||
cr &= ~(0xF << shift);
|
||||
cr |= port << shift;
|
||||
*exti_cr = cr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Interrupt handlers
|
||||
*/
|
||||
|
||||
void __irq_exti0(void) {
|
||||
dispatch_single_exti(EXTI0);
|
||||
}
|
||||
|
||||
void __irq_exti1(void) {
|
||||
dispatch_single_exti(EXTI1);
|
||||
}
|
||||
|
||||
void __irq_exti2(void) {
|
||||
dispatch_single_exti(EXTI2);
|
||||
}
|
||||
|
||||
void __irq_exti3(void) {
|
||||
dispatch_single_exti(EXTI3);
|
||||
}
|
||||
|
||||
void __irq_exti4(void) {
|
||||
dispatch_single_exti(EXTI4);
|
||||
}
|
||||
|
||||
void __irq_exti9_5(void) {
|
||||
dispatch_extis(5, 9);
|
||||
}
|
||||
|
||||
void __irq_exti15_10(void) {
|
||||
dispatch_extis(10, 15);
|
||||
}
|
||||
|
||||
/*
|
||||
* Auxiliary functions
|
||||
*/
|
||||
|
||||
/* Clear the pending bits for EXTIs whose bits are set in exti_msk.
|
||||
*
|
||||
* If a pending bit is cleared as the last instruction in an ISR, it
|
||||
* won't actually be cleared in time and the ISR will fire again. To
|
||||
* compensate, this function NOPs for 2 cycles after clearing the
|
||||
* pending bits to ensure it takes effect. */
|
||||
static __always_inline void clear_pending_msk(uint32 exti_msk) {
|
||||
EXTI_BASE->PR = exti_msk;
|
||||
asm volatile("nop");
|
||||
asm volatile("nop");
|
||||
}
|
||||
|
||||
/* This dispatch routine is for non-multiplexed EXTI lines only; i.e.,
|
||||
* it doesn't check EXTI_PR. */
|
||||
static __always_inline void dispatch_single_exti(uint32 exti) {
|
||||
voidArgumentFuncPtr handler = exti_channels[exti].handler;
|
||||
|
||||
if (!handler) {
|
||||
return;
|
||||
}
|
||||
|
||||
handler(exti_channels[exti].arg);
|
||||
clear_pending_msk(1U << exti);
|
||||
}
|
||||
|
||||
/* Dispatch routine for EXTIs which share an IRQ. */
|
||||
static __always_inline void dispatch_extis(uint32 start, uint32 stop) {
|
||||
uint32 pr = EXTI_BASE->PR;
|
||||
uint32 handled_msk = 0;
|
||||
uint32 exti;
|
||||
|
||||
/* Dispatch user handlers for pending EXTIs. */
|
||||
for (exti = start; exti <= stop; exti++) {
|
||||
uint32 eb = (1U << exti);
|
||||
if (pr & eb) {
|
||||
voidArgumentFuncPtr handler = exti_channels[exti].handler;
|
||||
if (handler) {
|
||||
handler(exti_channels[exti].arg);
|
||||
handled_msk |= eb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Clear the pending bits for handled EXTIs. */
|
||||
clear_pending_msk(handled_msk);
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2012 LeafLabs, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
#include <libmaple/gpio.h>
|
||||
#include "exti_private.h"
|
||||
|
||||
void exti_select(exti_num num, exti_cfg port) {
|
||||
exti_do_select(&AFIO_BASE->EXTICR1 + num / 4, num, port);
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Perry Hung.
|
||||
* Copyright (c) 2011, 2012 LeafLabs, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/flash.c
|
||||
* @brief Flash management functions
|
||||
*/
|
||||
|
||||
#include <libmaple/libmaple_types.h>
|
||||
#include <libmaple/flash.h>
|
||||
|
||||
/**
|
||||
* @brief Set flash wait states
|
||||
*
|
||||
* Note that not all wait states are available on every MCU. See the
|
||||
* Flash programming manual for your MCU for restrictions on the
|
||||
* allowed value of wait_states for a given system clock (SYSCLK)
|
||||
* frequency.
|
||||
*
|
||||
* @param wait_states number of wait states (one of
|
||||
* FLASH_WAIT_STATE_0, FLASH_WAIT_STATE_1,
|
||||
* ..., FLASH_WAIT_STATE_7).
|
||||
*/
|
||||
void flash_set_latency(uint32 wait_states) {
|
||||
uint32 val = FLASH_BASE->ACR;
|
||||
|
||||
val &= ~FLASH_ACR_LATENCY;
|
||||
val |= wait_states;
|
||||
|
||||
FLASH_BASE->ACR = val;
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2012 LeafLabs, LLC.
|
||||
* Copyright (c) 2010 Bryan Newbold.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/stm32f1/fsmc.c
|
||||
* @author Marti Bolivar <mbolivar@leaflabs.com>,
|
||||
* Bryan Newbold <bnewbold@robocracy.org>
|
||||
* @brief STM32F1 FSMC support.
|
||||
*/
|
||||
|
||||
#include <libmaple/stm32.h>
|
||||
|
||||
#if STM32_HAVE_FSMC /* Don't try building the rest for MCUs without FSMC */
|
||||
|
||||
#include <libmaple/fsmc.h>
|
||||
#include <libmaple/gpio.h>
|
||||
|
||||
void fsmc_sram_init_gpios(void) {
|
||||
/* Data lines... */
|
||||
gpio_set_mode(GPIOD, 0, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOD, 1, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOD, 8, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOD, 9, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOD, 10, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOD, 14, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOD, 15, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOE, 7, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOE, 8, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOE, 9, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOE, 10, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOE, 11, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOE, 12, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOE, 13, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOE, 14, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOE, 15, GPIO_AF_OUTPUT_PP);
|
||||
|
||||
/* Address lines... */
|
||||
gpio_set_mode(GPIOD, 11, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOD, 12, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOD, 13, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOF, 0, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOF, 1, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOF, 2, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOF, 3, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOF, 4, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOF, 5, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOF, 12, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOF, 13, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOF, 14, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOF, 15, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOG, 0, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOG, 1, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOG, 2, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOG, 3, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOG, 4, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOG, 5, GPIO_AF_OUTPUT_PP);
|
||||
|
||||
/* And control lines... */
|
||||
gpio_set_mode(GPIOD, 4, GPIO_AF_OUTPUT_PP); // NOE
|
||||
gpio_set_mode(GPIOD, 5, GPIO_AF_OUTPUT_PP); // NWE
|
||||
|
||||
gpio_set_mode(GPIOD, 7, GPIO_AF_OUTPUT_PP); // NE1
|
||||
gpio_set_mode(GPIOG, 9, GPIO_AF_OUTPUT_PP); // NE2
|
||||
gpio_set_mode(GPIOG, 10, GPIO_AF_OUTPUT_PP); // NE3
|
||||
gpio_set_mode(GPIOG, 12, GPIO_AF_OUTPUT_PP); // NE4
|
||||
|
||||
gpio_set_mode(GPIOE, 0, GPIO_AF_OUTPUT_PP); // NBL0
|
||||
gpio_set_mode(GPIOE, 1, GPIO_AF_OUTPUT_PP); // NBL1
|
||||
}
|
||||
|
||||
#endif /* STM32_HAVE_FSMC */
|
|
@ -0,0 +1,50 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Perry Hung.
|
||||
* Copyright (c) 2011, 2012 LeafLabs, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/gpio.c
|
||||
* @brief Generic STM32 GPIO support.
|
||||
*/
|
||||
|
||||
#include <libmaple/gpio.h>
|
||||
#include <libmaple/rcc.h>
|
||||
|
||||
/*
|
||||
* GPIO routines
|
||||
*/
|
||||
|
||||
/**
|
||||
* Initialize a GPIO device.
|
||||
*
|
||||
* Enables the clock for and resets the given device.
|
||||
*
|
||||
* @param dev GPIO device to initialize.
|
||||
*/
|
||||
void gpio_init(gpio_dev *dev) {
|
||||
rcc_clk_enable(dev->clk_id);
|
||||
rcc_reset_dev(dev->clk_id);
|
||||
}
|
|
@ -0,0 +1,166 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Perry Hung.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/stm32f1/gpio.c
|
||||
* @brief STM32F1 GPIO support.
|
||||
*/
|
||||
|
||||
#include <libmaple/gpio.h>
|
||||
#include <libmaple/rcc.h>
|
||||
|
||||
/*
|
||||
* GPIO devices
|
||||
*/
|
||||
|
||||
gpio_dev gpioa = {
|
||||
.regs = GPIOA_BASE,
|
||||
.clk_id = RCC_GPIOA,
|
||||
.exti_port = EXTI_PA,
|
||||
};
|
||||
/** GPIO port A device. */
|
||||
gpio_dev* const GPIOA = &gpioa;
|
||||
|
||||
gpio_dev gpiob = {
|
||||
.regs = GPIOB_BASE,
|
||||
.clk_id = RCC_GPIOB,
|
||||
.exti_port = EXTI_PB,
|
||||
};
|
||||
/** GPIO port B device. */
|
||||
gpio_dev* const GPIOB = &gpiob;
|
||||
|
||||
gpio_dev gpioc = {
|
||||
.regs = GPIOC_BASE,
|
||||
.clk_id = RCC_GPIOC,
|
||||
.exti_port = EXTI_PC,
|
||||
};
|
||||
/** GPIO port C device. */
|
||||
gpio_dev* const GPIOC = &gpioc;
|
||||
|
||||
gpio_dev gpiod = {
|
||||
.regs = GPIOD_BASE,
|
||||
.clk_id = RCC_GPIOD,
|
||||
.exti_port = EXTI_PD,
|
||||
};
|
||||
/** GPIO port D device. */
|
||||
gpio_dev* const GPIOD = &gpiod;
|
||||
|
||||
#ifdef STM32_HIGH_DENSITY
|
||||
gpio_dev gpioe = {
|
||||
.regs = GPIOE_BASE,
|
||||
.clk_id = RCC_GPIOE,
|
||||
.exti_port = EXTI_PE,
|
||||
};
|
||||
/** GPIO port E device. */
|
||||
gpio_dev* const GPIOE = &gpioe;
|
||||
|
||||
gpio_dev gpiof = {
|
||||
.regs = GPIOF_BASE,
|
||||
.clk_id = RCC_GPIOF,
|
||||
.exti_port = EXTI_PF,
|
||||
};
|
||||
/** GPIO port F device. */
|
||||
gpio_dev* const GPIOF = &gpiof;
|
||||
|
||||
gpio_dev gpiog = {
|
||||
.regs = GPIOG_BASE,
|
||||
.clk_id = RCC_GPIOG,
|
||||
.exti_port = EXTI_PG,
|
||||
};
|
||||
/** GPIO port G device. */
|
||||
gpio_dev* const GPIOG = &gpiog;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* GPIO routines
|
||||
*/
|
||||
|
||||
/**
|
||||
* Initialize and reset all available GPIO devices.
|
||||
*/
|
||||
void gpio_init_all(void) {
|
||||
gpio_init(GPIOA);
|
||||
gpio_init(GPIOB);
|
||||
gpio_init(GPIOC);
|
||||
gpio_init(GPIOD);
|
||||
#ifdef STM32_HIGH_DENSITY
|
||||
gpio_init(GPIOE);
|
||||
gpio_init(GPIOF);
|
||||
gpio_init(GPIOG);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the mode of a GPIO pin.
|
||||
*
|
||||
* @param dev GPIO device.
|
||||
* @param pin Pin on the device whose mode to set, 0--15.
|
||||
* @param mode General purpose or alternate function mode to set the pin to.
|
||||
* @see gpio_pin_mode
|
||||
*/
|
||||
void gpio_set_mode(gpio_dev *dev, uint8 pin, gpio_pin_mode mode) {
|
||||
gpio_reg_map *regs = dev->regs;
|
||||
__io uint32 *cr = ®s->CRL + (pin >> 3);
|
||||
uint32 shift = (pin & 0x7) * 4;
|
||||
uint32 tmp = *cr;
|
||||
|
||||
tmp &= ~(0xF << shift);
|
||||
tmp |= (mode == GPIO_INPUT_PU ? GPIO_INPUT_PD : mode) << shift;
|
||||
*cr = tmp;
|
||||
|
||||
if (mode == GPIO_INPUT_PD) {
|
||||
regs->ODR &= ~(1U << pin);
|
||||
} else if (mode == GPIO_INPUT_PU) {
|
||||
regs->ODR |= (1U << pin);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* AFIO
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Initialize the AFIO clock, and reset the AFIO registers.
|
||||
*/
|
||||
void afio_init(void) {
|
||||
rcc_clk_enable(RCC_AFIO);
|
||||
rcc_reset_dev(RCC_AFIO);
|
||||
}
|
||||
|
||||
#define AFIO_EXTI_SEL_MASK 0xF
|
||||
|
||||
/**
|
||||
* @brief Perform an alternate function remap.
|
||||
* @param remapping Remapping to perform.
|
||||
*/
|
||||
void afio_remap(afio_remap_peripheral remapping) {
|
||||
if (remapping & AFIO_REMAP_USE_MAPR2) {
|
||||
remapping &= ~AFIO_REMAP_USE_MAPR2;
|
||||
AFIO_BASE->MAPR2 |= remapping;
|
||||
} else {
|
||||
AFIO_BASE->MAPR |= remapping;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,509 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Perry Hung.
|
||||
* Copyright (c) 2012 LeafLabs, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/i2c.c
|
||||
* @author Perry Hung <perry@leaflabs.com>
|
||||
* @brief Inter-Integrated Circuit (I2C) support.
|
||||
*
|
||||
* Currently, only master mode is supported.
|
||||
*/
|
||||
|
||||
#include "i2c_private.h"
|
||||
|
||||
#include <libmaple/libmaple.h>
|
||||
#include <libmaple/rcc.h>
|
||||
#include <libmaple/gpio.h>
|
||||
#include <libmaple/nvic.h>
|
||||
#include <libmaple/i2c.h>
|
||||
#include <libmaple/systick.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static inline int32 wait_for_state_change(i2c_dev *dev,
|
||||
i2c_state state,
|
||||
uint32 timeout);
|
||||
static void set_ccr_trise(i2c_dev *dev, uint32 flags);
|
||||
|
||||
/**
|
||||
* @brief Fill data register with slave address
|
||||
* @param dev I2C device
|
||||
* @param addr Slave address
|
||||
* @param rw Read/write bit
|
||||
*/
|
||||
static inline void i2c_send_slave_addr(i2c_dev *dev, uint32 addr, uint32 rw) {
|
||||
dev->regs->DR = (addr << 1) | rw;
|
||||
}
|
||||
|
||||
/*
|
||||
* Simple debugging trail. Define I2C_DEBUG to turn on.
|
||||
*/
|
||||
#ifdef I2C_DEBUG
|
||||
|
||||
#define NR_CRUMBS 128
|
||||
static struct crumb crumbs[NR_CRUMBS];
|
||||
static uint32 cur_crumb = 0;
|
||||
|
||||
static inline void i2c_drop_crumb(uint32 event, uint32 arg0, uint32 arg1) {
|
||||
if (cur_crumb < NR_CRUMBS) {
|
||||
struct crumb *crumb = &crumbs[cur_crumb++];
|
||||
crumb->event = event;
|
||||
crumb->arg0 = arg0;
|
||||
crumb->arg1 = arg1;
|
||||
}
|
||||
}
|
||||
#define I2C_CRUMB(event, arg0, arg1) i2c_drop_crumb(event, arg0, arg1)
|
||||
|
||||
#else
|
||||
#define I2C_CRUMB(event, arg0, arg1)
|
||||
#endif
|
||||
|
||||
struct crumb {
|
||||
uint32 event;
|
||||
uint32 arg0;
|
||||
uint32 arg1;
|
||||
};
|
||||
|
||||
enum {
|
||||
IRQ_ENTRY = 1,
|
||||
TXE_ONLY = 2,
|
||||
TXE_BTF = 3,
|
||||
STOP_SENT = 4,
|
||||
TEST = 5,
|
||||
RX_ADDR_START = 6,
|
||||
RX_ADDR_STOP = 7,
|
||||
RXNE_ONLY = 8,
|
||||
RXNE_SENDING = 9,
|
||||
RXNE_START_SENT = 10,
|
||||
RXNE_STOP_SENT = 11,
|
||||
RXNE_DONE = 12,
|
||||
ERROR_ENTRY = 13,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Reset an I2C bus.
|
||||
*
|
||||
* Reset is accomplished by clocking out pulses until any hung slaves
|
||||
* release SDA and SCL, then generating a START condition, then a STOP
|
||||
* condition.
|
||||
*
|
||||
* @param dev I2C device
|
||||
*/
|
||||
void i2c_bus_reset(const i2c_dev *dev) {
|
||||
/* Release both lines */
|
||||
i2c_master_release_bus(dev);
|
||||
|
||||
/*
|
||||
* Make sure the bus is free by clocking it until any slaves release the
|
||||
* bus.
|
||||
*/
|
||||
while (!gpio_read_bit(sda_port(dev), dev->sda_pin)) {
|
||||
/* Wait for any clock stretching to finish */
|
||||
while (!gpio_read_bit(scl_port(dev), dev->scl_pin))
|
||||
;
|
||||
delay_us(10);
|
||||
|
||||
/* Pull low */
|
||||
gpio_write_bit(scl_port(dev), dev->scl_pin, 0);
|
||||
delay_us(10);
|
||||
|
||||
/* Release high again */
|
||||
gpio_write_bit(scl_port(dev), dev->scl_pin, 1);
|
||||
delay_us(10);
|
||||
}
|
||||
|
||||
/* Generate start then stop condition */
|
||||
gpio_write_bit(sda_port(dev), dev->sda_pin, 0);
|
||||
delay_us(10);
|
||||
gpio_write_bit(scl_port(dev), dev->scl_pin, 0);
|
||||
delay_us(10);
|
||||
gpio_write_bit(scl_port(dev), dev->scl_pin, 1);
|
||||
delay_us(10);
|
||||
gpio_write_bit(sda_port(dev), dev->sda_pin, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize an I2C device and reset its registers to their
|
||||
* default values.
|
||||
* @param dev Device to initialize.
|
||||
*/
|
||||
void i2c_init(i2c_dev *dev) {
|
||||
rcc_reset_dev(dev->clk_id);
|
||||
rcc_clk_enable(dev->clk_id);
|
||||
}
|
||||
|
||||
/* Hack for deprecated bit of STM32F1 functionality */
|
||||
#ifndef _I2C_HAVE_DEPRECATED_I2C_REMAP
|
||||
#define _i2c_handle_remap(dev, flags) ((void)0)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Initialize an I2C device as bus master
|
||||
* @param dev Device to enable
|
||||
* @param flags Bitwise or of the following I2C options:
|
||||
* I2C_FAST_MODE: 400 khz operation,
|
||||
* I2C_DUTY_16_9: 16/9 Tlow/Thigh duty cycle (only applicable for
|
||||
* fast mode),
|
||||
* I2C_BUS_RESET: Reset the bus and clock out any hung slaves on
|
||||
* initialization,
|
||||
* I2C_10BIT_ADDRESSING: Enable 10-bit addressing,
|
||||
* I2C_REMAP: (deprecated, STM32F1 only) Remap I2C1 to SCL/PB8
|
||||
* SDA/PB9.
|
||||
*/
|
||||
void i2c_master_enable(i2c_dev *dev, uint32 flags) {
|
||||
/* PE must be disabled to configure the device */
|
||||
ASSERT(!(dev->regs->CR1 & I2C_CR1_PE));
|
||||
|
||||
/* Ugh */
|
||||
_i2c_handle_remap(dev, flags);
|
||||
|
||||
/* Reset the bus. Clock out any hung slaves. */
|
||||
if (flags & I2C_BUS_RESET) {
|
||||
i2c_bus_reset(dev);
|
||||
}
|
||||
|
||||
/* Turn on clock and set GPIO modes */
|
||||
i2c_init(dev);
|
||||
i2c_config_gpios(dev);
|
||||
|
||||
/* Configure clock and rise time */
|
||||
set_ccr_trise(dev, flags);
|
||||
|
||||
/* Enable event and buffer interrupts */
|
||||
nvic_irq_enable(dev->ev_nvic_line);
|
||||
nvic_irq_enable(dev->er_nvic_line);
|
||||
i2c_enable_irq(dev, I2C_IRQ_EVENT | I2C_IRQ_BUFFER | I2C_IRQ_ERROR);
|
||||
|
||||
/* Make it go! */
|
||||
i2c_peripheral_enable(dev);
|
||||
|
||||
dev->state = I2C_STATE_IDLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Process an i2c transaction.
|
||||
*
|
||||
* Transactions are composed of one or more i2c_msg's, and may be read
|
||||
* or write tranfers. Multiple i2c_msg's will generate a repeated
|
||||
* start in between messages.
|
||||
*
|
||||
* @param dev I2C device
|
||||
* @param msgs Messages to send/receive
|
||||
* @param num Number of messages to send/receive
|
||||
* @param timeout Bus idle timeout in milliseconds before aborting the
|
||||
* transfer. 0 denotes no timeout.
|
||||
* @return 0 on success,
|
||||
* I2C_ERROR_PROTOCOL if there was a protocol error,
|
||||
* I2C_ERROR_TIMEOUT if the transfer timed out.
|
||||
*/
|
||||
int32 i2c_master_xfer(i2c_dev *dev,
|
||||
i2c_msg *msgs,
|
||||
uint16 num,
|
||||
uint32 timeout) {
|
||||
int32 rc;
|
||||
|
||||
ASSERT(dev->state == I2C_STATE_IDLE);
|
||||
|
||||
dev->msg = msgs;
|
||||
dev->msgs_left = num;
|
||||
dev->timestamp = systick_uptime();
|
||||
dev->state = I2C_STATE_BUSY;
|
||||
|
||||
i2c_enable_irq(dev, I2C_IRQ_EVENT);
|
||||
i2c_start_condition(dev);
|
||||
|
||||
rc = wait_for_state_change(dev, I2C_STATE_XFER_DONE, timeout);
|
||||
if (rc < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
dev->state = I2C_STATE_IDLE;
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Wait for an I2C event, or time out in case of error.
|
||||
* @param dev I2C device
|
||||
* @param state I2C_state state to wait for
|
||||
* @param timeout Timeout, in milliseconds
|
||||
* @return 0 if target state is reached, a negative value on error.
|
||||
*/
|
||||
static inline int32 wait_for_state_change(i2c_dev *dev,
|
||||
i2c_state state,
|
||||
uint32 timeout) {
|
||||
i2c_state tmp;
|
||||
|
||||
while (1) {
|
||||
tmp = dev->state;
|
||||
|
||||
if (tmp == I2C_STATE_ERROR) {
|
||||
return I2C_STATE_ERROR;
|
||||
}
|
||||
|
||||
if (tmp == state) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (timeout) {
|
||||
if (systick_uptime() > (dev->timestamp + timeout)) {
|
||||
/* TODO: overflow? */
|
||||
/* TODO: racy? */
|
||||
return I2C_ERROR_TIMEOUT;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Private API
|
||||
*/
|
||||
|
||||
/*
|
||||
* IRQ handler for I2C master. Handles transmission/reception.
|
||||
*/
|
||||
void _i2c_irq_handler(i2c_dev *dev) {
|
||||
/* WTFs:
|
||||
* - Where is I2C_MSG_10BIT_ADDR handled?
|
||||
*/
|
||||
i2c_msg *msg = dev->msg;
|
||||
|
||||
uint8 read = msg->flags & I2C_MSG_READ;
|
||||
|
||||
uint32 sr1 = dev->regs->SR1;
|
||||
uint32 sr2 = dev->regs->SR2;
|
||||
I2C_CRUMB(IRQ_ENTRY, sr1, sr2);
|
||||
|
||||
/*
|
||||
* Reset timeout counter
|
||||
*/
|
||||
dev->timestamp = systick_uptime();
|
||||
|
||||
/*
|
||||
* EV5: Start condition sent
|
||||
*/
|
||||
if (sr1 & I2C_SR1_SB) {
|
||||
msg->xferred = 0;
|
||||
i2c_enable_irq(dev, I2C_IRQ_BUFFER);
|
||||
|
||||
/*
|
||||
* Master receiver
|
||||
*/
|
||||
if (read) {
|
||||
i2c_enable_ack(dev);
|
||||
}
|
||||
|
||||
i2c_send_slave_addr(dev, msg->addr, read);
|
||||
sr1 = sr2 = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* EV6: Slave address sent
|
||||
*/
|
||||
if (sr1 & I2C_SR1_ADDR) {
|
||||
/*
|
||||
* Special case event EV6_1 for master receiver.
|
||||
* Generate NACK and restart/stop condition after ADDR
|
||||
* is cleared.
|
||||
*/
|
||||
if (read) {
|
||||
if (msg->length == 1) {
|
||||
i2c_disable_ack(dev);
|
||||
if (dev->msgs_left > 1) {
|
||||
i2c_start_condition(dev);
|
||||
I2C_CRUMB(RX_ADDR_START, 0, 0);
|
||||
} else {
|
||||
i2c_stop_condition(dev);
|
||||
I2C_CRUMB(RX_ADDR_STOP, 0, 0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Master transmitter: write first byte to fill shift
|
||||
* register. We should get another TXE interrupt
|
||||
* immediately to fill DR again.
|
||||
*/
|
||||
if (msg->length != 1) {
|
||||
i2c_write(dev, msg->data[msg->xferred++]);
|
||||
}
|
||||
}
|
||||
sr1 = sr2 = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* EV8: Master transmitter
|
||||
* Transmit buffer empty, but we haven't finished transmitting the last
|
||||
* byte written.
|
||||
*/
|
||||
if ((sr1 & I2C_SR1_TXE) && !(sr1 & I2C_SR1_BTF)) {
|
||||
I2C_CRUMB(TXE_ONLY, 0, 0);
|
||||
if (dev->msgs_left) {
|
||||
i2c_write(dev, msg->data[msg->xferred++]);
|
||||
if (msg->xferred == msg->length) {
|
||||
/*
|
||||
* End of this message. Turn off TXE/RXNE and wait for
|
||||
* BTF to send repeated start or stop condition.
|
||||
*/
|
||||
i2c_disable_irq(dev, I2C_IRQ_BUFFER);
|
||||
dev->msgs_left--;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* This should be impossible...
|
||||
*/
|
||||
ASSERT(0);
|
||||
}
|
||||
sr1 = sr2 = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* EV8_2: Master transmitter
|
||||
* Last byte sent, program repeated start/stop
|
||||
*/
|
||||
if ((sr1 & I2C_SR1_TXE) && (sr1 & I2C_SR1_BTF)) {
|
||||
I2C_CRUMB(TXE_BTF, 0, 0);
|
||||
if (dev->msgs_left) {
|
||||
I2C_CRUMB(TEST, 0, 0);
|
||||
/*
|
||||
* Repeated start insanity: We can't disable ITEVTEN or else SB
|
||||
* won't interrupt, but if we don't disable ITEVTEN, BTF will
|
||||
* continually interrupt us. What the fuck ST?
|
||||
*/
|
||||
i2c_start_condition(dev);
|
||||
while (!(dev->regs->SR1 & I2C_SR1_SB))
|
||||
;
|
||||
dev->msg++;
|
||||
} else {
|
||||
i2c_stop_condition(dev);
|
||||
|
||||
/*
|
||||
* Turn off event interrupts to keep BTF from firing until
|
||||
* the end of the stop condition. Why on earth they didn't
|
||||
* have a start/stop condition request clear BTF is beyond
|
||||
* me.
|
||||
*/
|
||||
i2c_disable_irq(dev, I2C_IRQ_EVENT);
|
||||
I2C_CRUMB(STOP_SENT, 0, 0);
|
||||
dev->state = I2C_STATE_XFER_DONE;
|
||||
}
|
||||
sr1 = sr2 = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* EV7: Master Receiver
|
||||
*/
|
||||
if (sr1 & I2C_SR1_RXNE) {
|
||||
I2C_CRUMB(RXNE_ONLY, 0, 0);
|
||||
msg->data[msg->xferred++] = dev->regs->DR;
|
||||
|
||||
/*
|
||||
* EV7_1: Second to last byte in the reception? Set NACK and generate
|
||||
* stop/restart condition in time for the last byte. We'll get one more
|
||||
* RXNE interrupt before shutting things down.
|
||||
*/
|
||||
if (msg->xferred == (msg->length - 1)) {
|
||||
i2c_disable_ack(dev);
|
||||
if (dev->msgs_left > 2) {
|
||||
i2c_start_condition(dev);
|
||||
I2C_CRUMB(RXNE_START_SENT, 0, 0);
|
||||
} else {
|
||||
i2c_stop_condition(dev);
|
||||
I2C_CRUMB(RXNE_STOP_SENT, 0, 0);
|
||||
}
|
||||
} else if (msg->xferred == msg->length) {
|
||||
dev->msgs_left--;
|
||||
if (dev->msgs_left == 0) {
|
||||
/*
|
||||
* We're done.
|
||||
*/
|
||||
I2C_CRUMB(RXNE_DONE, 0, 0);
|
||||
dev->state = I2C_STATE_XFER_DONE;
|
||||
} else {
|
||||
dev->msg++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Interrupt handler for I2C error conditions. Aborts any pending I2C
|
||||
* transactions.
|
||||
*/
|
||||
void _i2c_irq_error_handler(i2c_dev *dev) {
|
||||
I2C_CRUMB(ERROR_ENTRY, dev->regs->SR1, dev->regs->SR2);
|
||||
|
||||
dev->error_flags = dev->regs->SR2 & (I2C_SR1_BERR |
|
||||
I2C_SR1_ARLO |
|
||||
I2C_SR1_AF |
|
||||
I2C_SR1_OVR);
|
||||
/* Clear flags */
|
||||
dev->regs->SR1 = 0;
|
||||
dev->regs->SR2 = 0;
|
||||
|
||||
i2c_stop_condition(dev);
|
||||
i2c_disable_irq(dev, I2C_IRQ_BUFFER | I2C_IRQ_EVENT | I2C_IRQ_ERROR);
|
||||
dev->state = I2C_STATE_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* CCR/TRISE configuration helper
|
||||
*/
|
||||
static void set_ccr_trise(i2c_dev *dev, uint32 flags) {
|
||||
uint32 ccr = 0;
|
||||
uint32 trise = 0;
|
||||
uint32 clk_mhz = _i2c_bus_clk(dev);
|
||||
uint32 clk_hz = clk_mhz * (1000 * 1000);
|
||||
|
||||
i2c_set_input_clk(dev, clk_mhz);
|
||||
|
||||
if (flags & I2C_FAST_MODE) {
|
||||
ccr |= I2C_CCR_FS;
|
||||
|
||||
if (flags & I2C_DUTY_16_9) {
|
||||
/* Tlow/Thigh = 16/9 */
|
||||
ccr |= I2C_CCR_DUTY_16_9;
|
||||
ccr |= clk_hz / (400000 * 25);
|
||||
} else {
|
||||
/* Tlow/Thigh = 2 */
|
||||
ccr |= clk_hz / (400000 * 3);
|
||||
}
|
||||
|
||||
trise = (300 * clk_mhz / 1000) + 1;
|
||||
} else {
|
||||
/* Tlow/Thigh = 1 */
|
||||
ccr = clk_hz / (100000 * 2);
|
||||
trise = clk_mhz + 1;
|
||||
}
|
||||
|
||||
/* Set minimum required value if CCR < 1*/
|
||||
if ((ccr & I2C_CCR_CCR) == 0) {
|
||||
ccr |= 0x1;
|
||||
}
|
||||
|
||||
i2c_set_clk_control(dev, ccr);
|
||||
i2c_set_trise(dev, trise);
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2012 LeafLabs, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/stm32f1/i2c.c
|
||||
* @brief STM32F1 I2C support
|
||||
*/
|
||||
|
||||
#include "i2c_private.h"
|
||||
#include <libmaple/i2c.h>
|
||||
|
||||
/*
|
||||
* Devices
|
||||
*/
|
||||
|
||||
static i2c_dev i2c1 = I2C_DEV_OLD(1, &gpiob, 7, 6);
|
||||
static i2c_dev i2c2 = I2C_DEV_OLD(2, &gpiob, 11, 10);
|
||||
|
||||
/** STM32F1 I2C device 1 */
|
||||
i2c_dev* const I2C1 = &i2c1;
|
||||
/** STM32F1 I2C device 2 */
|
||||
i2c_dev* const I2C2 = &i2c2;
|
||||
|
||||
/*
|
||||
* Routines
|
||||
*/
|
||||
|
||||
static int i2c1_wants_remap(const i2c_dev *dev) {
|
||||
/* Check if we've got I2C1 configured for SDA/SCL remap on PB9/PB8 */
|
||||
return (dev->clk_id == RCC_I2C1) &&
|
||||
(scl_port(dev)->clk_id == RCC_GPIOB) &&
|
||||
(sda_port(dev)->clk_id == RCC_GPIOB) &&
|
||||
(dev->sda_pin == 9) &&
|
||||
(dev->scl_pin == 8);
|
||||
}
|
||||
|
||||
void i2c_config_gpios(const i2c_dev *dev) {
|
||||
if (i2c1_wants_remap(dev)) {
|
||||
afio_remap(AFIO_REMAP_I2C1);
|
||||
}
|
||||
gpio_set_mode(sda_port(dev), dev->sda_pin, GPIO_AF_OUTPUT_OD);
|
||||
gpio_set_mode(scl_port(dev), dev->scl_pin, GPIO_AF_OUTPUT_OD);
|
||||
}
|
||||
|
||||
void i2c_master_release_bus(const i2c_dev *dev) {
|
||||
gpio_write_bit(scl_port(dev), dev->scl_pin, 1);
|
||||
gpio_write_bit(sda_port(dev), dev->sda_pin, 1);
|
||||
gpio_set_mode(scl_port(dev), dev->scl_pin, GPIO_OUTPUT_OD);
|
||||
gpio_set_mode(sda_port(dev), dev->sda_pin, GPIO_OUTPUT_OD);
|
||||
}
|
||||
|
||||
/*
|
||||
* IRQ handlers
|
||||
*/
|
||||
|
||||
void __irq_i2c1_ev(void) {
|
||||
_i2c_irq_handler(I2C1);
|
||||
}
|
||||
|
||||
void __irq_i2c2_ev(void) {
|
||||
_i2c_irq_handler(I2C2);
|
||||
}
|
||||
|
||||
void __irq_i2c1_er(void) {
|
||||
_i2c_irq_error_handler(I2C1);
|
||||
}
|
||||
|
||||
void __irq_i2c2_er(void) {
|
||||
_i2c_irq_error_handler(I2C2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Internal APIs
|
||||
*/
|
||||
|
||||
void _i2c_irq_priority_fixup(i2c_dev *dev) {
|
||||
/*
|
||||
* Important STM32 Errata:
|
||||
*
|
||||
* See STM32F10xx8 and STM32F10xxB Errata sheet (Doc ID 14574 Rev 8),
|
||||
* Section 2.11.1, 2.11.2.
|
||||
*
|
||||
* 2.11.1:
|
||||
* When the EV7, EV7_1, EV6_1, EV6_3, EV2, EV8, and EV3 events are not
|
||||
* managed before the current byte is being transferred, problems may be
|
||||
* encountered such as receiving an extra byte, reading the same data twice
|
||||
* or missing data.
|
||||
*
|
||||
* 2.11.2:
|
||||
* In Master Receiver mode, when closing the communication using
|
||||
* method 2, the content of the last read data can be corrupted.
|
||||
*
|
||||
* If the user software is not able to read the data N-1 before the STOP
|
||||
* condition is generated on the bus, the content of the shift register
|
||||
* (data N) will be corrupted. (data N is shifted 1-bit to the left).
|
||||
*
|
||||
* ----------------------------------------------------------------------
|
||||
*
|
||||
* In order to ensure that events are not missed, the i2c interrupt must
|
||||
* not be preempted. We set the i2c interrupt priority to be the highest
|
||||
* interrupt in the system (priority level 0). All other interrupts have
|
||||
* been initialized to priority level 16. See nvic_init().
|
||||
*/
|
||||
nvic_irq_set_priority(dev->ev_nvic_line, 0);
|
||||
nvic_irq_set_priority(dev->er_nvic_line, 0);
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Michael Hope.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/iwdg.c
|
||||
* @brief Independent watchdog (IWDG) support
|
||||
*/
|
||||
|
||||
#include <libmaple/iwdg.h>
|
||||
|
||||
/**
|
||||
* @brief Initialise and start the watchdog
|
||||
*
|
||||
* The prescaler and reload set the timeout. For example, a prescaler
|
||||
* of IWDG_PRE_32 divides the 40 kHz clock by 32 and gives roughly 1
|
||||
* ms per reload.
|
||||
*
|
||||
* @param prescaler Prescaler for the 40 kHz IWDG clock.
|
||||
* @param reload Independent watchdog counter reload value.
|
||||
*/
|
||||
void iwdg_init(iwdg_prescaler prescaler, uint16 reload) {
|
||||
IWDG_BASE->KR = IWDG_KR_UNLOCK;
|
||||
IWDG_BASE->PR = prescaler;
|
||||
IWDG_BASE->RLR = reload;
|
||||
|
||||
/* Start things off */
|
||||
IWDG_BASE->KR = IWDG_KR_START;
|
||||
iwdg_feed();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reset the IWDG counter.
|
||||
*
|
||||
* Calling this function will cause the IWDG counter to be reset to
|
||||
* its reload value.
|
||||
*/
|
||||
void iwdg_feed(void) {
|
||||
IWDG_BASE->KR = IWDG_KR_FEED;
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Perry Hung.
|
||||
* Copyright (c) 2011 LeafLabs, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/nvic.c
|
||||
* @brief Nested vector interrupt controller support.
|
||||
*/
|
||||
|
||||
#include <libmaple/nvic.h>
|
||||
#include <libmaple/scb.h>
|
||||
#include <libmaple/stm32.h>
|
||||
|
||||
/**
|
||||
* @brief Set interrupt priority for an interrupt line
|
||||
*
|
||||
* Note: The STM32 only implements 4 bits of priority, ignoring the
|
||||
* lower 4 bits. This means there are only 16 levels of priority.
|
||||
* Bits[3:0] read as zero and ignore writes.
|
||||
*
|
||||
* @param irqn device to set
|
||||
* @param priority Priority to set, 0 being highest priority and 15
|
||||
* being lowest.
|
||||
*/
|
||||
void nvic_irq_set_priority(nvic_irq_num irqn, uint8 priority) {
|
||||
if (irqn < 0) {
|
||||
/* This interrupt is in the system handler block */
|
||||
SCB_BASE->SHP[((uint32)irqn & 0xF) - 4] = (priority & 0xF) << 4;
|
||||
} else {
|
||||
NVIC_BASE->IP[irqn] = (priority & 0xF) << 4;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the NVIC, setting interrupts to a default priority.
|
||||
*/
|
||||
void nvic_init(uint32 address, uint32 offset) {
|
||||
uint32 i;
|
||||
|
||||
nvic_set_vector_table(address, offset);
|
||||
|
||||
/*
|
||||
* Lower priority level for all peripheral interrupts to lowest
|
||||
* possible.
|
||||
*/
|
||||
for (i = 0; i < STM32_NR_INTERRUPTS; i++) {
|
||||
nvic_irq_set_priority((nvic_irq_num)i, 0xF);
|
||||
}
|
||||
|
||||
/* Lower systick interrupt priority to lowest level */
|
||||
nvic_irq_set_priority(NVIC_SYSTICK, 0xF);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the vector table base address.
|
||||
*
|
||||
* For stand-alone products, the vector table base address is normally
|
||||
* the start of Flash (0x08000000).
|
||||
*
|
||||
* @param address Vector table base address.
|
||||
* @param offset Offset from address. Some restrictions apply to the
|
||||
* use of nonzero offsets; see the ARM Cortex M3
|
||||
* Technical Reference Manual.
|
||||
*/
|
||||
void nvic_set_vector_table(uint32 address, uint32 offset) {
|
||||
SCB_BASE->VTOR = address | (offset & 0x1FFFFF80);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Force a system reset.
|
||||
*
|
||||
* Resets all major system components, excluding debug.
|
||||
*/
|
||||
void nvic_sys_reset() {
|
||||
uint32 prigroup = SCB_BASE->AIRCR & SCB_AIRCR_PRIGROUP;
|
||||
SCB_BASE->AIRCR = SCB_AIRCR_VECTKEY | SCB_AIRCR_SYSRESETREQ | prigroup;
|
||||
asm volatile("dsb");
|
||||
while (1)
|
||||
;
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2011 LeafLabs, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/pwr.c
|
||||
* @brief Power control (PWR) support.
|
||||
*/
|
||||
|
||||
#include <libmaple/pwr.h>
|
||||
#include <libmaple/rcc.h>
|
||||
|
||||
/**
|
||||
* Enables the power interface clock, and resets the power device.
|
||||
*/
|
||||
void pwr_init(void) {
|
||||
rcc_clk_enable(RCC_PWR);
|
||||
rcc_reset_dev(RCC_PWR);
|
||||
}
|
|
@ -0,0 +1,169 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Perry Hung.
|
||||
* Copyright (c) 2011 LeafLabs, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/rcc.c
|
||||
* @brief Portable RCC routines.
|
||||
*/
|
||||
|
||||
#include <libmaple/rcc.h>
|
||||
|
||||
#include "rcc_private.h"
|
||||
|
||||
/**
|
||||
* @brief Get a peripheral's clock domain
|
||||
* @param id Clock ID of the peripheral whose clock domain to return
|
||||
* @return Clock source for the given clock ID
|
||||
*/
|
||||
rcc_clk_domain rcc_dev_clk(rcc_clk_id id) {
|
||||
return rcc_dev_table[id].clk_domain;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Switch the clock used as the source of the system clock.
|
||||
*
|
||||
* After switching the source, this function blocks until the new
|
||||
* clock source is in use.
|
||||
*
|
||||
* @param sysclk_src New system clock source.
|
||||
* @see rcc_sysclk_src
|
||||
*/
|
||||
void rcc_switch_sysclk(rcc_sysclk_src sysclk_src) {
|
||||
uint32 cfgr = RCC_BASE->CFGR;
|
||||
cfgr &= ~RCC_CFGR_SW;
|
||||
cfgr |= sysclk_src;
|
||||
|
||||
/* Switch SYSCLK source. */
|
||||
RCC_BASE->CFGR = cfgr;
|
||||
|
||||
/* Wait for new source to come into use. */
|
||||
while ((RCC_BASE->CFGR & RCC_CFGR_SWS) != (sysclk_src << 2))
|
||||
;
|
||||
}
|
||||
|
||||
/*
|
||||
* Turning clocks off and on, querying their status.
|
||||
*/
|
||||
|
||||
/* IMPORTANT NOTE FOR IMPLEMENTORS:
|
||||
*
|
||||
* libmaple assumes that enum rcc_clk enumerators are two-byte
|
||||
* values, stored in a uint16, in the following way:
|
||||
*
|
||||
* - The high-order byte is the byte offset (from RCC_BASE) of the register
|
||||
* to touch when turning on or off the given clock.
|
||||
*
|
||||
* - The low-order byte is the bit in that register that turns the
|
||||
* clock on or off.
|
||||
*
|
||||
* Example for STM32F1: Turning on the high-speed external clock (HSE)
|
||||
* involves setting HSEON, bit 16, of RCC_CR. The high-order byte is
|
||||
* then offsetof(struct rcc_reg_map, CR) = 0, and the low-order byte
|
||||
* is 16.
|
||||
*
|
||||
* The corresponding value of RCC_CLK_HSE is thus (0 << 8) | 16 = 16.
|
||||
*
|
||||
* On all known STM32 series, this encoding has the property that
|
||||
* adding one to the low byte also gives the bit to check to determine
|
||||
* if the clock is ready. For example, on STM32F1, RCC_CR_HSERDY is
|
||||
* bit 17. If that's not the case on your series, rcc_is_clk_ready()
|
||||
* won't work for you. */
|
||||
|
||||
/* Returns the RCC register which controls the clock source. */
|
||||
static inline __io uint32* rcc_clk_reg(rcc_clk clock) {
|
||||
return (__io uint32*)((__io uint8*)RCC_BASE + (clock >> 8));
|
||||
}
|
||||
|
||||
/* Returns a mask in rcc_clk_reg(clock) to be used for turning the
|
||||
* clock on and off */
|
||||
static inline uint32 rcc_clk_on_mask(rcc_clk clock) {
|
||||
return 1 << (clock & 0xFF);
|
||||
}
|
||||
|
||||
/* Returns a mask in rcc_clk_reg(clock) to be used when checking the
|
||||
* readiness of the clock. */
|
||||
static inline uint32 rcc_clk_ready_mask(rcc_clk clock) {
|
||||
return rcc_clk_on_mask(clock) << 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Turn on a clock source.
|
||||
*
|
||||
* After this routine exits, callers should ensure that the clock
|
||||
* source is ready by waiting until rcc_is_clk_ready(clock) returns
|
||||
* true.
|
||||
*
|
||||
* @param clock Clock to turn on.
|
||||
* @see rcc_turn_off_clk()
|
||||
* @see rcc_is_clk_ready()
|
||||
*/
|
||||
void rcc_turn_on_clk(rcc_clk clock) {
|
||||
*rcc_clk_reg(clock) |= rcc_clk_on_mask(clock);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Turn off a clock source.
|
||||
*
|
||||
* In certain configurations, certain clock sources cannot be safely
|
||||
* turned off. (For example, the main PLL on STM32F1 devices cannot be
|
||||
* turned off if it has been selected as the SYSCLK source). Consult
|
||||
* the reference material for your MCU to ensure it is safe to call
|
||||
* this function.
|
||||
*
|
||||
* @param clock Clock to turn off.
|
||||
* @see rcc_turn_on_clk()
|
||||
* @see rcc_is_clk_ready()
|
||||
*/
|
||||
void rcc_turn_off_clk(rcc_clk clock) {
|
||||
*rcc_clk_reg(clock) &= ~rcc_clk_on_mask(clock);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if a clock is on.
|
||||
* @param clock Clock to check.
|
||||
* @return 1 if the clock is on, 0 if the clock is off.
|
||||
*/
|
||||
int rcc_is_clk_on(rcc_clk clock) {
|
||||
return !!(*rcc_clk_reg(clock) & rcc_clk_on_mask(clock));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if a clock source is ready.
|
||||
*
|
||||
* In general, it is not safe to rely on a clock source unless this
|
||||
* function returns nonzero. Also note that this function may return
|
||||
* nonzero for a short period of time after a clock has been turned
|
||||
* off. Consult the reference material for your MCU for more details.
|
||||
*
|
||||
* @param clock Clock whose readiness to check for.
|
||||
* @return Nonzero if the clock is ready, zero otherwise.
|
||||
* @see rcc_turn_on_clk()
|
||||
* @see rcc_turn_off_clk()
|
||||
*/
|
||||
int rcc_is_clk_ready(rcc_clk clock) {
|
||||
return (int)(*rcc_clk_reg(clock) & rcc_clk_ready_mask(clock));
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Perry Hung.
|
||||
* Copyright (c) 2011 LeafLabs, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/stm32f1/rcc.c
|
||||
* @brief STM32F1 RCC.
|
||||
*/
|
||||
|
||||
#include <libmaple/rcc.h>
|
||||
#include <libmaple/libmaple.h>
|
||||
#include <libmaple/bitband.h>
|
||||
|
||||
#include "rcc_private.h"
|
||||
|
||||
#define APB1 RCC_APB1
|
||||
#define APB2 RCC_APB2
|
||||
#define AHB RCC_AHB
|
||||
|
||||
/* Device descriptor table, maps rcc_clk_id onto bus and enable/reset
|
||||
* register bit numbers. */
|
||||
const struct rcc_dev_info rcc_dev_table[] = {
|
||||
[RCC_GPIOA] = { .clk_domain = APB2, .line_num = 2 },
|
||||
[RCC_GPIOB] = { .clk_domain = APB2, .line_num = 3 },
|
||||
[RCC_GPIOC] = { .clk_domain = APB2, .line_num = 4 },
|
||||
[RCC_GPIOD] = { .clk_domain = APB2, .line_num = 5 },
|
||||
[RCC_AFIO] = { .clk_domain = APB2, .line_num = 0 },
|
||||
[RCC_ADC1] = { .clk_domain = APB2, .line_num = 9 },
|
||||
[RCC_ADC2] = { .clk_domain = APB2, .line_num = 10 },
|
||||
[RCC_ADC3] = { .clk_domain = APB2, .line_num = 15 },
|
||||
[RCC_USART1] = { .clk_domain = APB2, .line_num = 14 },
|
||||
[RCC_USART2] = { .clk_domain = APB1, .line_num = 17 },
|
||||
[RCC_USART3] = { .clk_domain = APB1, .line_num = 18 },
|
||||
[RCC_TIMER1] = { .clk_domain = APB2, .line_num = 11 },
|
||||
[RCC_TIMER2] = { .clk_domain = APB1, .line_num = 0 },
|
||||
[RCC_TIMER3] = { .clk_domain = APB1, .line_num = 1 },
|
||||
[RCC_TIMER4] = { .clk_domain = APB1, .line_num = 2 },
|
||||
[RCC_SPI1] = { .clk_domain = APB2, .line_num = 12 },
|
||||
[RCC_SPI2] = { .clk_domain = APB1, .line_num = 14 },
|
||||
[RCC_DMA1] = { .clk_domain = AHB, .line_num = 0 },
|
||||
[RCC_PWR] = { .clk_domain = APB1, .line_num = 28},
|
||||
[RCC_BKP] = { .clk_domain = APB1, .line_num = 27},
|
||||
[RCC_I2C1] = { .clk_domain = APB1, .line_num = 21 },
|
||||
[RCC_I2C2] = { .clk_domain = APB1, .line_num = 22 },
|
||||
[RCC_CRC] = { .clk_domain = AHB, .line_num = 6},
|
||||
[RCC_FLITF] = { .clk_domain = AHB, .line_num = 4},
|
||||
[RCC_SRAM] = { .clk_domain = AHB, .line_num = 2},
|
||||
[RCC_USB] = { .clk_domain = APB1, .line_num = 23},
|
||||
#if defined(STM32_HIGH_DENSITY) || defined(STM32_XL_DENSITY)
|
||||
[RCC_GPIOE] = { .clk_domain = APB2, .line_num = 6 },
|
||||
[RCC_GPIOF] = { .clk_domain = APB2, .line_num = 7 },
|
||||
[RCC_GPIOG] = { .clk_domain = APB2, .line_num = 8 },
|
||||
[RCC_UART4] = { .clk_domain = APB1, .line_num = 19 },
|
||||
[RCC_UART5] = { .clk_domain = APB1, .line_num = 20 },
|
||||
[RCC_TIMER5] = { .clk_domain = APB1, .line_num = 3 },
|
||||
[RCC_TIMER6] = { .clk_domain = APB1, .line_num = 4 },
|
||||
[RCC_TIMER7] = { .clk_domain = APB1, .line_num = 5 },
|
||||
[RCC_TIMER8] = { .clk_domain = APB2, .line_num = 13 },
|
||||
[RCC_FSMC] = { .clk_domain = AHB, .line_num = 8 },
|
||||
[RCC_DAC] = { .clk_domain = APB1, .line_num = 29 },
|
||||
[RCC_DMA2] = { .clk_domain = AHB, .line_num = 1 },
|
||||
[RCC_SDIO] = { .clk_domain = AHB, .line_num = 10 },
|
||||
[RCC_SPI3] = { .clk_domain = APB1, .line_num = 15 },
|
||||
#endif
|
||||
#ifdef STM32_XL_DENSITY
|
||||
[RCC_TIMER9] = { .clk_domain = APB2, .line_num = 19 },
|
||||
[RCC_TIMER10] = { .clk_domain = APB2, .line_num = 20 },
|
||||
[RCC_TIMER11] = { .clk_domain = APB2, .line_num = 21 },
|
||||
[RCC_TIMER12] = { .clk_domain = APB1, .line_num = 6 },
|
||||
[RCC_TIMER13] = { .clk_domain = APB1, .line_num = 7 },
|
||||
[RCC_TIMER14] = { .clk_domain = APB1, .line_num = 8 },
|
||||
#endif
|
||||
};
|
||||
|
||||
__deprecated
|
||||
void rcc_clk_init(rcc_sysclk_src sysclk_src,
|
||||
rcc_pllsrc pll_src,
|
||||
rcc_pll_multiplier pll_mul) {
|
||||
/* Assume that we're going to clock the chip off the PLL, fed by
|
||||
* the HSE */
|
||||
ASSERT(sysclk_src == RCC_CLKSRC_PLL &&
|
||||
pll_src == RCC_PLLSRC_HSE);
|
||||
|
||||
RCC_BASE->CFGR = pll_src | pll_mul;
|
||||
|
||||
/* Turn on, and wait for, HSE. */
|
||||
rcc_turn_on_clk(RCC_CLK_HSE);
|
||||
while (!rcc_is_clk_ready(RCC_CLK_HSE))
|
||||
;
|
||||
|
||||
/* Do the same for the main PLL. */
|
||||
rcc_turn_on_clk(RCC_CLK_PLL);
|
||||
while(!rcc_is_clk_ready(RCC_CLK_PLL))
|
||||
;
|
||||
|
||||
/* Finally, switch over to the PLL. */
|
||||
rcc_switch_sysclk(RCC_CLKSRC_PLL);
|
||||
}
|
||||
|
||||
/* pll_cfg->data must point to a valid struct stm32f1_rcc_pll_data. */
|
||||
void rcc_configure_pll(rcc_pll_cfg *pll_cfg) {
|
||||
stm32f1_rcc_pll_data *data = pll_cfg->data;
|
||||
rcc_pll_multiplier pll_mul = data->pll_mul;
|
||||
uint32 cfgr;
|
||||
|
||||
/* Check that the PLL is disabled. */
|
||||
ASSERT_FAULT(!rcc_is_clk_on(RCC_CLK_PLL));
|
||||
|
||||
cfgr = RCC_BASE->CFGR;
|
||||
cfgr &= ~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLMUL);
|
||||
cfgr |= pll_cfg->pllsrc | pll_mul;
|
||||
RCC_BASE->CFGR = cfgr;
|
||||
}
|
||||
|
||||
void rcc_clk_enable(rcc_clk_id id) {
|
||||
static __io uint32* enable_regs[] = {
|
||||
[APB1] = &RCC_BASE->APB1ENR,
|
||||
[APB2] = &RCC_BASE->APB2ENR,
|
||||
[AHB] = &RCC_BASE->AHBENR,
|
||||
};
|
||||
rcc_do_clk_enable(enable_regs, id);
|
||||
}
|
||||
|
||||
void rcc_reset_dev(rcc_clk_id id) {
|
||||
static __io uint32* reset_regs[] = {
|
||||
[APB1] = &RCC_BASE->APB1RSTR,
|
||||
[APB2] = &RCC_BASE->APB2RSTR,
|
||||
};
|
||||
rcc_do_reset_dev(reset_regs, id);
|
||||
}
|
||||
|
||||
void rcc_set_prescaler(rcc_prescaler prescaler, uint32 divider) {
|
||||
static const uint32 masks[] = {
|
||||
[RCC_PRESCALER_AHB] = RCC_CFGR_HPRE,
|
||||
[RCC_PRESCALER_APB1] = RCC_CFGR_PPRE1,
|
||||
[RCC_PRESCALER_APB2] = RCC_CFGR_PPRE2,
|
||||
[RCC_PRESCALER_USB] = RCC_CFGR_USBPRE,
|
||||
[RCC_PRESCALER_ADC] = RCC_CFGR_ADCPRE,
|
||||
};
|
||||
rcc_do_set_prescaler(masks, prescaler, divider);
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2011, 2012 LeafLabs, LLC.
|
||||
* Copyright (c) 2010 Perry Hung.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/spi.c
|
||||
* @author Marti Bolivar <mbolivar@leaflabs.com>
|
||||
* @brief Serial Peripheral Interface (SPI) support.
|
||||
* Currently, there is no Integrated Interchip Sound (I2S) support.
|
||||
*/
|
||||
|
||||
#include <libmaple/spi.h>
|
||||
#include <libmaple/bitband.h>
|
||||
|
||||
static void spi_reconfigure(spi_dev *dev, uint32 cr1_config);
|
||||
|
||||
/*
|
||||
* SPI convenience routines
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Initialize and reset a SPI device.
|
||||
* @param dev Device to initialize and reset.
|
||||
*/
|
||||
void spi_init(spi_dev *dev) {
|
||||
rcc_clk_enable(dev->clk_id);
|
||||
rcc_reset_dev(dev->clk_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configure and enable a SPI device as bus master.
|
||||
*
|
||||
* The device's peripheral will be disabled before being reconfigured.
|
||||
*
|
||||
* @param dev Device to configure as bus master
|
||||
* @param baud Bus baud rate
|
||||
* @param mode SPI mode
|
||||
* @param flags Logical OR of spi_cfg_flag values.
|
||||
* @see spi_cfg_flag
|
||||
*/
|
||||
void spi_master_enable(spi_dev *dev,
|
||||
spi_baud_rate baud,
|
||||
spi_mode mode,
|
||||
uint32 flags) {
|
||||
spi_reconfigure(dev, baud | flags | SPI_CR1_MSTR | mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configure and enable a SPI device as a bus slave.
|
||||
*
|
||||
* The device's peripheral will be disabled before being reconfigured.
|
||||
*
|
||||
* @param dev Device to configure as a bus slave
|
||||
* @param mode SPI mode
|
||||
* @param flags Logical OR of spi_cfg_flag values.
|
||||
* @see spi_cfg_flag
|
||||
*/
|
||||
void spi_slave_enable(spi_dev *dev, spi_mode mode, uint32 flags) {
|
||||
spi_reconfigure(dev, flags | mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Nonblocking SPI transmit.
|
||||
* @param dev SPI port to use for transmission
|
||||
* @param buf Buffer to transmit. The sizeof buf's elements are
|
||||
* inferred from dev's data frame format (i.e., are
|
||||
* correctly treated as 8-bit or 16-bit quantities).
|
||||
* @param len Maximum number of elements to transmit.
|
||||
* @return Number of elements transmitted.
|
||||
*/
|
||||
uint32 spi_tx(spi_dev *dev, const void *buf, uint32 len) {
|
||||
uint32 txed = 0;
|
||||
uint8 byte_frame = spi_dff(dev) == SPI_DFF_8_BIT;
|
||||
while (spi_is_tx_empty(dev) && (txed < len)) {
|
||||
if (byte_frame) {
|
||||
dev->regs->DR = ((const uint8*)buf)[txed++];
|
||||
} else {
|
||||
dev->regs->DR = ((const uint16*)buf)[txed++];
|
||||
}
|
||||
}
|
||||
return txed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable a SPI peripheral
|
||||
* @param dev Device to enable
|
||||
*/
|
||||
void spi_peripheral_enable(spi_dev *dev) {
|
||||
bb_peri_set_bit(&dev->regs->CR1, SPI_CR1_SPE_BIT, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disable a SPI peripheral
|
||||
* @param dev Device to disable
|
||||
*/
|
||||
void spi_peripheral_disable(spi_dev *dev) {
|
||||
bb_peri_set_bit(&dev->regs->CR1, SPI_CR1_SPE_BIT, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable DMA requests whenever the transmit buffer is empty
|
||||
* @param dev SPI device on which to enable TX DMA requests
|
||||
*/
|
||||
void spi_tx_dma_enable(spi_dev *dev) {
|
||||
bb_peri_set_bit(&dev->regs->CR2, SPI_CR2_TXDMAEN_BIT, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disable DMA requests whenever the transmit buffer is empty
|
||||
* @param dev SPI device on which to disable TX DMA requests
|
||||
*/
|
||||
void spi_tx_dma_disable(spi_dev *dev) {
|
||||
bb_peri_set_bit(&dev->regs->CR2, SPI_CR2_TXDMAEN_BIT, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable DMA requests whenever the receive buffer is empty
|
||||
* @param dev SPI device on which to enable RX DMA requests
|
||||
*/
|
||||
void spi_rx_dma_enable(spi_dev *dev) {
|
||||
bb_peri_set_bit(&dev->regs->CR2, SPI_CR2_RXDMAEN_BIT, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disable DMA requests whenever the receive buffer is empty
|
||||
* @param dev SPI device on which to disable RX DMA requests
|
||||
*/
|
||||
void spi_rx_dma_disable(spi_dev *dev) {
|
||||
bb_peri_set_bit(&dev->regs->CR2, SPI_CR2_RXDMAEN_BIT, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* SPI auxiliary routines
|
||||
*/
|
||||
|
||||
static void spi_reconfigure(spi_dev *dev, uint32 cr1_config) {
|
||||
spi_irq_disable(dev, SPI_INTERRUPTS_ALL);
|
||||
spi_peripheral_disable(dev);
|
||||
dev->regs->CR1 = cr1_config;
|
||||
spi_peripheral_enable(dev);
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2011, 2012 LeafLabs, LLC.
|
||||
* Copyright (c) 2010 Perry Hung.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/stm32f1/spi.c
|
||||
* @author Marti Bolivar <mbolivar@leaflabs.com>
|
||||
* @brief STM32F1 SPI/I2S.
|
||||
*/
|
||||
|
||||
#include <libmaple/spi.h>
|
||||
#include <libmaple/gpio.h>
|
||||
#include "spi_private.h"
|
||||
|
||||
/*
|
||||
* Devices
|
||||
*/
|
||||
|
||||
static spi_dev spi1 = SPI_DEV(1);
|
||||
static spi_dev spi2 = SPI_DEV(2);
|
||||
|
||||
spi_dev *SPI1 = &spi1;
|
||||
spi_dev *SPI2 = &spi2;
|
||||
|
||||
#if defined(STM32_HIGH_DENSITY) || defined(STM32_XL_DENSITY)
|
||||
static spi_dev spi3 = SPI_DEV(3);
|
||||
spi_dev *SPI3 = &spi3;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Routines
|
||||
*/
|
||||
|
||||
void spi_config_gpios(spi_dev *ignored,
|
||||
uint8 as_master,
|
||||
gpio_dev *nss_dev,
|
||||
uint8 nss_bit,
|
||||
gpio_dev *comm_dev,
|
||||
uint8 sck_bit,
|
||||
uint8 miso_bit,
|
||||
uint8 mosi_bit) {
|
||||
if (as_master) {
|
||||
gpio_set_mode(nss_dev, nss_bit, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(comm_dev, sck_bit, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(comm_dev, miso_bit, GPIO_INPUT_FLOATING);
|
||||
gpio_set_mode(comm_dev, mosi_bit, GPIO_AF_OUTPUT_PP);
|
||||
} else {
|
||||
gpio_set_mode(nss_dev, nss_bit, GPIO_INPUT_FLOATING);
|
||||
gpio_set_mode(comm_dev, sck_bit, GPIO_INPUT_FLOATING);
|
||||
gpio_set_mode(comm_dev, miso_bit, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(comm_dev, mosi_bit, GPIO_INPUT_FLOATING);
|
||||
}
|
||||
}
|
||||
|
||||
void spi_foreach(void (*fn)(spi_dev*)) {
|
||||
fn(SPI1);
|
||||
fn(SPI2);
|
||||
#if defined(STM32_HIGH_DENSITY) || defined(STM32_XL_DENSITY)
|
||||
fn(SPI3);
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,263 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2011 Perry Hung.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/* STM32F1 performance line ISR weak declarations */
|
||||
|
||||
#include <libmaple/stm32.h>
|
||||
|
||||
.thumb
|
||||
|
||||
/* Default handler for all non-overridden interrupts and exceptions */
|
||||
.globl __default_handler
|
||||
.type __default_handler, %function
|
||||
|
||||
__default_handler:
|
||||
b .
|
||||
|
||||
.weak __exc_nmi
|
||||
.globl __exc_nmi
|
||||
.set __exc_nmi, __default_handler
|
||||
.weak __exc_hardfault
|
||||
.globl __exc_hardfault
|
||||
.set __exc_hardfault, __default_handler
|
||||
.weak __exc_memmanage
|
||||
.globl __exc_memmanage
|
||||
.set __exc_memmanage, __default_handler
|
||||
.weak __exc_busfault
|
||||
.globl __exc_busfault
|
||||
.set __exc_busfault, __default_handler
|
||||
.weak __exc_usagefault
|
||||
.globl __exc_usagefault
|
||||
.set __exc_usagefault, __default_handler
|
||||
.weak __stm32reservedexception7
|
||||
.globl __stm32reservedexception7
|
||||
.set __stm32reservedexception7, __default_handler
|
||||
.weak __stm32reservedexception8
|
||||
.globl __stm32reservedexception8
|
||||
.set __stm32reservedexception8, __default_handler
|
||||
.weak __stm32reservedexception9
|
||||
.globl __stm32reservedexception9
|
||||
.set __stm32reservedexception9, __default_handler
|
||||
.weak __stm32reservedexception10
|
||||
.globl __stm32reservedexception10
|
||||
.set __stm32reservedexception10, __default_handler
|
||||
.weak __exc_svc
|
||||
.globl __exc_svc
|
||||
.set __exc_svc, __default_handler
|
||||
.weak __exc_debug_monitor
|
||||
.globl __exc_debug_monitor
|
||||
.set __exc_debug_monitor, __default_handler
|
||||
.weak __stm32reservedexception13
|
||||
.globl __stm32reservedexception13
|
||||
.set __stm32reservedexception13, __default_handler
|
||||
.weak __exc_pendsv
|
||||
.globl __exc_pendsv
|
||||
.set __exc_pendsv, __default_handler
|
||||
.weak __exc_systick
|
||||
.globl __exc_systick
|
||||
.set __exc_systick, __default_handler
|
||||
.weak __irq_wwdg
|
||||
.globl __irq_wwdg
|
||||
.set __irq_wwdg, __default_handler
|
||||
.weak __irq_pvd
|
||||
.globl __irq_pvd
|
||||
.set __irq_pvd, __default_handler
|
||||
.weak __irq_tamper
|
||||
.globl __irq_tamper
|
||||
.set __irq_tamper, __default_handler
|
||||
.weak __irq_rtc
|
||||
.globl __irq_rtc
|
||||
.set __irq_rtc, __default_handler
|
||||
.weak __irq_flash
|
||||
.globl __irq_flash
|
||||
.set __irq_flash, __default_handler
|
||||
.weak __irq_rcc
|
||||
.globl __irq_rcc
|
||||
.set __irq_rcc, __default_handler
|
||||
.weak __irq_exti0
|
||||
.globl __irq_exti0
|
||||
.set __irq_exti0, __default_handler
|
||||
.weak __irq_exti1
|
||||
.globl __irq_exti1
|
||||
.set __irq_exti1, __default_handler
|
||||
.weak __irq_exti2
|
||||
.globl __irq_exti2
|
||||
.set __irq_exti2, __default_handler
|
||||
.weak __irq_exti3
|
||||
.globl __irq_exti3
|
||||
.set __irq_exti3, __default_handler
|
||||
.weak __irq_exti4
|
||||
.globl __irq_exti4
|
||||
.set __irq_exti4, __default_handler
|
||||
.weak __irq_dma1_channel1
|
||||
.globl __irq_dma1_channel1
|
||||
.set __irq_dma1_channel1, __default_handler
|
||||
.weak __irq_dma1_channel2
|
||||
.globl __irq_dma1_channel2
|
||||
.set __irq_dma1_channel2, __default_handler
|
||||
.weak __irq_dma1_channel3
|
||||
.globl __irq_dma1_channel3
|
||||
.set __irq_dma1_channel3, __default_handler
|
||||
.weak __irq_dma1_channel4
|
||||
.globl __irq_dma1_channel4
|
||||
.set __irq_dma1_channel4, __default_handler
|
||||
.weak __irq_dma1_channel5
|
||||
.globl __irq_dma1_channel5
|
||||
.set __irq_dma1_channel5, __default_handler
|
||||
.weak __irq_dma1_channel6
|
||||
.globl __irq_dma1_channel6
|
||||
.set __irq_dma1_channel6, __default_handler
|
||||
.weak __irq_dma1_channel7
|
||||
.globl __irq_dma1_channel7
|
||||
.set __irq_dma1_channel7, __default_handler
|
||||
.weak __irq_adc
|
||||
.globl __irq_adc
|
||||
.set __irq_adc, __default_handler
|
||||
.weak __irq_usb_hp_can_tx
|
||||
.globl __irq_usb_hp_can_tx
|
||||
.set __irq_usb_hp_can_tx, __default_handler
|
||||
.weak __irq_usb_lp_can_rx0
|
||||
.globl __irq_usb_lp_can_rx0
|
||||
.set __irq_usb_lp_can_rx0, __default_handler
|
||||
.weak __irq_can_rx1
|
||||
.globl __irq_can_rx1
|
||||
.set __irq_can_rx1, __default_handler
|
||||
.weak __irq_can_sce
|
||||
.globl __irq_can_sce
|
||||
.set __irq_can_sce, __default_handler
|
||||
.weak __irq_exti9_5
|
||||
.globl __irq_exti9_5
|
||||
.set __irq_exti9_5, __default_handler
|
||||
.weak __irq_tim1_brk
|
||||
.globl __irq_tim1_brk
|
||||
.set __irq_tim1_brk, __default_handler
|
||||
.weak __irq_tim1_up
|
||||
.globl __irq_tim1_up
|
||||
.set __irq_tim1_up, __default_handler
|
||||
.weak __irq_tim1_trg_com
|
||||
.globl __irq_tim1_trg_com
|
||||
.set __irq_tim1_trg_com, __default_handler
|
||||
.weak __irq_tim1_cc
|
||||
.globl __irq_tim1_cc
|
||||
.set __irq_tim1_cc, __default_handler
|
||||
.weak __irq_tim2
|
||||
.globl __irq_tim2
|
||||
.set __irq_tim2, __default_handler
|
||||
.weak __irq_tim3
|
||||
.globl __irq_tim3
|
||||
.set __irq_tim3, __default_handler
|
||||
.weak __irq_tim4
|
||||
.globl __irq_tim4
|
||||
.set __irq_tim4, __default_handler
|
||||
.weak __irq_i2c1_ev
|
||||
.globl __irq_i2c1_ev
|
||||
.set __irq_i2c1_ev, __default_handler
|
||||
.weak __irq_i2c1_er
|
||||
.globl __irq_i2c1_er
|
||||
.set __irq_i2c1_er, __default_handler
|
||||
.weak __irq_i2c2_ev
|
||||
.globl __irq_i2c2_ev
|
||||
.set __irq_i2c2_ev, __default_handler
|
||||
.weak __irq_i2c2_er
|
||||
.globl __irq_i2c2_er
|
||||
.set __irq_i2c2_er, __default_handler
|
||||
.weak __irq_spi1
|
||||
.globl __irq_spi1
|
||||
.set __irq_spi1, __default_handler
|
||||
.weak __irq_spi2
|
||||
.globl __irq_spi2
|
||||
.set __irq_spi2, __default_handler
|
||||
.weak __irq_usart1
|
||||
.globl __irq_usart1
|
||||
.set __irq_usart1, __default_handler
|
||||
.weak __irq_usart2
|
||||
.globl __irq_usart2
|
||||
.set __irq_usart2, __default_handler
|
||||
.weak __irq_usart3
|
||||
.globl __irq_usart3
|
||||
.set __irq_usart3, __default_handler
|
||||
.weak __irq_exti15_10
|
||||
.globl __irq_exti15_10
|
||||
.set __irq_exti15_10, __default_handler
|
||||
.weak __irq_rtcalarm
|
||||
.globl __irq_rtcalarm
|
||||
.set __irq_rtcalarm, __default_handler
|
||||
.weak __irq_usbwakeup
|
||||
.globl __irq_usbwakeup
|
||||
.set __irq_usbwakeup, __default_handler
|
||||
#if defined (STM32_HIGH_DENSITY)
|
||||
.weak __irq_tim8_brk
|
||||
.globl __irq_tim8_brk
|
||||
.set __irq_tim8_brk, __default_handler
|
||||
.weak __irq_tim8_up
|
||||
.globl __irq_tim8_up
|
||||
.set __irq_tim8_up, __default_handler
|
||||
.weak __irq_tim8_trg_com
|
||||
.globl __irq_tim8_trg_com
|
||||
.set __irq_tim8_trg_com, __default_handler
|
||||
.weak __irq_tim8_cc
|
||||
.globl __irq_tim8_cc
|
||||
.set __irq_tim8_cc, __default_handler
|
||||
.weak __irq_adc3
|
||||
.globl __irq_adc3
|
||||
.set __irq_adc3, __default_handler
|
||||
.weak __irq_fsmc
|
||||
.globl __irq_fsmc
|
||||
.set __irq_fsmc, __default_handler
|
||||
.weak __irq_sdio
|
||||
.globl __irq_sdio
|
||||
.set __irq_sdio, __default_handler
|
||||
.weak __irq_tim5
|
||||
.globl __irq_tim5
|
||||
.set __irq_tim5, __default_handler
|
||||
.weak __irq_spi3
|
||||
.globl __irq_spi3
|
||||
.set __irq_spi3, __default_handler
|
||||
.weak __irq_uart4
|
||||
.globl __irq_uart4
|
||||
.set __irq_uart4, __default_handler
|
||||
.weak __irq_uart5
|
||||
.globl __irq_uart5
|
||||
.set __irq_uart5, __default_handler
|
||||
.weak __irq_tim6
|
||||
.globl __irq_tim6
|
||||
.set __irq_tim6, __default_handler
|
||||
.weak __irq_tim7
|
||||
.globl __irq_tim7
|
||||
.set __irq_tim7, __default_handler
|
||||
.weak __irq_dma2_channel1
|
||||
.globl __irq_dma2_channel1
|
||||
.set __irq_dma2_channel1, __default_handler
|
||||
.weak __irq_dma2_channel2
|
||||
.globl __irq_dma2_channel2
|
||||
.set __irq_dma2_channel2, __default_handler
|
||||
.weak __irq_dma2_channel3
|
||||
.globl __irq_dma2_channel3
|
||||
.set __irq_dma2_channel3, __default_handler
|
||||
.weak __irq_dma2_channel4_5
|
||||
.globl __irq_dma2_channel4_5
|
||||
.set __irq_dma2_channel4_5, __default_handler
|
||||
#endif /* STM32_HIGH_DENSITY */
|
|
@ -0,0 +1,118 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2011 Perry Hung.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/* STM32F1 performance line vector table */
|
||||
|
||||
#include <libmaple/stm32.h>
|
||||
|
||||
.section ".stm32.interrupt_vector"
|
||||
|
||||
.globl __stm32_vector_table
|
||||
.type __stm32_vector_table, %object
|
||||
|
||||
__stm32_vector_table:
|
||||
/* CM3 core interrupts */
|
||||
.long __msp_init
|
||||
.long __exc_reset
|
||||
.long __exc_nmi
|
||||
.long __exc_hardfault
|
||||
.long __exc_memmanage
|
||||
.long __exc_busfault
|
||||
.long __exc_usagefault
|
||||
.long __stm32reservedexception7
|
||||
.long __stm32reservedexception8
|
||||
.long __stm32reservedexception9
|
||||
.long __stm32reservedexception10
|
||||
.long __exc_svc
|
||||
.long __exc_debug_monitor
|
||||
.long __stm32reservedexception13
|
||||
.long __exc_pendsv
|
||||
.long __exc_systick
|
||||
/* Peripheral interrupts */
|
||||
.long __irq_wwdg
|
||||
.long __irq_pvd
|
||||
.long __irq_tamper
|
||||
.long __irq_rtc
|
||||
.long __irq_flash
|
||||
.long __irq_rcc
|
||||
.long __irq_exti0
|
||||
.long __irq_exti1
|
||||
.long __irq_exti2
|
||||
.long __irq_exti3
|
||||
.long __irq_exti4
|
||||
.long __irq_dma1_channel1
|
||||
.long __irq_dma1_channel2
|
||||
.long __irq_dma1_channel3
|
||||
.long __irq_dma1_channel4
|
||||
.long __irq_dma1_channel5
|
||||
.long __irq_dma1_channel6
|
||||
.long __irq_dma1_channel7
|
||||
.long __irq_adc
|
||||
.long __irq_usb_hp_can_tx
|
||||
.long __irq_usb_lp_can_rx0
|
||||
.long __irq_can_rx1
|
||||
.long __irq_can_sce
|
||||
.long __irq_exti9_5
|
||||
.long __irq_tim1_brk
|
||||
.long __irq_tim1_up
|
||||
.long __irq_tim1_trg_com
|
||||
.long __irq_tim1_cc
|
||||
.long __irq_tim2
|
||||
.long __irq_tim3
|
||||
.long __irq_tim4
|
||||
.long __irq_i2c1_ev
|
||||
.long __irq_i2c1_er
|
||||
.long __irq_i2c2_ev
|
||||
.long __irq_i2c2_er
|
||||
.long __irq_spi1
|
||||
.long __irq_spi2
|
||||
.long __irq_usart1
|
||||
.long __irq_usart2
|
||||
.long __irq_usart3
|
||||
.long __irq_exti15_10
|
||||
.long __irq_rtcalarm
|
||||
.long __irq_usbwakeup
|
||||
#if defined (STM32_HIGH_DENSITY)
|
||||
.long __irq_tim8_brk
|
||||
.long __irq_tim8_up
|
||||
.long __irq_tim8_trg_com
|
||||
.long __irq_tim8_cc
|
||||
.long __irq_adc3
|
||||
.long __irq_fsmc
|
||||
.long __irq_sdio
|
||||
.long __irq_tim5
|
||||
.long __irq_spi3
|
||||
.long __irq_uart4
|
||||
.long __irq_uart5
|
||||
.long __irq_tim6
|
||||
.long __irq_tim7
|
||||
.long __irq_dma2_channel1
|
||||
.long __irq_dma2_channel2
|
||||
.long __irq_dma2_channel3
|
||||
.long __irq_dma2_channel4_5
|
||||
#endif /* STM32_HIGH_DENSITY */
|
||||
|
||||
.size __stm32_vector_table, . - __stm32_vector_table
|
|
@ -0,0 +1,88 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Perry Hung.
|
||||
* Copyright (c) 2010, 2011 LeafLabs, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/systick.c
|
||||
* @brief System timer (SysTick).
|
||||
*/
|
||||
|
||||
#include <libmaple/systick.h>
|
||||
|
||||
volatile uint32 systick_uptime_millis;
|
||||
static void (*systick_user_callback)(void);
|
||||
|
||||
/**
|
||||
* @brief Initialize and enable SysTick.
|
||||
*
|
||||
* Clocks the system timer with the core clock, turns it on, and
|
||||
* enables interrupts.
|
||||
*
|
||||
* @param reload_val Appropriate reload counter to tick every 1 ms.
|
||||
*/
|
||||
void systick_init(uint32 reload_val) {
|
||||
SYSTICK_BASE->RVR = reload_val;
|
||||
systick_enable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clock the system timer with the core clock, but don't turn it
|
||||
* on or enable interrupt.
|
||||
*/
|
||||
void systick_disable() {
|
||||
SYSTICK_BASE->CSR = SYSTICK_CSR_CLKSOURCE_CORE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clock the system timer with the core clock and turn it on;
|
||||
* interrupt every 1 ms, for systick_timer_millis.
|
||||
*/
|
||||
void systick_enable() {
|
||||
/* re-enables init registers without changing reload val */
|
||||
SYSTICK_BASE->CSR = (SYSTICK_CSR_CLKSOURCE_CORE |
|
||||
SYSTICK_CSR_ENABLE |
|
||||
SYSTICK_CSR_TICKINT_PEND);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Attach a callback to be called from the SysTick exception handler.
|
||||
*
|
||||
* To detach a callback, call this function again with a null argument.
|
||||
*/
|
||||
void systick_attach_callback(void (*callback)(void)) {
|
||||
systick_user_callback = callback;
|
||||
}
|
||||
|
||||
/*
|
||||
* SysTick ISR
|
||||
*/
|
||||
|
||||
void __exc_systick(void) {
|
||||
systick_uptime_millis++;
|
||||
if (systick_user_callback) {
|
||||
systick_user_callback();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,412 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2011 LeafLabs, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/timer.c
|
||||
* @author Marti Bolivar <mbolivar@leaflabs.com>
|
||||
* @brief Portable timer routines.
|
||||
*/
|
||||
|
||||
#include <libmaple/timer.h>
|
||||
#include <libmaple/stm32.h>
|
||||
#include "timer_private.h"
|
||||
|
||||
static void disable_channel(timer_dev *dev, uint8 channel);
|
||||
static void pwm_mode(timer_dev *dev, uint8 channel);
|
||||
static void output_compare_mode(timer_dev *dev, uint8 channel);
|
||||
|
||||
static inline void enable_irq(timer_dev *dev, timer_interrupt_id iid);
|
||||
|
||||
/*
|
||||
* Devices
|
||||
*
|
||||
* Defer to the timer_private API for declaring these.
|
||||
*/
|
||||
|
||||
#if STM32_HAVE_TIMER(1)
|
||||
static timer_dev timer1 = ADVANCED_TIMER(1);
|
||||
/** Timer 1 device (advanced) */
|
||||
timer_dev *TIMER1 = &timer1;
|
||||
#endif
|
||||
#if STM32_HAVE_TIMER(2)
|
||||
static timer_dev timer2 = GENERAL_TIMER(2);
|
||||
/** Timer 2 device (general-purpose) */
|
||||
timer_dev *TIMER2 = &timer2;
|
||||
#endif
|
||||
#if STM32_HAVE_TIMER(3)
|
||||
static timer_dev timer3 = GENERAL_TIMER(3);
|
||||
/** Timer 3 device (general-purpose) */
|
||||
timer_dev *TIMER3 = &timer3;
|
||||
#endif
|
||||
#if STM32_HAVE_TIMER(4)
|
||||
static timer_dev timer4 = GENERAL_TIMER(4);
|
||||
/** Timer 4 device (general-purpose) */
|
||||
timer_dev *TIMER4 = &timer4;
|
||||
#endif
|
||||
#if STM32_HAVE_TIMER(5)
|
||||
static timer_dev timer5 = GENERAL_TIMER(5);
|
||||
/** Timer 5 device (general-purpose) */
|
||||
timer_dev *TIMER5 = &timer5;
|
||||
#endif
|
||||
#if STM32_HAVE_TIMER(6)
|
||||
static timer_dev timer6 = BASIC_TIMER(6);
|
||||
/** Timer 6 device (basic) */
|
||||
timer_dev *TIMER6 = &timer6;
|
||||
#endif
|
||||
#if STM32_HAVE_TIMER(7)
|
||||
static timer_dev timer7 = BASIC_TIMER(7);
|
||||
/** Timer 7 device (basic) */
|
||||
timer_dev *TIMER7 = &timer7;
|
||||
#endif
|
||||
#if STM32_HAVE_TIMER(8)
|
||||
static timer_dev timer8 = ADVANCED_TIMER(8);
|
||||
/** Timer 8 device (advanced) */
|
||||
timer_dev *TIMER8 = &timer8;
|
||||
#endif
|
||||
#if STM32_HAVE_TIMER(9)
|
||||
static timer_dev timer9 = RESTRICTED_GENERAL_TIMER(9, TIMER_DIER_TIE_BIT);
|
||||
/** Timer 9 device (general-purpose) */
|
||||
timer_dev *TIMER9 = &timer9;
|
||||
#endif
|
||||
#if STM32_HAVE_TIMER(10)
|
||||
static timer_dev timer10 = RESTRICTED_GENERAL_TIMER(10, TIMER_DIER_CC1IE_BIT);
|
||||
/** Timer 10 device (general-purpose) */
|
||||
timer_dev *TIMER10 = &timer10;
|
||||
#endif
|
||||
#if STM32_HAVE_TIMER(11)
|
||||
static timer_dev timer11 = RESTRICTED_GENERAL_TIMER(11, TIMER_DIER_CC1IE_BIT);
|
||||
/** Timer 11 device (general-purpose) */
|
||||
timer_dev *TIMER11 = &timer11;
|
||||
#endif
|
||||
#if STM32_HAVE_TIMER(12)
|
||||
static timer_dev timer12 = RESTRICTED_GENERAL_TIMER(12, TIMER_DIER_TIE_BIT);
|
||||
/** Timer 12 device (general-purpose) */
|
||||
timer_dev *TIMER12 = &timer12;
|
||||
#endif
|
||||
#if STM32_HAVE_TIMER(13)
|
||||
static timer_dev timer13 = RESTRICTED_GENERAL_TIMER(13, TIMER_DIER_CC1IE_BIT);
|
||||
/** Timer 13 device (general-purpose) */
|
||||
timer_dev *TIMER13 = &timer13;
|
||||
#endif
|
||||
#if STM32_HAVE_TIMER(14)
|
||||
static timer_dev timer14 = RESTRICTED_GENERAL_TIMER(14, TIMER_DIER_CC1IE_BIT);
|
||||
/** Timer 14 device (general-purpose) */
|
||||
timer_dev *TIMER14 = &timer14;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Routines
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Call a function on timer devices.
|
||||
* @param fn Function to call on each timer device.
|
||||
*/
|
||||
void timer_foreach(void (*fn)(timer_dev*)) {
|
||||
#if STM32_HAVE_TIMER(1)
|
||||
fn(TIMER1);
|
||||
#endif
|
||||
#if STM32_HAVE_TIMER(2)
|
||||
fn(TIMER2);
|
||||
#endif
|
||||
#if STM32_HAVE_TIMER(3)
|
||||
fn(TIMER3);
|
||||
#endif
|
||||
#if STM32_HAVE_TIMER(4)
|
||||
fn(TIMER4);
|
||||
#endif
|
||||
#if STM32_HAVE_TIMER(5)
|
||||
fn(TIMER5);
|
||||
#endif
|
||||
#if STM32_HAVE_TIMER(6)
|
||||
fn(TIMER6);
|
||||
#endif
|
||||
#if STM32_HAVE_TIMER(7)
|
||||
fn(TIMER7);
|
||||
#endif
|
||||
#if STM32_HAVE_TIMER(8)
|
||||
fn(TIMER8);
|
||||
#endif
|
||||
#if STM32_HAVE_TIMER(9)
|
||||
fn(TIMER9);
|
||||
#endif
|
||||
#if STM32_HAVE_TIMER(10)
|
||||
fn(TIMER10);
|
||||
#endif
|
||||
#if STM32_HAVE_TIMER(11)
|
||||
fn(TIMER11);
|
||||
#endif
|
||||
#if STM32_HAVE_TIMER(12)
|
||||
fn(TIMER12);
|
||||
#endif
|
||||
#if STM32_HAVE_TIMER(13)
|
||||
fn(TIMER13);
|
||||
#endif
|
||||
#if STM32_HAVE_TIMER(14)
|
||||
fn(TIMER14);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a timer, and reset its register map.
|
||||
* @param dev Timer to initialize
|
||||
*/
|
||||
void timer_init(timer_dev *dev) {
|
||||
rcc_clk_enable(dev->clk_id);
|
||||
rcc_reset_dev(dev->clk_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disable a timer.
|
||||
*
|
||||
* The timer will stop counting, all DMA requests and interrupts will
|
||||
* be disabled, and no state changes will be output.
|
||||
*
|
||||
* @param dev Timer to disable.
|
||||
*/
|
||||
void timer_disable(timer_dev *dev) {
|
||||
(dev->regs).bas->CR1 = 0;
|
||||
(dev->regs).bas->DIER = 0;
|
||||
switch (dev->type) {
|
||||
case TIMER_ADVANCED: /* fall-through */
|
||||
case TIMER_GENERAL:
|
||||
(dev->regs).gen->CCER = 0;
|
||||
break;
|
||||
case TIMER_BASIC:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the mode of an individual timer channel.
|
||||
*
|
||||
* Note that not all timers can be configured in every mode. For
|
||||
* example, basic timers cannot be configured to output compare mode.
|
||||
* Be sure to use a timer which is appropriate for the mode you want.
|
||||
*
|
||||
* @param dev Timer whose channel mode to set
|
||||
* @param channel Relevant channel
|
||||
* @param mode New timer mode for channel
|
||||
*/
|
||||
void timer_set_mode(timer_dev *dev, uint8 channel, timer_mode mode) {
|
||||
ASSERT_FAULT(channel > 0 && channel <= 4);
|
||||
|
||||
/* TODO decide about the basic timers */
|
||||
ASSERT(dev->type != TIMER_BASIC);
|
||||
if (dev->type == TIMER_BASIC)
|
||||
return;
|
||||
|
||||
switch (mode) {
|
||||
case TIMER_DISABLED:
|
||||
disable_channel(dev, channel);
|
||||
break;
|
||||
case TIMER_PWM:
|
||||
pwm_mode(dev, channel);
|
||||
break;
|
||||
case TIMER_OUTPUT_COMPARE:
|
||||
output_compare_mode(dev, channel);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Determine whether a timer has a particular capture/compare channel.
|
||||
*
|
||||
* Different timers have different numbers of capture/compare channels
|
||||
* (and some have none at all). Use this function to test whether a
|
||||
* given timer/channel combination will work.
|
||||
*
|
||||
* @param dev Timer device
|
||||
* @param channel Capture/compare channel, from 1 to 4
|
||||
* @return Nonzero if dev has channel, zero otherwise.
|
||||
*/
|
||||
int timer_has_cc_channel(timer_dev *dev, uint8 channel) {
|
||||
/* On all currently supported series: advanced and "full-featured"
|
||||
* general purpose timers have all four channels. Of the
|
||||
* restricted general timers, timers 9 and 12 have channels 1 and
|
||||
* 2; the others have channel 1 only. Basic timers have none. */
|
||||
rcc_clk_id id = dev->clk_id;
|
||||
ASSERT((1 <= channel) && (channel <= 4));
|
||||
if (id <= RCC_TIMER5 || id == RCC_TIMER8) {
|
||||
return 1; /* 1 and 8 are advanced, 2-5 are "full" general */
|
||||
} else if (id <= RCC_TIMER7) {
|
||||
return 0; /* 6 and 7 are basic */
|
||||
}
|
||||
/* The rest are restricted general. */
|
||||
return (((id == RCC_TIMER9 || id == RCC_TIMER12) && channel <= 2) ||
|
||||
channel == 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Attach a timer interrupt.
|
||||
* @param dev Timer device
|
||||
* @param interrupt Interrupt number to attach to; this may be any
|
||||
* timer_interrupt_id or timer_channel value appropriate
|
||||
* for the timer.
|
||||
* @param handler Handler to attach to the given interrupt.
|
||||
* @see timer_interrupt_id
|
||||
* @see timer_channel
|
||||
*/
|
||||
void timer_attach_interrupt(timer_dev *dev,
|
||||
uint8 interrupt,
|
||||
voidFuncPtr handler) {
|
||||
dev->handlers[interrupt] = handler;
|
||||
timer_enable_irq(dev, interrupt);
|
||||
enable_irq(dev, interrupt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Detach a timer interrupt.
|
||||
* @param dev Timer device
|
||||
* @param interrupt Interrupt number to detach; this may be any
|
||||
* timer_interrupt_id or timer_channel value appropriate
|
||||
* for the timer.
|
||||
* @see timer_interrupt_id
|
||||
* @see timer_channel
|
||||
*/
|
||||
void timer_detach_interrupt(timer_dev *dev, uint8 interrupt) {
|
||||
timer_disable_irq(dev, interrupt);
|
||||
dev->handlers[interrupt] = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Utilities
|
||||
*/
|
||||
|
||||
static void disable_channel(timer_dev *dev, uint8 channel) {
|
||||
timer_detach_interrupt(dev, channel);
|
||||
timer_cc_disable(dev, channel);
|
||||
}
|
||||
|
||||
static void pwm_mode(timer_dev *dev, uint8 channel) {
|
||||
timer_disable_irq(dev, channel);
|
||||
timer_oc_set_mode(dev, channel, TIMER_OC_MODE_PWM_1, TIMER_OC_PE);
|
||||
timer_cc_enable(dev, channel);
|
||||
}
|
||||
|
||||
static void output_compare_mode(timer_dev *dev, uint8 channel) {
|
||||
timer_oc_set_mode(dev, channel, TIMER_OC_MODE_ACTIVE_ON_MATCH, 0);
|
||||
timer_cc_enable(dev, channel);
|
||||
}
|
||||
|
||||
static void enable_adv_irq(timer_dev *dev, timer_interrupt_id id);
|
||||
static void enable_bas_gen_irq(timer_dev *dev);
|
||||
|
||||
static inline void enable_irq(timer_dev *dev, timer_interrupt_id iid) {
|
||||
if (dev->type == TIMER_ADVANCED) {
|
||||
enable_adv_irq(dev, iid);
|
||||
} else {
|
||||
enable_bas_gen_irq(dev);
|
||||
}
|
||||
}
|
||||
|
||||
/* Advanced control timers have several IRQ lines corresponding to
|
||||
* different timer interrupts.
|
||||
*
|
||||
* Note: This function assumes that the only advanced timers are TIM1
|
||||
* and TIM8, and needs the obvious changes if that assumption is
|
||||
* violated by a later STM32 series. */
|
||||
static void enable_adv_irq(timer_dev *dev, timer_interrupt_id id) {
|
||||
uint8 is_tim1 = dev->clk_id == RCC_TIMER1;
|
||||
nvic_irq_num irq_num;
|
||||
switch (id) {
|
||||
case TIMER_UPDATE_INTERRUPT:
|
||||
irq_num = (is_tim1 ?
|
||||
NVIC_TIMER1_UP_TIMER10 :
|
||||
NVIC_TIMER8_UP_TIMER13);
|
||||
break;
|
||||
case TIMER_CC1_INTERRUPT: /* Fall through */
|
||||
case TIMER_CC2_INTERRUPT: /* ... */
|
||||
case TIMER_CC3_INTERRUPT: /* ... */
|
||||
case TIMER_CC4_INTERRUPT:
|
||||
irq_num = is_tim1 ? NVIC_TIMER1_CC : NVIC_TIMER8_CC;
|
||||
break;
|
||||
case TIMER_COM_INTERRUPT: /* Fall through */
|
||||
case TIMER_TRG_INTERRUPT:
|
||||
irq_num = (is_tim1 ?
|
||||
NVIC_TIMER1_TRG_COM_TIMER11 :
|
||||
NVIC_TIMER8_TRG_COM_TIMER14);
|
||||
break;
|
||||
case TIMER_BREAK_INTERRUPT:
|
||||
irq_num = (is_tim1 ?
|
||||
NVIC_TIMER1_BRK_TIMER9 :
|
||||
NVIC_TIMER8_BRK_TIMER12);
|
||||
break;
|
||||
default:
|
||||
/* Can't happen, but placate the compiler */
|
||||
ASSERT(0);
|
||||
return;
|
||||
}
|
||||
nvic_irq_enable(irq_num);
|
||||
}
|
||||
|
||||
/* Basic and general purpose timers have a single IRQ line, which is
|
||||
* shared by all interrupts supported by a particular timer. */
|
||||
static void enable_bas_gen_irq(timer_dev *dev) {
|
||||
nvic_irq_num irq_num;
|
||||
switch (dev->clk_id) {
|
||||
case RCC_TIMER2:
|
||||
irq_num = NVIC_TIMER2;
|
||||
break;
|
||||
case RCC_TIMER3:
|
||||
irq_num = NVIC_TIMER3;
|
||||
break;
|
||||
case RCC_TIMER4:
|
||||
irq_num = NVIC_TIMER4;
|
||||
break;
|
||||
case RCC_TIMER5:
|
||||
irq_num = NVIC_TIMER5;
|
||||
break;
|
||||
case RCC_TIMER6:
|
||||
irq_num = NVIC_TIMER6;
|
||||
break;
|
||||
case RCC_TIMER7:
|
||||
irq_num = NVIC_TIMER7;
|
||||
break;
|
||||
case RCC_TIMER9:
|
||||
irq_num = NVIC_TIMER1_BRK_TIMER9;
|
||||
break;
|
||||
case RCC_TIMER10:
|
||||
irq_num = NVIC_TIMER1_UP_TIMER10;
|
||||
break;
|
||||
case RCC_TIMER11:
|
||||
irq_num = NVIC_TIMER1_TRG_COM_TIMER11;
|
||||
break;
|
||||
case RCC_TIMER12:
|
||||
irq_num = NVIC_TIMER8_BRK_TIMER12;
|
||||
break;
|
||||
case RCC_TIMER13:
|
||||
irq_num = NVIC_TIMER8_UP_TIMER13;
|
||||
break;
|
||||
case RCC_TIMER14:
|
||||
irq_num = NVIC_TIMER8_TRG_COM_TIMER14;
|
||||
break;
|
||||
default:
|
||||
ASSERT_FAULT(0);
|
||||
return;
|
||||
}
|
||||
nvic_irq_enable(irq_num);
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2011, 2012 LeafLabs, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/stm32f1/timer.c
|
||||
* @author Marti Bolivar <mbolivar@leaflabs.com>
|
||||
* @brief STM32F1 timer.
|
||||
*/
|
||||
|
||||
#include <libmaple/timer.h>
|
||||
#include <libmaple/stm32.h>
|
||||
#include "timer_private.h"
|
||||
|
||||
/*
|
||||
* IRQ handlers
|
||||
*
|
||||
* Defer to the timer_private dispatch API.
|
||||
*
|
||||
* FIXME: The names of these handlers are inaccurate since XL-density
|
||||
* devices came out. Update these to match the STM32F2 names, maybe
|
||||
* using some weak symbol magic to preserve backwards compatibility if
|
||||
* possible. Once that's done, we can just move the IRQ handlers into
|
||||
* the top-level libmaple/timer.c, and there will be no need for this
|
||||
* file.
|
||||
*/
|
||||
|
||||
void __irq_tim1_brk(void) {
|
||||
dispatch_adv_brk(TIMER1);
|
||||
#if STM32_HAVE_TIMER(9)
|
||||
dispatch_tim_9_12(TIMER9);
|
||||
#endif
|
||||
}
|
||||
|
||||
void __irq_tim1_up(void) {
|
||||
dispatch_adv_up(TIMER1);
|
||||
#if STM32_HAVE_TIMER(10)
|
||||
dispatch_tim_10_11_13_14(TIMER10);
|
||||
#endif
|
||||
}
|
||||
|
||||
void __irq_tim1_trg_com(void) {
|
||||
dispatch_adv_trg_com(TIMER1);
|
||||
#if STM32_HAVE_TIMER(11)
|
||||
dispatch_tim_10_11_13_14(TIMER11);
|
||||
#endif
|
||||
}
|
||||
|
||||
void __irq_tim1_cc(void) {
|
||||
dispatch_adv_cc(TIMER1);
|
||||
}
|
||||
|
||||
void __irq_tim2(void) {
|
||||
dispatch_general(TIMER2);
|
||||
}
|
||||
|
||||
void __irq_tim3(void) {
|
||||
dispatch_general(TIMER3);
|
||||
}
|
||||
|
||||
void __irq_tim4(void) {
|
||||
dispatch_general(TIMER4);
|
||||
}
|
||||
|
||||
#if defined(STM32_HIGH_DENSITY) || defined(STM32_XL_DENSITY)
|
||||
void __irq_tim5(void) {
|
||||
dispatch_general(TIMER5);
|
||||
}
|
||||
|
||||
void __irq_tim6(void) {
|
||||
dispatch_basic(TIMER6);
|
||||
}
|
||||
|
||||
void __irq_tim7(void) {
|
||||
dispatch_basic(TIMER7);
|
||||
}
|
||||
|
||||
void __irq_tim8_brk(void) {
|
||||
dispatch_adv_brk(TIMER8);
|
||||
#if STM32_HAVE_TIMER(12)
|
||||
dispatch_tim_9_12(TIMER12);
|
||||
#endif
|
||||
}
|
||||
|
||||
void __irq_tim8_up(void) {
|
||||
dispatch_adv_up(TIMER8);
|
||||
#if STM32_HAVE_TIMER(13)
|
||||
dispatch_tim_10_11_13_14(TIMER13);
|
||||
#endif
|
||||
}
|
||||
|
||||
void __irq_tim8_trg_com(void) {
|
||||
dispatch_adv_trg_com(TIMER8);
|
||||
#if STM32_HAVE_TIMER(14)
|
||||
dispatch_tim_10_11_13_14(TIMER14);
|
||||
#endif
|
||||
}
|
||||
|
||||
void __irq_tim8_cc(void) {
|
||||
dispatch_adv_cc(TIMER8);
|
||||
}
|
||||
#endif /* defined(STM32_HIGH_DENSITY) || defined(STM32_XL_DENSITY) */
|
|
@ -0,0 +1,138 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Perry Hung.
|
||||
* Copyright (c) 2011 LeafLabs, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/usart.c
|
||||
* @author Marti Bolivar <mbolivar@leaflabs.com>,
|
||||
* Perry Hung <perry@leaflabs.com>
|
||||
* @brief Portable USART routines
|
||||
*/
|
||||
|
||||
#include <libmaple/usart.h>
|
||||
|
||||
/**
|
||||
* @brief Initialize a serial port.
|
||||
* @param dev Serial port to be initialized
|
||||
*/
|
||||
void usart_init(usart_dev *dev) {
|
||||
rb_init(dev->rb, USART_RX_BUF_SIZE, dev->rx_buf);
|
||||
rcc_clk_enable(dev->clk_id);
|
||||
nvic_irq_enable(dev->irq_num);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable a serial port.
|
||||
*
|
||||
* USART is enabled in single buffer transmission mode, multibuffer
|
||||
* receiver mode, 8n1.
|
||||
*
|
||||
* Serial port must have a baud rate configured to work properly.
|
||||
*
|
||||
* @param dev Serial port to enable.
|
||||
* @see usart_set_baud_rate()
|
||||
*/
|
||||
void usart_enable(usart_dev *dev) {
|
||||
usart_reg_map *regs = dev->regs;
|
||||
regs->CR1 = (USART_CR1_TE | USART_CR1_RE | USART_CR1_RXNEIE |
|
||||
USART_CR1_M_8N1);
|
||||
regs->CR1 |= USART_CR1_UE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Turn off a serial port.
|
||||
* @param dev Serial port to be disabled
|
||||
*/
|
||||
void usart_disable(usart_dev *dev) {
|
||||
/* FIXME this misbehaves (on F1) if you try to use PWM on TX afterwards */
|
||||
usart_reg_map *regs = dev->regs;
|
||||
|
||||
/* TC bit must be high before disabling the USART */
|
||||
while((regs->CR1 & USART_CR1_UE) && !(regs->SR & USART_SR_TC))
|
||||
;
|
||||
|
||||
/* Disable UE */
|
||||
regs->CR1 &= ~USART_CR1_UE;
|
||||
|
||||
/* Clean up buffer */
|
||||
usart_reset_rx(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Nonblocking USART transmit
|
||||
* @param dev Serial port to transmit over
|
||||
* @param buf Buffer to transmit
|
||||
* @param len Maximum number of bytes to transmit
|
||||
* @return Number of bytes transmitted
|
||||
*/
|
||||
uint32 usart_tx(usart_dev *dev, const uint8 *buf, uint32 len) {
|
||||
usart_reg_map *regs = dev->regs;
|
||||
uint32 txed = 0;
|
||||
while ((regs->SR & USART_SR_TXE) && (txed < len)) {
|
||||
regs->DR = buf[txed++];
|
||||
}
|
||||
return txed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Nonblocking USART receive.
|
||||
* @param dev Serial port to receive bytes from
|
||||
* @param buf Buffer to store received bytes into
|
||||
* @param len Maximum number of bytes to store
|
||||
* @return Number of bytes received
|
||||
*/
|
||||
uint32 usart_rx(usart_dev *dev, uint8 *buf, uint32 len) {
|
||||
uint32 rxed = 0;
|
||||
while (usart_data_available(dev) && rxed < len) {
|
||||
*buf++ = usart_getc(dev);
|
||||
rxed++;
|
||||
}
|
||||
return rxed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Transmit an unsigned integer to the specified serial port in
|
||||
* decimal format.
|
||||
*
|
||||
* This function blocks until the integer's digits have been
|
||||
* completely transmitted.
|
||||
*
|
||||
* @param dev Serial port to send on
|
||||
* @param val Number to print
|
||||
*/
|
||||
void usart_putudec(usart_dev *dev, uint32 val) {
|
||||
char digits[12];
|
||||
int i = 0;
|
||||
|
||||
do {
|
||||
digits[i++] = val % 10 + '0';
|
||||
val /= 10;
|
||||
} while (val > 0);
|
||||
|
||||
while (--i >= 0) {
|
||||
usart_putc(dev, digits[i]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,170 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2012 LeafLabs, LLC.
|
||||
* Copyright (c) 2010 Perry Hung.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/stm32f1/usart.c
|
||||
* @author Marti Bolivar <mbolivar@leaflabs.com>,
|
||||
* Perry Hung <perry@leaflabs.com>
|
||||
* @brief STM32F1 USART.
|
||||
*/
|
||||
|
||||
#include <libmaple/usart.h>
|
||||
#include <libmaple/gpio.h>
|
||||
#include "usart_private.h"
|
||||
|
||||
/*
|
||||
* Devices
|
||||
*/
|
||||
|
||||
static ring_buffer usart1_rb;
|
||||
static usart_dev usart1 = {
|
||||
.regs = USART1_BASE,
|
||||
.rb = &usart1_rb,
|
||||
.max_baud = 4500000UL,
|
||||
.clk_id = RCC_USART1,
|
||||
.irq_num = NVIC_USART1,
|
||||
};
|
||||
/** USART1 device */
|
||||
usart_dev *USART1 = &usart1;
|
||||
|
||||
static ring_buffer usart2_rb;
|
||||
static usart_dev usart2 = {
|
||||
.regs = USART2_BASE,
|
||||
.rb = &usart2_rb,
|
||||
.max_baud = 2250000UL,
|
||||
.clk_id = RCC_USART2,
|
||||
.irq_num = NVIC_USART2,
|
||||
};
|
||||
/** USART2 device */
|
||||
usart_dev *USART2 = &usart2;
|
||||
|
||||
static ring_buffer usart3_rb;
|
||||
static usart_dev usart3 = {
|
||||
.regs = USART3_BASE,
|
||||
.rb = &usart3_rb,
|
||||
.max_baud = 2250000UL,
|
||||
.clk_id = RCC_USART3,
|
||||
.irq_num = NVIC_USART3,
|
||||
};
|
||||
/** USART3 device */
|
||||
usart_dev *USART3 = &usart3;
|
||||
|
||||
#if defined(STM32_HIGH_DENSITY) || defined(STM32_XL_DENSITY)
|
||||
static ring_buffer uart4_rb;
|
||||
static usart_dev uart4 = {
|
||||
.regs = UART4_BASE,
|
||||
.rb = &uart4_rb,
|
||||
.max_baud = 2250000UL,
|
||||
.clk_id = RCC_UART4,
|
||||
.irq_num = NVIC_UART4,
|
||||
};
|
||||
/** UART4 device */
|
||||
usart_dev *UART4 = &uart4;
|
||||
|
||||
static ring_buffer uart5_rb;
|
||||
static usart_dev uart5 = {
|
||||
.regs = UART5_BASE,
|
||||
.rb = &uart5_rb,
|
||||
.max_baud = 2250000UL,
|
||||
.clk_id = RCC_UART5,
|
||||
.irq_num = NVIC_UART5,
|
||||
};
|
||||
/** UART5 device */
|
||||
usart_dev *UART5 = &uart5;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Routines
|
||||
*/
|
||||
|
||||
void usart_config_gpios_async(usart_dev *udev,
|
||||
gpio_dev *rx_dev, uint8 rx,
|
||||
gpio_dev *tx_dev, uint8 tx,
|
||||
unsigned flags) {
|
||||
gpio_set_mode(rx_dev, rx, GPIO_INPUT_FLOATING);
|
||||
gpio_set_mode(tx_dev, tx, GPIO_AF_OUTPUT_PP);
|
||||
}
|
||||
|
||||
void usart_set_baud_rate(usart_dev *dev, uint32 clock_speed, uint32 baud) {
|
||||
uint32 integer_part;
|
||||
uint32 fractional_part;
|
||||
uint32 tmp;
|
||||
|
||||
/* Figure out the clock speed, if the user doesn't give one. */
|
||||
if (clock_speed == 0) {
|
||||
clock_speed = _usart_clock_freq(dev);
|
||||
}
|
||||
ASSERT(clock_speed);
|
||||
|
||||
/* Convert desired baud rate to baud rate register setting. */
|
||||
integer_part = (25 * clock_speed) / (4 * baud);
|
||||
tmp = (integer_part / 100) << 4;
|
||||
fractional_part = integer_part - (100 * (tmp >> 4));
|
||||
tmp |= (((fractional_part * 16) + 50) / 100) & ((uint8)0x0F);
|
||||
|
||||
dev->regs->BRR = (uint16)tmp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Call a function on each USART.
|
||||
* @param fn Function to call.
|
||||
*/
|
||||
void usart_foreach(void (*fn)(usart_dev*)) {
|
||||
fn(USART1);
|
||||
fn(USART2);
|
||||
fn(USART3);
|
||||
#ifdef STM32_HIGH_DENSITY
|
||||
fn(UART4);
|
||||
fn(UART5);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Interrupt handlers.
|
||||
*/
|
||||
|
||||
void __irq_usart1(void) {
|
||||
usart_irq(&usart1_rb, USART1_BASE);
|
||||
}
|
||||
|
||||
void __irq_usart2(void) {
|
||||
usart_irq(&usart2_rb, USART2_BASE);
|
||||
}
|
||||
|
||||
void __irq_usart3(void) {
|
||||
usart_irq(&usart3_rb, USART3_BASE);
|
||||
}
|
||||
|
||||
#ifdef STM32_HIGH_DENSITY
|
||||
void __irq_uart4(void) {
|
||||
usart_irq(&uart4_rb, UART4_BASE);
|
||||
}
|
||||
|
||||
void __irq_uart5(void) {
|
||||
usart_irq(&uart5_rb, UART5_BASE);
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,41 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2012 LeafLabs, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/usart_private.c
|
||||
* @author Marti Bolivar <mbolivar@leaflabs.com>
|
||||
* @brief Private USART routines.
|
||||
*/
|
||||
|
||||
#include "usart_private.h"
|
||||
#include <libmaple/rcc.h>
|
||||
#include <libmaple/stm32.h>
|
||||
|
||||
uint32 _usart_clock_freq(usart_dev *dev) {
|
||||
rcc_clk_domain domain = rcc_dev_clk(dev->clk_id);
|
||||
return (domain == RCC_APB1 ? STM32_PCLK1 :
|
||||
(domain == RCC_APB2 ? STM32_PCLK2 : 0));
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
The USB submodule of libmaple is a separate piece of the codebase for
|
||||
reasons that are largely historical.
|
||||
|
||||
Current Status:
|
||||
|
||||
There's only support for the USB device peripheral found on
|
||||
STM32F103s.
|
||||
|
||||
We rely on the low level core library provided by ST to implement
|
||||
the USB transfer protocol for control endpoint transfers.
|
||||
|
||||
The virtual com port (which is exposed via
|
||||
<libmaple/usb_cdcacm.h>) serves two important purposes.
|
||||
|
||||
1) It allows serial data transfers between user sketches an a
|
||||
host computer.
|
||||
|
||||
2) It allows the host PC to issue a system reset into the DFU
|
||||
bootloader with the DTR + RTS + "1EAF" sequence (see
|
||||
leaflabs.com/docs/bootloader.html for more information on
|
||||
this).
|
||||
|
||||
After reset, Maple will run the DFU bootloader for a few seconds,
|
||||
during which the user can begin a DFU upload operation (uploads
|
||||
application binary into RAM/FLASH). Thus, without this virtual com
|
||||
port, it would be necessary to find an alternative means to reset
|
||||
the chip in order to enable the bootloader.
|
||||
|
||||
If you would like to develop your own USB application for whatever
|
||||
reason (e.g. to use faster isochronous enpoints for streaming
|
||||
audio, or implement the USB HID or Mass Storage specs), then
|
||||
ensure that you leave some hook for resetting Maple remotely in
|
||||
order to spin up the DFU bootloader. Please make sure to get
|
||||
yourself a unique vendor/product ID pair for your application, as
|
||||
some operating systems will assign a host-side driver based on
|
||||
these tags.
|
||||
|
||||
It would be possible to build a compound USB device, that
|
||||
implements endpoints for both the virtual COM port as well as some
|
||||
other components (mass storage etc.). However, this turns out to
|
||||
be a burden from the host driver side, as Windows and *nix handle
|
||||
compound USB devices quite differently.
|
||||
|
||||
Be mindful that enabling the USB peripheral isn't "free." The
|
||||
device must respond to periodic bus activity (every few
|
||||
milliseconds) by servicing an ISR. Therefore, the USB application
|
||||
should be disabled inside of timing critical applications.
|
||||
|
||||
In order to disconnect the device from the host, a USB_DISC pin is
|
||||
asserted (e.g. on Maple, this is PC12). Alternatively, the NVIC
|
||||
can be directly configured to disable the USB LP/HP IRQ's.
|
||||
|
||||
The files inside of usb_lib were provided by ST and are subject to
|
||||
their own license, all other files were written by the LeafLabs
|
||||
team and fall under the MIT license.
|
||||
|
||||
TODO:
|
||||
|
||||
- Generic USB driver core with series-provided backends, like
|
||||
libopencm3 has.
|
||||
- Strip out ST code.
|
||||
- Integration with a high level USB library (like LUFA/MyUSB) to
|
||||
allow users to write custom USB applications.
|
|
@ -0,0 +1,45 @@
|
|||
# Standard things
|
||||
sp := $(sp).x
|
||||
dirstack_$(sp) := $(d)
|
||||
d := $(dir)
|
||||
BUILDDIRS += $(BUILD_PATH)/$(d)
|
||||
|
||||
# Local flags
|
||||
CFLAGS_$(d) = -I$(d) -I$(d)/$(MCU_SERIES) -I$(d)/usb_lib $(LIBMAPLE_INCLUDES) $(LIBMAPLE_PRIVATE_INCLUDES) -Wall
|
||||
|
||||
# Add usblib and series subdirectory to BUILDDIRS.
|
||||
BUILDDIRS += $(BUILD_PATH)/$(d)/$(MCU_SERIES)
|
||||
BUILDDIRS += $(BUILD_PATH)/$(d)/usb_lib
|
||||
|
||||
# Local rules and targets
|
||||
sSRCS_$(d) :=
|
||||
cSRCS_$(d) :=
|
||||
# We currently only have F1 performance line support. Sigh.
|
||||
ifeq ($(MCU_SERIES), stm32f1)
|
||||
ifeq ($(MCU_F1_LINE), performance)
|
||||
cSRCS_$(d) += $(MCU_SERIES)/usb.c
|
||||
cSRCS_$(d) += $(MCU_SERIES)/usb_reg_map.c
|
||||
cSRCS_$(d) += $(MCU_SERIES)/usb_cdcacm.c
|
||||
cSRCS_$(d) += usb_lib/usb_core.c
|
||||
cSRCS_$(d) += usb_lib/usb_init.c
|
||||
cSRCS_$(d) += usb_lib/usb_mem.c
|
||||
cSRCS_$(d) += usb_lib/usb_regs.c
|
||||
endif
|
||||
endif
|
||||
|
||||
sFILES_$(d) := $(sSRCS_$(d):%=$(d)/%)
|
||||
cFILES_$(d) := $(cSRCS_$(d):%=$(d)/%)
|
||||
|
||||
OBJS_$(d) := $(sFILES_$(d):%.S=$(BUILD_PATH)/%.o) \
|
||||
$(cFILES_$(d):%.c=$(BUILD_PATH)/%.o)
|
||||
DEPS_$(d) := $(OBJS_$(d):%.o=%.d)
|
||||
|
||||
$(OBJS_$(d)): TGT_CFLAGS := $(CFLAGS_$(d))
|
||||
$(OBJS_$(d)): TGT_ASFLAGS :=
|
||||
|
||||
TGT_BIN += $(OBJS_$(d))
|
||||
|
||||
# Standard things
|
||||
-include $(DEPS_$(d))
|
||||
d := $(dirstack_$(sp))
|
||||
sp := $(basename $(sp))
|
|
@ -0,0 +1,387 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 LeafLabs LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/usb/stm32f1/usb.c
|
||||
* @brief USB support.
|
||||
*
|
||||
* This is a mess.
|
||||
*/
|
||||
|
||||
#include <libmaple/usb.h>
|
||||
|
||||
#include <libmaple/libmaple.h>
|
||||
#include <libmaple/rcc.h>
|
||||
|
||||
/* Private headers */
|
||||
#include "usb_reg_map.h"
|
||||
#include "usb_lib_globals.h"
|
||||
|
||||
/* usb_lib headers */
|
||||
#include "usb_type.h"
|
||||
#include "usb_core.h"
|
||||
|
||||
static void dispatch_ctr_lp(void);
|
||||
|
||||
/*
|
||||
* usb_lib/ globals
|
||||
*/
|
||||
|
||||
uint16 SaveTState; /* caches TX status for later use */
|
||||
uint16 SaveRState; /* caches RX status for later use */
|
||||
|
||||
/*
|
||||
* Other state
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
RESUME_EXTERNAL,
|
||||
RESUME_INTERNAL,
|
||||
RESUME_LATER,
|
||||
RESUME_WAIT,
|
||||
RESUME_START,
|
||||
RESUME_ON,
|
||||
RESUME_OFF,
|
||||
RESUME_ESOF
|
||||
} RESUME_STATE;
|
||||
|
||||
struct {
|
||||
volatile RESUME_STATE eState;
|
||||
volatile uint8 bESOFcnt;
|
||||
} ResumeS;
|
||||
|
||||
static usblib_dev usblib = {
|
||||
.irq_mask = USB_ISR_MSK,
|
||||
.state = USB_UNCONNECTED,
|
||||
.prevState = USB_UNCONNECTED,
|
||||
.clk_id = RCC_USB,
|
||||
};
|
||||
usblib_dev *USBLIB = &usblib;
|
||||
|
||||
/*
|
||||
* Routines
|
||||
*/
|
||||
|
||||
void usb_init_usblib(usblib_dev *dev,
|
||||
void (**ep_int_in)(void),
|
||||
void (**ep_int_out)(void)) {
|
||||
rcc_clk_enable(dev->clk_id);
|
||||
|
||||
dev->ep_int_in = ep_int_in;
|
||||
dev->ep_int_out = ep_int_out;
|
||||
|
||||
/* usb_lib/ declares both and then assumes that pFoo points to Foo
|
||||
* (even though the names don't always match), which is stupid for
|
||||
* all of the obvious reasons, but whatever. Here we are. */
|
||||
pInformation = &Device_Info;
|
||||
pProperty = &Device_Property;
|
||||
pUser_Standard_Requests = &User_Standard_Requests;
|
||||
|
||||
pInformation->ControlState = 2; /* FIXME [0.0.12] use
|
||||
CONTROL_STATE enumerator */
|
||||
pProperty->Init();
|
||||
}
|
||||
|
||||
static void usb_suspend(void) {
|
||||
uint16 cntr;
|
||||
|
||||
/* TODO decide if read/modify/write is really what we want
|
||||
* (e.g. usb_resume_init() reconfigures CNTR). */
|
||||
cntr = USB_BASE->CNTR;
|
||||
cntr |= USB_CNTR_FSUSP;
|
||||
USB_BASE->CNTR = cntr;
|
||||
cntr |= USB_CNTR_LP_MODE;
|
||||
USB_BASE->CNTR = cntr;
|
||||
|
||||
USBLIB->prevState = USBLIB->state;
|
||||
USBLIB->state = USB_SUSPENDED;
|
||||
}
|
||||
|
||||
static void usb_resume_init(void) {
|
||||
uint16 cntr;
|
||||
|
||||
cntr = USB_BASE->CNTR;
|
||||
cntr &= ~USB_CNTR_LP_MODE;
|
||||
USB_BASE->CNTR = cntr;
|
||||
|
||||
/* Enable interrupt lines */
|
||||
USB_BASE->CNTR = USB_ISR_MSK;
|
||||
}
|
||||
|
||||
static void usb_resume(RESUME_STATE eResumeSetVal) {
|
||||
uint16 cntr;
|
||||
|
||||
if (eResumeSetVal != RESUME_ESOF) {
|
||||
ResumeS.eState = eResumeSetVal;
|
||||
}
|
||||
|
||||
switch (ResumeS.eState) {
|
||||
case RESUME_EXTERNAL:
|
||||
usb_resume_init();
|
||||
ResumeS.eState = RESUME_OFF;
|
||||
USBLIB->state = USBLIB->prevState;
|
||||
break;
|
||||
case RESUME_INTERNAL:
|
||||
usb_resume_init();
|
||||
ResumeS.eState = RESUME_START;
|
||||
break;
|
||||
case RESUME_LATER:
|
||||
ResumeS.bESOFcnt = 2;
|
||||
ResumeS.eState = RESUME_WAIT;
|
||||
break;
|
||||
case RESUME_WAIT:
|
||||
ResumeS.bESOFcnt--;
|
||||
if (ResumeS.bESOFcnt == 0) {
|
||||
ResumeS.eState = RESUME_START;
|
||||
}
|
||||
break;
|
||||
case RESUME_START:
|
||||
cntr = USB_BASE->CNTR;
|
||||
cntr |= USB_CNTR_RESUME;
|
||||
USB_BASE->CNTR = cntr;
|
||||
ResumeS.eState = RESUME_ON;
|
||||
ResumeS.bESOFcnt = 10;
|
||||
break;
|
||||
case RESUME_ON:
|
||||
ResumeS.bESOFcnt--;
|
||||
if (ResumeS.bESOFcnt == 0) {
|
||||
cntr = USB_BASE->CNTR;
|
||||
cntr &= ~USB_CNTR_RESUME;
|
||||
USB_BASE->CNTR = cntr;
|
||||
USBLIB->state = USBLIB->prevState;
|
||||
ResumeS.eState = RESUME_OFF;
|
||||
}
|
||||
break;
|
||||
case RESUME_OFF:
|
||||
case RESUME_ESOF:
|
||||
default:
|
||||
ResumeS.eState = RESUME_OFF;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#define SUSPEND_ENABLED 1
|
||||
void __irq_usb_lp_can_rx0(void) {
|
||||
uint16 istr = USB_BASE->ISTR;
|
||||
|
||||
/* Use USB_ISR_MSK to only include code for bits we care about. */
|
||||
|
||||
#if (USB_ISR_MSK & USB_ISTR_RESET)
|
||||
if (istr & USB_ISTR_RESET & USBLIB->irq_mask) {
|
||||
USB_BASE->ISTR = ~USB_ISTR_RESET;
|
||||
pProperty->Reset();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (USB_ISR_MSK & USB_ISTR_PMAOVR)
|
||||
if (istr & ISTR_PMAOVR & USBLIB->irq_mask) {
|
||||
USB_BASE->ISTR = ~USB_ISTR_PMAOVR;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (USB_ISR_MSK & USB_ISTR_ERR)
|
||||
if (istr & USB_ISTR_ERR & USBLIB->irq_mask) {
|
||||
USB_BASE->ISTR = ~USB_ISTR_ERR;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (USB_ISR_MSK & USB_ISTR_WKUP)
|
||||
if (istr & USB_ISTR_WKUP & USBLIB->irq_mask) {
|
||||
USB_BASE->ISTR = ~USB_ISTR_WKUP;
|
||||
usb_resume(RESUME_EXTERNAL);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (USB_ISR_MSK & USB_ISTR_SUSP)
|
||||
if (istr & USB_ISTR_SUSP & USBLIB->irq_mask) {
|
||||
/* check if SUSPEND is possible */
|
||||
if (SUSPEND_ENABLED) {
|
||||
usb_suspend();
|
||||
} else {
|
||||
/* if not possible then resume after xx ms */
|
||||
usb_resume(RESUME_LATER);
|
||||
}
|
||||
/* clear of the ISTR bit must be done after setting of CNTR_FSUSP */
|
||||
USB_BASE->ISTR = ~USB_ISTR_SUSP;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (USB_ISR_MSK & USB_ISTR_SOF)
|
||||
if (istr & USB_ISTR_SOF & USBLIB->irq_mask) {
|
||||
USB_BASE->ISTR = ~USB_ISTR_SOF;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (USB_ISR_MSK & USB_ISTR_ESOF)
|
||||
if (istr & USB_ISTR_ESOF & USBLIB->irq_mask) {
|
||||
USB_BASE->ISTR = ~USB_ISTR_ESOF;
|
||||
/* resume handling timing is made with ESOFs */
|
||||
usb_resume(RESUME_ESOF); /* request without change of the machine state */
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Service the correct transfer interrupt.
|
||||
*/
|
||||
|
||||
#if (USB_ISR_MSK & USB_ISTR_CTR)
|
||||
if (istr & USB_ISTR_CTR & USBLIB->irq_mask) {
|
||||
dispatch_ctr_lp();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Auxiliary routines
|
||||
*/
|
||||
|
||||
static inline uint8 dispatch_endpt_zero(uint16 istr_dir);
|
||||
static inline void dispatch_endpt(uint8 ep);
|
||||
static inline void set_rx_tx_status0(uint16 rx, uint16 tx);
|
||||
|
||||
static void handle_setup0(void);
|
||||
static void handle_in0(void);
|
||||
static void handle_out0(void);
|
||||
|
||||
static void dispatch_ctr_lp() {
|
||||
uint16 istr;
|
||||
while (((istr = USB_BASE->ISTR) & USB_ISTR_CTR) != 0) {
|
||||
/* TODO WTF, figure this out: RM0008 says CTR is read-only,
|
||||
* but ST's firmware claims it's clear-only, and emphasizes
|
||||
* the importance of clearing it in more than one place. */
|
||||
USB_BASE->ISTR = ~USB_ISTR_CTR;
|
||||
uint8 ep_id = istr & USB_ISTR_EP_ID;
|
||||
if (ep_id == 0) {
|
||||
/* TODO figure out why it's OK to break out of the loop
|
||||
* once we're done serving endpoint zero, but not okay if
|
||||
* there are multiple nonzero endpoint transfers to
|
||||
* handle. */
|
||||
if (dispatch_endpt_zero(istr & USB_ISTR_DIR)) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
dispatch_endpt(ep_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* FIXME Dataflow on endpoint 0 RX/TX status is based off of ST's
|
||||
* code, and is ugly/confusing in its use of SaveRState/SaveTState.
|
||||
* Fixing this requires filling in handle_in0(), handle_setup0(),
|
||||
* handle_out0(). */
|
||||
static inline uint8 dispatch_endpt_zero(uint16 istr_dir) {
|
||||
uint32 epr = (uint16)USB_BASE->EP[0];
|
||||
|
||||
if (!(epr & (USB_EP_CTR_TX | USB_EP_SETUP | USB_EP_CTR_RX))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Cache RX/TX statuses in SaveRState/SaveTState, respectively.
|
||||
* The various handle_foo0() may clobber these values
|
||||
* before we reset them at the end of this routine. */
|
||||
SaveRState = epr & USB_EP_STAT_RX;
|
||||
SaveTState = epr & USB_EP_STAT_TX;
|
||||
|
||||
/* Set actual RX/TX statuses to NAK while we're thinking */
|
||||
set_rx_tx_status0(USB_EP_STAT_RX_NAK, USB_EP_STAT_TX_NAK);
|
||||
|
||||
if (istr_dir == 0) {
|
||||
/* ST RM0008: "If DIR bit=0, CTR_TX bit is set in the USB_EPnR
|
||||
* register related to the interrupting endpoint. The
|
||||
* interrupting transaction is of IN type (data transmitted by
|
||||
* the USB peripheral to the host PC)." */
|
||||
ASSERT_FAULT(epr & USB_EP_CTR_TX);
|
||||
usb_clear_ctr_tx(USB_EP0);
|
||||
handle_in0();
|
||||
} else {
|
||||
/* RM0008: "If DIR bit=1, CTR_RX bit or both CTR_TX/CTR_RX
|
||||
* are set in the USB_EPnR register related to the
|
||||
* interrupting endpoint. The interrupting transaction is of
|
||||
* OUT type (data received by the USB peripheral from the host
|
||||
* PC) or two pending transactions are waiting to be
|
||||
* processed."
|
||||
*
|
||||
* [mbolivar] Note how the following control flow (which
|
||||
* replicates ST's) doesn't seem to actually handle both
|
||||
* interrupts that are ostensibly pending when both CTR_RX and
|
||||
* CTR_TX are set.
|
||||
*
|
||||
* TODO sort this mess out.
|
||||
*/
|
||||
if (epr & USB_EP_CTR_TX) {
|
||||
usb_clear_ctr_tx(USB_EP0);
|
||||
handle_in0();
|
||||
} else { /* SETUP or CTR_RX */
|
||||
/* SETUP is held constant while CTR_RX is set, so clear it
|
||||
* either way */
|
||||
usb_clear_ctr_rx(USB_EP0);
|
||||
if (epr & USB_EP_SETUP) {
|
||||
handle_setup0();
|
||||
} else { /* CTR_RX */
|
||||
handle_out0();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set_rx_tx_status0(SaveRState, SaveTState);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline void dispatch_endpt(uint8 ep) {
|
||||
uint32 epr = USB_BASE->EP[ep];
|
||||
/* If ISTR_CTR is set and the ISTR gave us this EP_ID to handle,
|
||||
* then presumably at least one of CTR_RX and CTR_TX is set, but
|
||||
* again, ST's control flow allows for the possibility of neither.
|
||||
*
|
||||
* TODO try to find out if neither being set is possible. */
|
||||
if (epr & USB_EP_CTR_RX) {
|
||||
usb_clear_ctr_rx(ep);
|
||||
(USBLIB->ep_int_out[ep - 1])();
|
||||
}
|
||||
if (epr & USB_EP_CTR_TX) {
|
||||
usb_clear_ctr_tx(ep);
|
||||
(USBLIB->ep_int_in[ep - 1])();
|
||||
}
|
||||
}
|
||||
|
||||
static inline void set_rx_tx_status0(uint16 rx, uint16 tx) {
|
||||
usb_set_ep_rx_stat(USB_EP0, rx);
|
||||
usb_set_ep_tx_stat(USB_EP0, tx);
|
||||
}
|
||||
|
||||
/* TODO Rip out usb_lib/ dependency from the following functions: */
|
||||
|
||||
static void handle_setup0(void) {
|
||||
Setup0_Process();
|
||||
}
|
||||
|
||||
static void handle_in0(void) {
|
||||
In0_Process();
|
||||
}
|
||||
|
||||
static void handle_out0(void) {
|
||||
Out0_Process();
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2011 LeafLabs, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "usb_reg_map.h"
|
||||
|
||||
/* TODO these could use some improvement; they're fairly
|
||||
* straightforward ports of the analogous ST code. The PMA blit
|
||||
* routines in particular are obvious targets for performance
|
||||
* measurement and tuning. */
|
||||
|
||||
void usb_copy_to_pma(const uint8 *buf, uint16 len, uint16 pma_offset) {
|
||||
uint16 *dst = (uint16*)usb_pma_ptr(pma_offset);
|
||||
uint16 n = len >> 1;
|
||||
uint16 i;
|
||||
for (i = 0; i < n; i++) {
|
||||
*dst = (uint16)(*buf) | *(buf + 1) << 8;
|
||||
buf += 2;
|
||||
dst += 2;
|
||||
}
|
||||
if (len & 1) {
|
||||
*dst = *buf;
|
||||
}
|
||||
}
|
||||
|
||||
void usb_copy_from_pma(uint8 *buf, uint16 len, uint16 pma_offset) {
|
||||
uint32 *src = (uint32*)usb_pma_ptr(pma_offset);
|
||||
uint16 *dst = (uint16*)buf;
|
||||
uint16 n = len >> 1;
|
||||
uint16 i;
|
||||
for (i = 0; i < n; i++) {
|
||||
*dst++ = *src++;
|
||||
}
|
||||
if (len & 1) {
|
||||
*dst = *src & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_set_ep_rx_count_common(uint32 *rxc, uint16 count) {
|
||||
uint16 nblocks;
|
||||
if (count > 62) {
|
||||
/* use 32-byte memory block size */
|
||||
nblocks = count >> 5;
|
||||
if ((count & 0x1F) == 0) {
|
||||
nblocks--;
|
||||
}
|
||||
*rxc = (nblocks << 10) | 0x8000;
|
||||
} else {
|
||||
/* use 2-byte memory block size */
|
||||
nblocks = count >> 1;
|
||||
if ((count & 0x1) != 0) {
|
||||
nblocks++;
|
||||
}
|
||||
*rxc = nblocks << 10;
|
||||
}
|
||||
}
|
||||
|
||||
void usb_set_ep_rx_buf0_count(uint8 ep, uint16 count) {
|
||||
uint32 *rxc = usb_ep_rx_buf0_count_ptr(ep);
|
||||
usb_set_ep_rx_count_common(rxc, count);
|
||||
}
|
||||
|
||||
void usb_set_ep_rx_count(uint8 ep, uint16 count) {
|
||||
uint32 *rxc = usb_ep_rx_count_ptr(ep);
|
||||
usb_set_ep_rx_count_common(rxc, count);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,64 @@
|
|||
/******************** (C) COPYRIGHT 2008 STMicroelectronics ********************
|
||||
* File Name : usb_init.c
|
||||
* Author : MCD Application Team
|
||||
* Version : V2.2.1
|
||||
* Date : 09/22/2008
|
||||
* Description : Initialization routines & global variables
|
||||
********************************************************************************
|
||||
* THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
|
||||
* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE TIME.
|
||||
* AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE
|
||||
* CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING
|
||||
* INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
|
||||
*******************************************************************************/
|
||||
|
||||
/* Includes ------------------------------------------------------------------*/
|
||||
#include "usb_lib.h"
|
||||
|
||||
/* Private typedef -----------------------------------------------------------*/
|
||||
/* Private define ------------------------------------------------------------*/
|
||||
/* Private macro -------------------------------------------------------------*/
|
||||
/* Private variables ---------------------------------------------------------*/
|
||||
/* The number of current endpoint, it will be used to specify an endpoint */
|
||||
u8 EPindex;
|
||||
/* The number of current device, it is an index to the Device_Table */
|
||||
/* u8 Device_no; */
|
||||
/* Points to the DEVICE_INFO structure of current device */
|
||||
/* The purpose of this register is to speed up the execution */
|
||||
DEVICE_INFO *pInformation;
|
||||
/* Points to the DEVICE_PROP structure of current device */
|
||||
/* The purpose of this register is to speed up the execution */
|
||||
DEVICE_PROP *pProperty;
|
||||
/* Temporary save the state of Rx & Tx status. */
|
||||
/* Whenever the Rx or Tx state is changed, its value is saved */
|
||||
/* in this variable first and will be set to the EPRB or EPRA */
|
||||
/* at the end of interrupt process */
|
||||
u16 SaveState ;
|
||||
u16 wInterrupt_Mask;
|
||||
DEVICE_INFO Device_Info;
|
||||
USER_STANDARD_REQUESTS *pUser_Standard_Requests;
|
||||
|
||||
/* Extern variables ----------------------------------------------------------*/
|
||||
/* Private function prototypes -----------------------------------------------*/
|
||||
/* Private functions ---------------------------------------------------------*/
|
||||
|
||||
/*******************************************************************************
|
||||
* Function Name : USB_Init
|
||||
* Description : USB system initialization
|
||||
* Input : None.
|
||||
* Output : None.
|
||||
* Return : None.
|
||||
*******************************************************************************/
|
||||
void USB_Init(void)
|
||||
{
|
||||
pInformation = &Device_Info;
|
||||
pInformation->ControlState = 2;
|
||||
pProperty = &Device_Property;
|
||||
pUser_Standard_Requests = &User_Standard_Requests;
|
||||
/* Initialize devices one by one */
|
||||
|
||||
pProperty->Init();
|
||||
}
|
||||
|
||||
/******************* (C) COPYRIGHT 2008 STMicroelectronics *****END OF FILE****/
|
|
@ -0,0 +1,73 @@
|
|||
/******************** (C) COPYRIGHT 2008 STMicroelectronics ********************
|
||||
* File Name : usb_mem.c
|
||||
* Author : MCD Application Team
|
||||
* Version : V2.2.1
|
||||
* Date : 09/22/2008
|
||||
* Description : Utility functions for memory transfers to/from PMA
|
||||
********************************************************************************
|
||||
* THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
|
||||
* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE TIME.
|
||||
* AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE
|
||||
* CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING
|
||||
* INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
|
||||
*******************************************************************************/
|
||||
|
||||
/* Includes ------------------------------------------------------------------*/
|
||||
#include "usb_lib.h"
|
||||
|
||||
/* Private typedef -----------------------------------------------------------*/
|
||||
/* Private define ------------------------------------------------------------*/
|
||||
/* Private macro -------------------------------------------------------------*/
|
||||
/* Private variables ---------------------------------------------------------*/
|
||||
/* Extern variables ----------------------------------------------------------*/
|
||||
/* Private function prototypes -----------------------------------------------*/
|
||||
/* Private functions ---------------------------------------------------------*/
|
||||
/*******************************************************************************
|
||||
* Function Name : UserToPMABufferCopy
|
||||
* Description : Copy a buffer from user memory area to packet memory area (PMA)
|
||||
* Input : - pbUsrBuf: pointer to user memory area.
|
||||
* - wPMABufAddr: address into PMA.
|
||||
* - wNBytes: no. of bytes to be copied.
|
||||
* Output : None.
|
||||
* Return : None .
|
||||
*******************************************************************************/
|
||||
void UserToPMABufferCopy(const u8 *pbUsrBuf, u16 wPMABufAddr, u16 wNBytes)
|
||||
{
|
||||
u32 n = (wNBytes + 1) >> 1; /* n = (wNBytes + 1) / 2 */
|
||||
u32 i, temp1, temp2;
|
||||
u16 *pdwVal;
|
||||
pdwVal = (u16 *)(wPMABufAddr * 2 + PMAAddr);
|
||||
for (i = n; i != 0; i--)
|
||||
{
|
||||
temp1 = (u16) * pbUsrBuf;
|
||||
pbUsrBuf++;
|
||||
temp2 = temp1 | (u16) * pbUsrBuf << 8;
|
||||
*pdwVal++ = temp2;
|
||||
pdwVal++;
|
||||
pbUsrBuf++;
|
||||
}
|
||||
}
|
||||
/*******************************************************************************
|
||||
* Function Name : PMAToUserBufferCopy
|
||||
* Description : Copy a buffer from user memory area to packet memory area (PMA)
|
||||
* Input : - pbUsrBuf = pointer to user memory area.
|
||||
* - wPMABufAddr = address into PMA.
|
||||
* - wNBytes = no. of bytes to be copied.
|
||||
* Output : None.
|
||||
* Return : None.
|
||||
*******************************************************************************/
|
||||
void PMAToUserBufferCopy(u8 *pbUsrBuf, u16 wPMABufAddr, u16 wNBytes)
|
||||
{
|
||||
u32 n = (wNBytes + 1) >> 1;/* /2*/
|
||||
u32 i;
|
||||
u32 *pdwVal;
|
||||
pdwVal = (u32 *)(wPMABufAddr * 2 + PMAAddr);
|
||||
for (i = n; i != 0; i--)
|
||||
{
|
||||
*(u16*)pbUsrBuf++ = *pdwVal++;
|
||||
pbUsrBuf++;
|
||||
}
|
||||
}
|
||||
|
||||
/******************* (C) COPYRIGHT 2008 STMicroelectronics *****END OF FILE****/
|
|
@ -0,0 +1,748 @@
|
|||
/******************** (C) COPYRIGHT 2008 STMicroelectronics ********************
|
||||
* File Name : usb_regs.c
|
||||
* Author : MCD Application Team
|
||||
* Version : V2.2.1
|
||||
* Date : 09/22/2008
|
||||
* Description : Interface functions to USB cell registers
|
||||
********************************************************************************
|
||||
* THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
|
||||
* WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE TIME.
|
||||
* AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE
|
||||
* CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING
|
||||
* INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
|
||||
*******************************************************************************/
|
||||
|
||||
/* Includes ------------------------------------------------------------------*/
|
||||
#include "usb_lib.h"
|
||||
|
||||
/* Private typedef -----------------------------------------------------------*/
|
||||
/* Private define ------------------------------------------------------------*/
|
||||
/* Private macro -------------------------------------------------------------*/
|
||||
/* Private variables ---------------------------------------------------------*/
|
||||
/* Extern variables ----------------------------------------------------------*/
|
||||
/* Private function prototypes -----------------------------------------------*/
|
||||
/* Private functions ---------------------------------------------------------*/
|
||||
|
||||
/*******************************************************************************
|
||||
* Function Name : SetCNTR.
|
||||
* Description : Set the CNTR register value.
|
||||
* Input : wRegValue: new register value.
|
||||
* Output : None.
|
||||
* Return : None.
|
||||
*******************************************************************************/
|
||||
void SetCNTR(u16 wRegValue)
|
||||
{
|
||||
_SetCNTR(wRegValue);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Function Name : GetCNTR.
|
||||
* Description : returns the CNTR register value.
|
||||
* Input : None.
|
||||
* Output : None.
|
||||
* Return : CNTR register Value.
|
||||
*******************************************************************************/
|
||||
u16 GetCNTR(void)
|
||||
{
|
||||
return(_GetCNTR());
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Function Name : SetISTR.
|
||||
* Description : Set the ISTR register value.
|
||||
* Input : wRegValue: new register value.
|
||||
* Output : None.
|
||||
* Return : None.
|
||||
*******************************************************************************/
|
||||
void SetISTR(u16 wRegValue)
|
||||
{
|
||||
_SetISTR(wRegValue);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Function Name : GetISTR
|
||||
* Description : Returns the ISTR register value.
|
||||
* Input : None.
|
||||
* Output : None.
|
||||
* Return : ISTR register Value
|
||||
*******************************************************************************/
|
||||
u16 GetISTR(void)
|
||||
{
|
||||
return(_GetISTR());
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Function Name : GetFNR
|
||||
* Description : Returns the FNR register value.
|
||||
* Input : None.
|
||||
* Output : None.
|
||||
* Return : FNR register Value
|
||||
*******************************************************************************/
|
||||
u16 GetFNR(void)
|
||||
{
|
||||
return(_GetFNR());
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Function Name : SetDADDR
|
||||
* Description : Set the DADDR register value.
|
||||
* Input : wRegValue: new register value.
|
||||
* Output : None.
|
||||
* Return : None.
|
||||
*******************************************************************************/
|
||||
void SetDADDR(u16 wRegValue)
|
||||
{
|
||||
_SetDADDR(wRegValue);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Function Name : GetDADDR
|
||||
* Description : Returns the DADDR register value.
|
||||
* Input : None.
|
||||
* Output : None.
|
||||
* Return : DADDR register Value
|
||||
*******************************************************************************/
|
||||
u16 GetDADDR(void)
|
||||
{
|
||||
return(_GetDADDR());
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Function Name : SetBTABLE
|
||||
* Description : Set the BTABLE.
|
||||
* Input : wRegValue: New register value.
|
||||
* Output : None.
|
||||
* Return : None.
|
||||
*******************************************************************************/
|
||||
void SetBTABLE(u16 wRegValue)
|
||||
{
|
||||
_SetBTABLE(wRegValue);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Function Name : GetBTABLE.
|
||||
* Description : Returns the BTABLE register value.
|
||||
* Input : None.
|
||||
* Output : None.
|
||||
* Return : BTABLE address.
|
||||
*******************************************************************************/
|
||||
u16 GetBTABLE(void)
|
||||
{
|
||||
return(_GetBTABLE());
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Function Name : SetENDPOINT
|
||||
* Description : Setthe Endpoint register value.
|
||||
* Input : bEpNum: Endpoint Number.
|
||||
* wRegValue.
|
||||
* Output : None.
|
||||
* Return : None.
|
||||
*******************************************************************************/
|
||||
void SetENDPOINT(u8 bEpNum, u16 wRegValue)
|
||||
{
|
||||
_SetENDPOINT(bEpNum, wRegValue);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Function Name : GetENDPOINT
|
||||
* Description : Return the Endpoint register value.
|
||||
* Input : bEpNum: Endpoint Number.
|
||||
* Output : None.
|
||||
* Return : Endpoint register value.
|
||||
*******************************************************************************/
|
||||
u16 GetENDPOINT(u8 bEpNum)
|
||||
{
|
||||
return(_GetENDPOINT(bEpNum));
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Function Name : SetEPType
|
||||
* Description : sets the type in the endpoint register.
|
||||
* Input : bEpNum: Endpoint Number.
|
||||
* wType: type definition.
|
||||
* Output : None.
|
||||
* Return : None.
|
||||
*******************************************************************************/
|
||||
void SetEPType(u8 bEpNum, u16 wType)
|
||||
{
|
||||
_SetEPType(bEpNum, wType);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Function Name : GetEPType
|
||||
* Description : Returns the endpoint type.
|
||||
* Input : bEpNum: Endpoint Number.
|
||||
* Output : None.
|
||||
* Return : Endpoint Type
|
||||
*******************************************************************************/
|
||||
u16 GetEPType(u8 bEpNum)
|
||||
{
|
||||
return(_GetEPType(bEpNum));
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Function Name : SetEPTxStatus
|
||||
* Description : Set the status of Tx endpoint.
|
||||
* Input : bEpNum: Endpoint Number.
|
||||
* wState: new state.
|
||||
* Output : None.
|
||||
* Return : None.
|
||||
*******************************************************************************/
|
||||
void SetEPTxStatus(u8 bEpNum, u16 wState)
|
||||
{
|
||||
_SetEPTxStatus(bEpNum, wState);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Function Name : SetEPRxStatus
|
||||
* Description : Set the status of Rx endpoint.
|
||||
* Input : bEpNum: Endpoint Number.
|
||||
* wState: new state.
|
||||
* Output : None.
|
||||
* Return : None.
|
||||
*******************************************************************************/
|
||||
void SetEPRxStatus(u8 bEpNum, u16 wState)
|
||||
{
|
||||
_SetEPRxStatus(bEpNum, wState);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Function Name : SetDouBleBuffEPStall
|
||||
* Description : sets the status for Double Buffer Endpoint to STALL
|
||||
* Input : bEpNum: Endpoint Number.
|
||||
* bDir: Endpoint direction.
|
||||
* Output : None.
|
||||
* Return : None.
|
||||
*******************************************************************************/
|
||||
void SetDouBleBuffEPStall(u8 bEpNum, u8 bDir)
|
||||
{
|
||||
u16 Endpoint_DTOG_Status;
|
||||
Endpoint_DTOG_Status = GetENDPOINT(bEpNum);
|
||||
if (bDir == EP_DBUF_OUT)
|
||||
{ /* OUT double buffered endpoint */
|
||||
_SetENDPOINT(bEpNum, Endpoint_DTOG_Status & ~EPRX_DTOG1);
|
||||
}
|
||||
else if (bDir == EP_DBUF_IN)
|
||||
{ /* IN double buffered endpoint */
|
||||
_SetENDPOINT(bEpNum, Endpoint_DTOG_Status & ~EPTX_DTOG1);
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Function Name : GetEPTxStatus
|
||||
* Description : Returns the endpoint Tx status.
|
||||
* Input : bEpNum: Endpoint Number.
|
||||
* Output : None.
|
||||
* Return : Endpoint TX Status
|
||||
*******************************************************************************/
|
||||
u16 GetEPTxStatus(u8 bEpNum)
|
||||
{
|
||||
return(_GetEPTxStatus(bEpNum));
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Function Name : GetEPRxStatus
|
||||
* Description : Returns the endpoint Rx status.
|
||||
* Input : bEpNum: Endpoint Number.
|
||||
* Output : None.
|
||||
* Return : Endpoint RX Status
|
||||
*******************************************************************************/
|
||||
u16 GetEPRxStatus(u8 bEpNum)
|
||||
{
|
||||
return(_GetEPRxStatus(bEpNum));
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Function Name : SetEPTxValid
|
||||
* Description : Valid the endpoint Tx Status.
|
||||
* Input : bEpNum: Endpoint Number.
|
||||
* Output : None.
|
||||
* Return : None.
|
||||
*******************************************************************************/
|
||||
void SetEPTxValid(u8 bEpNum)
|
||||
{
|
||||
_SetEPTxStatus(bEpNum, EP_TX_VALID);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Function Name : SetEPRxValid
|
||||
* Description : Valid the endpoint Rx Status.
|
||||
* Input : bEpNum: Endpoint Number.
|
||||
* Output : None.
|
||||
* Return : None.
|
||||
*******************************************************************************/
|
||||
void SetEPRxValid(u8 bEpNum)
|
||||
{
|
||||
_SetEPRxStatus(bEpNum, EP_RX_VALID);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Function Name : SetEP_KIND
|
||||
* Description : Clear the EP_KIND bit.
|
||||
* Input : bEpNum: Endpoint Number.
|
||||
* Output : None.
|
||||
* Return : None.
|
||||
*******************************************************************************/
|
||||
void SetEP_KIND(u8 bEpNum)
|
||||
{
|
||||
_SetEP_KIND(bEpNum);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Function Name : ClearEP_KIND
|
||||
* Description : set the EP_KIND bit.
|
||||
* Input : bEpNum: Endpoint Number.
|
||||
* Output : None.
|
||||
* Return : None.
|
||||
*******************************************************************************/
|
||||
void ClearEP_KIND(u8 bEpNum)
|
||||
{
|
||||
_ClearEP_KIND(bEpNum);
|
||||
}
|
||||
/*******************************************************************************
|
||||
* Function Name : Clear_Status_Out
|
||||
* Description : Clear the Status Out of the related Endpoint
|
||||
* Input : bEpNum: Endpoint Number.
|
||||
* Output : None.
|
||||
* Return : None.
|
||||
*******************************************************************************/
|
||||
void Clear_Status_Out(u8 bEpNum)
|
||||
{
|
||||
_ClearEP_KIND(bEpNum);
|
||||
}
|
||||
/*******************************************************************************
|
||||
* Function Name : Set_Status_Out
|
||||
* Description : Set the Status Out of the related Endpoint
|
||||
* Input : bEpNum: Endpoint Number.
|
||||
* Output : None.
|
||||
* Return : None.
|
||||
*******************************************************************************/
|
||||
void Set_Status_Out(u8 bEpNum)
|
||||
{
|
||||
_SetEP_KIND(bEpNum);
|
||||
}
|
||||
/*******************************************************************************
|
||||
* Function Name : SetEPDoubleBuff
|
||||
* Description : Enable the double buffer feature for the endpoint.
|
||||
* Input : bEpNum: Endpoint Number.
|
||||
* Output : None.
|
||||
* Return : None.
|
||||
*******************************************************************************/
|
||||
void SetEPDoubleBuff(u8 bEpNum)
|
||||
{
|
||||
_SetEP_KIND(bEpNum);
|
||||
}
|
||||
/*******************************************************************************
|
||||
* Function Name : ClearEPDoubleBuff
|
||||
* Description : Disable the double buffer feature for the endpoint.
|
||||
* Input : bEpNum: Endpoint Number.
|
||||
* Output : None.
|
||||
* Return : None.
|
||||
*******************************************************************************/
|
||||
void ClearEPDoubleBuff(u8 bEpNum)
|
||||
{
|
||||
_ClearEP_KIND(bEpNum);
|
||||
}
|
||||
/*******************************************************************************
|
||||
* Function Name : GetTxStallStatus
|
||||
* Description : Returns the Stall status of the Tx endpoint.
|
||||
* Input : bEpNum: Endpoint Number.
|
||||
* Output : None.
|
||||
* Return : Tx Stall status.
|
||||
*******************************************************************************/
|
||||
u16 GetTxStallStatus(u8 bEpNum)
|
||||
{
|
||||
return(_GetTxStallStatus(bEpNum));
|
||||
}
|
||||
/*******************************************************************************
|
||||
* Function Name : GetRxStallStatus
|
||||
* Description : Returns the Stall status of the Rx endpoint.
|
||||
* Input : bEpNum: Endpoint Number.
|
||||
* Output : None.
|
||||
* Return : Rx Stall status.
|
||||
*******************************************************************************/
|
||||
u16 GetRxStallStatus(u8 bEpNum)
|
||||
{
|
||||
return(_GetRxStallStatus(bEpNum));
|
||||
}
|
||||
/*******************************************************************************
|
||||
* Function Name : ClearEP_CTR_RX
|
||||
* Description : Clear the CTR_RX bit.
|
||||
* Input : bEpNum: Endpoint Number.
|
||||
* Output : None.
|
||||
* Return : None.
|
||||
*******************************************************************************/
|
||||
void ClearEP_CTR_RX(u8 bEpNum)
|
||||
{
|
||||
_ClearEP_CTR_RX(bEpNum);
|
||||
}
|
||||
/*******************************************************************************
|
||||
* Function Name : ClearEP_CTR_TX
|
||||
* Description : Clear the CTR_TX bit.
|
||||
* Input : bEpNum: Endpoint Number.
|
||||
* Output : None.
|
||||
* Return : None.
|
||||
*******************************************************************************/
|
||||
void ClearEP_CTR_TX(u8 bEpNum)
|
||||
{
|
||||
_ClearEP_CTR_TX(bEpNum);
|
||||
}
|
||||
/*******************************************************************************
|
||||
* Function Name : ToggleDTOG_RX
|
||||
* Description : Toggle the DTOG_RX bit.
|
||||
* Input : bEpNum: Endpoint Number.
|
||||
* Output : None.
|
||||
* Return : None.
|
||||
*******************************************************************************/
|
||||
void ToggleDTOG_RX(u8 bEpNum)
|
||||
{
|
||||
_ToggleDTOG_RX(bEpNum);
|
||||
}
|
||||
/*******************************************************************************
|
||||
* Function Name : ToggleDTOG_TX
|
||||
* Description : Toggle the DTOG_TX bit.
|
||||
* Input : bEpNum: Endpoint Number.
|
||||
* Output : None.
|
||||
* Return : None.
|
||||
*******************************************************************************/
|
||||
void ToggleDTOG_TX(u8 bEpNum)
|
||||
{
|
||||
_ToggleDTOG_TX(bEpNum);
|
||||
}
|
||||
/*******************************************************************************
|
||||
* Function Name : ClearDTOG_RX.
|
||||
* Description : Clear the DTOG_RX bit.
|
||||
* Input : bEpNum: Endpoint Number.
|
||||
* Output : None.
|
||||
* Return : None.
|
||||
*******************************************************************************/
|
||||
void ClearDTOG_RX(u8 bEpNum)
|
||||
{
|
||||
_ClearDTOG_RX(bEpNum);
|
||||
}
|
||||
/*******************************************************************************
|
||||
* Function Name : ClearDTOG_TX.
|
||||
* Description : Clear the DTOG_TX bit.
|
||||
* Input : bEpNum: Endpoint Number.
|
||||
* Output : None.
|
||||
* Return : None.
|
||||
*******************************************************************************/
|
||||
void ClearDTOG_TX(u8 bEpNum)
|
||||
{
|
||||
_ClearDTOG_TX(bEpNum);
|
||||
}
|
||||
/*******************************************************************************
|
||||
* Function Name : SetEPAddress
|
||||
* Description : Set the endpoint address.
|
||||
* Input : bEpNum: Endpoint Number.
|
||||
* bAddr: New endpoint address.
|
||||
* Output : None.
|
||||
* Return : None.
|
||||
*******************************************************************************/
|
||||
void SetEPAddress(u8 bEpNum, u8 bAddr)
|
||||
{
|
||||
_SetEPAddress(bEpNum, bAddr);
|
||||
}
|
||||
/*******************************************************************************
|
||||
* Function Name : GetEPAddress
|
||||
* Description : Get the endpoint address.
|
||||
* Input : bEpNum: Endpoint Number.
|
||||
* Output : None.
|
||||
* Return : Endpoint address.
|
||||
*******************************************************************************/
|
||||
u8 GetEPAddress(u8 bEpNum)
|
||||
{
|
||||
return(_GetEPAddress(bEpNum));
|
||||
}
|
||||
/*******************************************************************************
|
||||
* Function Name : SetEPTxAddr
|
||||
* Description : Set the endpoint Tx buffer address.
|
||||
* Input : bEpNum: Endpoint Number.
|
||||
* wAddr: new address.
|
||||
* Output : None.
|
||||
* Return : None.
|
||||
*******************************************************************************/
|
||||
void SetEPTxAddr(u8 bEpNum, u16 wAddr)
|
||||
{
|
||||
_SetEPTxAddr(bEpNum, wAddr);
|
||||
}
|
||||
/*******************************************************************************
|
||||
* Function Name : SetEPRxAddr
|
||||
* Description : Set the endpoint Rx buffer address.
|
||||
* Input : bEpNum: Endpoint Number.
|
||||
* wAddr: new address.
|
||||
* Output : None.
|
||||
* Return : None.
|
||||
*******************************************************************************/
|
||||
void SetEPRxAddr(u8 bEpNum, u16 wAddr)
|
||||
{
|
||||
_SetEPRxAddr(bEpNum, wAddr);
|
||||
}
|
||||
/*******************************************************************************
|
||||
* Function Name : GetEPTxAddr
|
||||
* Description : Returns the endpoint Tx buffer address.
|
||||
* Input : bEpNum: Endpoint Number.
|
||||
* Output : None.
|
||||
* Return : Rx buffer address.
|
||||
*******************************************************************************/
|
||||
u16 GetEPTxAddr(u8 bEpNum)
|
||||
{
|
||||
return(_GetEPTxAddr(bEpNum));
|
||||
}
|
||||
/*******************************************************************************
|
||||
* Function Name : GetEPRxAddr.
|
||||
* Description : Returns the endpoint Rx buffer address.
|
||||
* Input : bEpNum: Endpoint Number.
|
||||
* Output : None.
|
||||
* Return : Rx buffer address.
|
||||
*******************************************************************************/
|
||||
u16 GetEPRxAddr(u8 bEpNum)
|
||||
{
|
||||
return(_GetEPRxAddr(bEpNum));
|
||||
}
|
||||
/*******************************************************************************
|
||||
* Function Name : SetEPTxCount.
|
||||
* Description : Set the Tx count.
|
||||
* Input : bEpNum: Endpoint Number.
|
||||
* wCount: new count value.
|
||||
* Output : None.
|
||||
* Return : None.
|
||||
*******************************************************************************/
|
||||
void SetEPTxCount(u8 bEpNum, u16 wCount)
|
||||
{
|
||||
_SetEPTxCount(bEpNum, wCount);
|
||||
}
|
||||
/*******************************************************************************
|
||||
* Function Name : SetEPCountRxReg.
|
||||
* Description : Set the Count Rx Register value.
|
||||
* Input : *pdwReg: point to the register.
|
||||
* wCount: the new register value.
|
||||
* Output : None.
|
||||
* Return : None.
|
||||
*******************************************************************************/
|
||||
void SetEPCountRxReg(u32 *pdwReg, u16 wCount)
|
||||
{
|
||||
_SetEPCountRxReg(dwReg, wCount);
|
||||
}
|
||||
/*******************************************************************************
|
||||
* Function Name : SetEPRxCount
|
||||
* Description : Set the Rx count.
|
||||
* Input : bEpNum: Endpoint Number.
|
||||
* wCount: the new count value.
|
||||
* Output : None.
|
||||
* Return : None.
|
||||
*******************************************************************************/
|
||||
void SetEPRxCount(u8 bEpNum, u16 wCount)
|
||||
{
|
||||
_SetEPRxCount(bEpNum, wCount);
|
||||
}
|
||||
/*******************************************************************************
|
||||
* Function Name : GetEPTxCount
|
||||
* Description : Get the Tx count.
|
||||
* Input : bEpNum: Endpoint Number.
|
||||
* Output : None
|
||||
* Return : Tx count value.
|
||||
*******************************************************************************/
|
||||
u16 GetEPTxCount(u8 bEpNum)
|
||||
{
|
||||
return(_GetEPTxCount(bEpNum));
|
||||
}
|
||||
/*******************************************************************************
|
||||
* Function Name : GetEPRxCount
|
||||
* Description : Get the Rx count.
|
||||
* Input : bEpNum: Endpoint Number.
|
||||
* Output : None.
|
||||
* Return : Rx count value.
|
||||
*******************************************************************************/
|
||||
u16 GetEPRxCount(u8 bEpNum)
|
||||
{
|
||||
return(_GetEPRxCount(bEpNum));
|
||||
}
|
||||
/*******************************************************************************
|
||||
* Function Name : SetEPDblBuffAddr
|
||||
* Description : Set the addresses of the buffer 0 and 1.
|
||||
* Input : bEpNum: Endpoint Number.
|
||||
* wBuf0Addr: new address of buffer 0.
|
||||
* wBuf1Addr: new address of buffer 1.
|
||||
* Output : None.
|
||||
* Return : None.
|
||||
*******************************************************************************/
|
||||
void SetEPDblBuffAddr(u8 bEpNum, u16 wBuf0Addr, u16 wBuf1Addr)
|
||||
{
|
||||
_SetEPDblBuffAddr(bEpNum, wBuf0Addr, wBuf1Addr);
|
||||
}
|
||||
/*******************************************************************************
|
||||
* Function Name : SetEPDblBuf0Addr
|
||||
* Description : Set the Buffer 1 address.
|
||||
* Input : bEpNum: Endpoint Number
|
||||
* wBuf0Addr: new address.
|
||||
* Output : None.
|
||||
* Return : None.
|
||||
*******************************************************************************/
|
||||
void SetEPDblBuf0Addr(u8 bEpNum, u16 wBuf0Addr)
|
||||
{
|
||||
_SetEPDblBuf0Addr(bEpNum, wBuf0Addr);
|
||||
}
|
||||
/*******************************************************************************
|
||||
* Function Name : SetEPDblBuf1Addr
|
||||
* Description : Set the Buffer 1 address.
|
||||
* Input : bEpNum: Endpoint Number
|
||||
* wBuf1Addr: new address.
|
||||
* Output : None.
|
||||
* Return : None.
|
||||
*******************************************************************************/
|
||||
void SetEPDblBuf1Addr(u8 bEpNum, u16 wBuf1Addr)
|
||||
{
|
||||
_SetEPDblBuf1Addr(bEpNum, wBuf1Addr);
|
||||
}
|
||||
/*******************************************************************************
|
||||
* Function Name : GetEPDblBuf0Addr
|
||||
* Description : Returns the address of the Buffer 0.
|
||||
* Input : bEpNum: Endpoint Number.
|
||||
* Output : None.
|
||||
* Return : None.
|
||||
*******************************************************************************/
|
||||
u16 GetEPDblBuf0Addr(u8 bEpNum)
|
||||
{
|
||||
return(_GetEPDblBuf0Addr(bEpNum));
|
||||
}
|
||||
/*******************************************************************************
|
||||
* Function Name : GetEPDblBuf1Addr
|
||||
* Description : Returns the address of the Buffer 1.
|
||||
* Input : bEpNum: Endpoint Number.
|
||||
* Output : None.
|
||||
* Return : Address of the Buffer 1.
|
||||
*******************************************************************************/
|
||||
u16 GetEPDblBuf1Addr(u8 bEpNum)
|
||||
{
|
||||
return(_GetEPDblBuf1Addr(bEpNum));
|
||||
}
|
||||
/*******************************************************************************
|
||||
* Function Name : SetEPDblBuffCount
|
||||
* Description : Set the number of bytes for a double Buffer
|
||||
* endpoint.
|
||||
* Input : bEpNum,bDir, wCount
|
||||
* Output : None.
|
||||
* Return : None.
|
||||
*******************************************************************************/
|
||||
void SetEPDblBuffCount(u8 bEpNum, u8 bDir, u16 wCount)
|
||||
{
|
||||
_SetEPDblBuffCount(bEpNum, bDir, wCount);
|
||||
}
|
||||
/*******************************************************************************
|
||||
* Function Name : SetEPDblBuf0Count
|
||||
* Description : Set the number of bytes in the buffer 0 of a double Buffer
|
||||
* endpoint.
|
||||
* Input : bEpNum, bDir, wCount
|
||||
* Output : None.
|
||||
* Return : None.
|
||||
*******************************************************************************/
|
||||
void SetEPDblBuf0Count(u8 bEpNum, u8 bDir, u16 wCount)
|
||||
{
|
||||
_SetEPDblBuf0Count(bEpNum, bDir, wCount);
|
||||
}
|
||||
/*******************************************************************************
|
||||
* Function Name : SetEPDblBuf1Count
|
||||
* Description : Set the number of bytes in the buffer 0 of a double Buffer
|
||||
* endpoint.
|
||||
* Input : bEpNum, bDir, wCount
|
||||
* Output : None.
|
||||
* Return : None.
|
||||
*******************************************************************************/
|
||||
void SetEPDblBuf1Count(u8 bEpNum, u8 bDir, u16 wCount)
|
||||
{
|
||||
_SetEPDblBuf1Count(bEpNum, bDir, wCount);
|
||||
}
|
||||
/*******************************************************************************
|
||||
* Function Name : GetEPDblBuf0Count
|
||||
* Description : Returns the number of byte received in the buffer 0 of a double
|
||||
* Buffer endpoint.
|
||||
* Input : bEpNum: Endpoint Number.
|
||||
* Output : None.
|
||||
* Return : Endpoint Buffer 0 count
|
||||
*******************************************************************************/
|
||||
u16 GetEPDblBuf0Count(u8 bEpNum)
|
||||
{
|
||||
return(_GetEPDblBuf0Count(bEpNum));
|
||||
}
|
||||
/*******************************************************************************
|
||||
* Function Name : GetEPDblBuf1Count
|
||||
* Description : Returns the number of data received in the buffer 1 of a double
|
||||
* Buffer endpoint.
|
||||
* Input : bEpNum: Endpoint Number.
|
||||
* Output : None.
|
||||
* Return : Endpoint Buffer 1 count.
|
||||
*******************************************************************************/
|
||||
u16 GetEPDblBuf1Count(u8 bEpNum)
|
||||
{
|
||||
return(_GetEPDblBuf1Count(bEpNum));
|
||||
}
|
||||
/*******************************************************************************
|
||||
* Function Name : GetEPDblBufDir
|
||||
* Description : gets direction of the double buffered endpoint
|
||||
* Input : bEpNum: Endpoint Number.
|
||||
* Output : None.
|
||||
* Return : EP_DBUF_OUT, EP_DBUF_IN,
|
||||
* EP_DBUF_ERR if the endpoint counter not yet programmed.
|
||||
*******************************************************************************/
|
||||
EP_DBUF_DIR GetEPDblBufDir(u8 bEpNum)
|
||||
{
|
||||
if ((u16)(*_pEPRxCount(bEpNum) & 0xFC00) != 0)
|
||||
return(EP_DBUF_OUT);
|
||||
else if (((u16)(*_pEPTxCount(bEpNum)) & 0x03FF) != 0)
|
||||
return(EP_DBUF_IN);
|
||||
else
|
||||
return(EP_DBUF_ERR);
|
||||
}
|
||||
/*******************************************************************************
|
||||
* Function Name : FreeUserBuffer
|
||||
* Description : free buffer used from the application realizing it to the line
|
||||
toggles bit SW_BUF in the double buffered endpoint register
|
||||
* Input : bEpNum, bDir
|
||||
* Output : None.
|
||||
* Return : None.
|
||||
*******************************************************************************/
|
||||
void FreeUserBuffer(u8 bEpNum, u8 bDir)
|
||||
{
|
||||
if (bDir == EP_DBUF_OUT)
|
||||
{ /* OUT double buffered endpoint */
|
||||
_ToggleDTOG_TX(bEpNum);
|
||||
}
|
||||
else if (bDir == EP_DBUF_IN)
|
||||
{ /* IN double buffered endpoint */
|
||||
_ToggleDTOG_RX(bEpNum);
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Function Name : ToWord
|
||||
* Description : merge two byte in a word.
|
||||
* Input : bh: byte high, bl: bytes low.
|
||||
* Output : None.
|
||||
* Return : resulted word.
|
||||
*******************************************************************************/
|
||||
u16 ToWord(u8 bh, u8 bl)
|
||||
{
|
||||
u16 wRet;
|
||||
wRet = (u16)bl | ((u16)bh << 8);
|
||||
return(wRet);
|
||||
}
|
||||
/*******************************************************************************
|
||||
* Function Name : ByteSwap
|
||||
* Description : Swap two byte in a word.
|
||||
* Input : wSwW: word to Swap.
|
||||
* Output : None.
|
||||
* Return : resulted word.
|
||||
*******************************************************************************/
|
||||
u16 ByteSwap(u16 wSwW)
|
||||
{
|
||||
u8 bTemp;
|
||||
u16 wRet;
|
||||
bTemp = (u8)(wSwW & 0xff);
|
||||
wRet = (wSwW >> 8) | ((u16)bTemp << 8);
|
||||
return(wRet);
|
||||
}
|
||||
|
||||
/******************* (C) COPYRIGHT 2008 STMicroelectronics *****END OF FILE****/
|
|
@ -0,0 +1,150 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Perry Hung.
|
||||
* Copyright (c) 2011, 2012 LeafLabs, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/util.c
|
||||
* @brief Utility procedures for debugging
|
||||
*/
|
||||
|
||||
#include <libmaple/libmaple.h>
|
||||
#include <libmaple/usart.h>
|
||||
#include <libmaple/gpio.h>
|
||||
#include <libmaple/nvic.h>
|
||||
|
||||
/* (Undocumented) hooks used by Wirish to direct our behavior here */
|
||||
extern __weak void __lm_error(void);
|
||||
extern __weak usart_dev* __lm_enable_error_usart(void);
|
||||
|
||||
/* If you define ERROR_LED_PORT and ERROR_LED_PIN, then a failed
|
||||
* ASSERT() will also throb() an LED connected to that port and pin.
|
||||
*/
|
||||
#if defined(ERROR_LED_PORT) && defined(ERROR_LED_PIN)
|
||||
#define HAVE_ERROR_LED
|
||||
#endif
|
||||
|
||||
/* (Called from exc.S with global interrupts disabled.) */
|
||||
__attribute__((noreturn)) void __error(void) {
|
||||
if (__lm_error) {
|
||||
__lm_error();
|
||||
}
|
||||
/* Reenable global interrupts */
|
||||
nvic_globalirq_enable();
|
||||
throb();
|
||||
}
|
||||
|
||||
/*
|
||||
* Print an error message on a UART upon a failed assertion (if one is
|
||||
* available), and punt to __error().
|
||||
*
|
||||
* @param file Source file of failed assertion
|
||||
* @param line Source line of failed assertion
|
||||
* @param exp String representation of failed assertion
|
||||
* @sideeffect Turns of all peripheral interrupts except USB.
|
||||
*/
|
||||
void _fail(const char* file, int line, const char* exp) {
|
||||
if (__lm_enable_error_usart) {
|
||||
/* Initialize the error USART */
|
||||
usart_dev *err_usart = __lm_enable_error_usart();
|
||||
|
||||
/* Print failed assert message */
|
||||
usart_putstr(err_usart, "ERROR: FAILED ASSERT(");
|
||||
usart_putstr(err_usart, exp);
|
||||
usart_putstr(err_usart, "): ");
|
||||
usart_putstr(err_usart, file);
|
||||
usart_putstr(err_usart, ": ");
|
||||
usart_putudec(err_usart, line);
|
||||
usart_putc(err_usart, '\n');
|
||||
usart_putc(err_usart, '\r');
|
||||
}
|
||||
/* Shutdown and error fade */
|
||||
__error();
|
||||
}
|
||||
|
||||
/*
|
||||
* Provide an __assert_func handler to libc so that calls to assert()
|
||||
* get redirected to _fail.
|
||||
*/
|
||||
void __assert_func(const char* file, int line, const char* method,
|
||||
const char* expression) {
|
||||
_fail(file, line, expression);
|
||||
}
|
||||
|
||||
/*
|
||||
* Provide an abort() implementation that aborts execution and punts
|
||||
* to __error().
|
||||
*/
|
||||
void abort() {
|
||||
if (__lm_enable_error_usart) {
|
||||
/* Initialize the error USART */
|
||||
usart_dev *err_usart = __lm_enable_error_usart();
|
||||
/* Print abort message. */
|
||||
usart_putstr(err_usart, "ERROR: PROGRAM ABORTED VIA abort()\r\n");
|
||||
}
|
||||
|
||||
/* Shutdown and error fade */
|
||||
__error();
|
||||
}
|
||||
|
||||
/* This was public as of v0.0.12, so we've got to keep it public. */
|
||||
/**
|
||||
* @brief Fades the error LED on and off
|
||||
* @sideeffect Sets output push-pull on ERROR_LED_PIN.
|
||||
*/
|
||||
__attribute__((noreturn)) void throb(void) {
|
||||
#ifdef HAVE_ERROR_LED
|
||||
int32 slope = 1;
|
||||
uint32 CC = 0x0000;
|
||||
uint32 TOP_CNT = 0x0200;
|
||||
uint32 i = 0;
|
||||
|
||||
gpio_set_mode(ERROR_LED_PORT, ERROR_LED_PIN, GPIO_MODE_OUTPUT);
|
||||
/* Error fade. */
|
||||
while (1) {
|
||||
if (CC == TOP_CNT) {
|
||||
slope = -1;
|
||||
} else if (CC == 0) {
|
||||
slope = 1;
|
||||
}
|
||||
|
||||
if (i == TOP_CNT) {
|
||||
CC += slope;
|
||||
i = 0;
|
||||
}
|
||||
|
||||
if (i < CC) {
|
||||
gpio_write_bit(ERROR_LED_PORT, ERROR_LED_PIN, 1);
|
||||
} else {
|
||||
gpio_write_bit(ERROR_LED_PORT, ERROR_LED_PIN, 0);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
#else
|
||||
/* No error LED is defined; do nothing. */
|
||||
while (1)
|
||||
;
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Perry Hung.
|
||||
* Copyright (c) 2011, 2012 LeafLabs, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/adc.c
|
||||
* @author Marti Bolivar <mbolivar@leaflabs.com>,
|
||||
* Perry Hung <perry@leaflabs.com>
|
||||
* @brief Analog to digital converter routines
|
||||
*/
|
||||
|
||||
#include <libmaple/adc.h>
|
||||
#include <libmaple/libmaple.h>
|
||||
#include <libmaple/rcc.h>
|
||||
|
||||
/**
|
||||
* @brief Initialize an ADC peripheral.
|
||||
*
|
||||
* Initializes the RCC clock line for the given peripheral. Resets
|
||||
* ADC device registers.
|
||||
*
|
||||
* @param dev ADC peripheral to initialize
|
||||
*/
|
||||
void adc_init(const adc_dev *dev) {
|
||||
rcc_clk_enable(dev->clk_id);
|
||||
rcc_reset_dev(dev->clk_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set external event select for regular group
|
||||
* @param dev ADC device
|
||||
* @param event Event used to trigger the start of conversion.
|
||||
* @see adc_extsel_event
|
||||
*/
|
||||
void adc_set_extsel(const adc_dev *dev, adc_extsel_event event) {
|
||||
uint32 cr2 = dev->regs->CR2;
|
||||
cr2 &= ~ADC_CR2_EXTSEL;
|
||||
cr2 |= event;
|
||||
dev->regs->CR2 = cr2;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the sample rate for all channels on an ADC device.
|
||||
*
|
||||
* Don't call this during conversion.
|
||||
*
|
||||
* @param dev adc device
|
||||
* @param smp_rate sample rate to set
|
||||
* @see adc_smp_rate
|
||||
*/
|
||||
void adc_set_sample_rate(const adc_dev *dev, adc_smp_rate smp_rate) {
|
||||
uint32 adc_smpr1_val = 0, adc_smpr2_val = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
if (i < 8) {
|
||||
/* ADC_SMPR1 determines sample time for channels [10,17] */
|
||||
adc_smpr1_val |= smp_rate << (i * 3);
|
||||
}
|
||||
/* ADC_SMPR2 determines sample time for channels [0,9] */
|
||||
adc_smpr2_val |= smp_rate << (i * 3);
|
||||
}
|
||||
|
||||
dev->regs->SMPR1 = adc_smpr1_val;
|
||||
dev->regs->SMPR2 = adc_smpr2_val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Perform a single synchronous software triggered conversion on a
|
||||
* channel.
|
||||
* @param dev ADC device to use for reading.
|
||||
* @param channel channel to convert
|
||||
* @return conversion result
|
||||
*/
|
||||
uint16 adc_read(const adc_dev *dev, uint8 channel) {
|
||||
adc_reg_map *regs = dev->regs;
|
||||
|
||||
adc_set_reg_seqlen(dev, 1);
|
||||
|
||||
regs->SQR3 = channel;
|
||||
regs->CR2 |= ADC_CR2_SWSTART;
|
||||
while (!(regs->SR & ADC_SR_EOC))
|
||||
;
|
||||
|
||||
return (uint16)(regs->DR & ADC_DR_DATA);
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2012 LeafLabs, LLC.
|
||||
* Copyright (c) 2010 Perry Hung.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/stm32f1/adc.c
|
||||
* @author Marti Bolivar <mbolivar@leaflabs.com>,
|
||||
* Perry Hung <perry@leaflabs.com>
|
||||
* @brief STM32F1 ADC support.
|
||||
*/
|
||||
|
||||
#include <libmaple/adc.h>
|
||||
#include <libmaple/gpio.h>
|
||||
|
||||
/*
|
||||
* Devices
|
||||
*/
|
||||
|
||||
static adc_dev adc1 = {
|
||||
.regs = ADC1_BASE,
|
||||
.clk_id = RCC_ADC1,
|
||||
};
|
||||
/** ADC1 device. */
|
||||
const adc_dev *ADC1 = &adc1;
|
||||
|
||||
static adc_dev adc2 = {
|
||||
.regs = ADC2_BASE,
|
||||
.clk_id = RCC_ADC2,
|
||||
};
|
||||
/** ADC2 device. */
|
||||
const adc_dev *ADC2 = &adc2;
|
||||
|
||||
#if defined(STM32_HIGH_DENSITY) || defined(STM32_XL_DENSITY)
|
||||
static adc_dev adc3 = {
|
||||
.regs = ADC3_BASE,
|
||||
.clk_id = RCC_ADC3,
|
||||
};
|
||||
/** ADC3 device. */
|
||||
const adc_dev *ADC3 = &adc3;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* STM32F1 routines
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Calibrate an ADC peripheral
|
||||
*
|
||||
* Availability: STM32F1.
|
||||
*
|
||||
* @param dev adc device
|
||||
*/
|
||||
void adc_calibrate(const adc_dev *dev) {
|
||||
__io uint32 *rstcal_bit = bb_perip(&(dev->regs->CR2), 3);
|
||||
__io uint32 *cal_bit = bb_perip(&(dev->regs->CR2), 2);
|
||||
|
||||
*rstcal_bit = 1;
|
||||
while (*rstcal_bit)
|
||||
;
|
||||
|
||||
*cal_bit = 1;
|
||||
while (*cal_bit)
|
||||
;
|
||||
}
|
||||
|
||||
/*
|
||||
* Common routines
|
||||
*/
|
||||
|
||||
void adc_set_prescaler(adc_prescaler pre) {
|
||||
rcc_set_prescaler(RCC_PRESCALER_ADC, (uint32)pre);
|
||||
}
|
||||
|
||||
void adc_foreach(void (*fn)(const adc_dev*)) {
|
||||
fn(ADC1);
|
||||
fn(ADC2);
|
||||
#if defined(STM32_HIGH_DENSITY) || defined(STM32_XL_DENSITY)
|
||||
fn(ADC3);
|
||||
#endif
|
||||
}
|
||||
|
||||
void adc_config_gpio(const adc_dev *ignored, gpio_dev *gdev, uint8 bit) {
|
||||
gpio_set_mode(gdev, bit, GPIO_INPUT_ANALOG);
|
||||
}
|
||||
|
||||
void adc_enable_single_swstart(const adc_dev *dev) {
|
||||
adc_init(dev);
|
||||
adc_set_extsel(dev, ADC_SWSTART);
|
||||
adc_set_exttrig(dev, 1);
|
||||
adc_enable(dev);
|
||||
adc_calibrate(dev);
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 LeafLabs, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/stm32f1/bkp.c
|
||||
* @brief STM32F1 Backup register support.
|
||||
*/
|
||||
|
||||
#include <libmaple/bkp.h>
|
||||
#include <libmaple/pwr.h>
|
||||
#include <libmaple/rcc.h>
|
||||
#include <libmaple/bitband.h>
|
||||
|
||||
static inline __io uint32* data_register(uint8 reg);
|
||||
|
||||
bkp_dev bkp = {
|
||||
.regs = BKP_BASE,
|
||||
};
|
||||
/** Backup device. */
|
||||
const bkp_dev *BKP = &bkp;
|
||||
|
||||
/**
|
||||
* @brief Initialize backup interface.
|
||||
*
|
||||
* Enables the power and backup interface clocks, and resets the
|
||||
* backup device.
|
||||
*/
|
||||
void bkp_init(void) {
|
||||
/* Don't call pwr_init(), or you'll reset the device. We just
|
||||
* need the clock. */
|
||||
rcc_clk_enable(RCC_PWR);
|
||||
rcc_clk_enable(RCC_BKP);
|
||||
rcc_reset_dev(RCC_BKP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable write access to the backup registers. Backup interface must
|
||||
* be initialized for subsequent register writes to work.
|
||||
* @see bkp_init()
|
||||
*/
|
||||
void bkp_enable_writes(void) {
|
||||
*bb_perip(&PWR_BASE->CR, PWR_CR_DBP_BIT) = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable write access to the backup registers.
|
||||
*/
|
||||
void bkp_disable_writes(void) {
|
||||
*bb_perip(&PWR_BASE->CR, PWR_CR_DBP_BIT) = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a value from given backup data register.
|
||||
* @param reg Data register to read, from 1 to BKP_NR_DATA_REGS (10 on
|
||||
* medium-density devices, 42 on high-density devices).
|
||||
*/
|
||||
uint16 bkp_read(uint8 reg) {
|
||||
__io uint32* dr = data_register(reg);
|
||||
if (!dr) {
|
||||
ASSERT(0); /* nonexistent register */
|
||||
return 0;
|
||||
}
|
||||
return (uint16)*dr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write a value to given data register.
|
||||
*
|
||||
* Write access to backup registers must be enabled.
|
||||
*
|
||||
* @param reg Data register to write, from 1 to BKP_NR_DATA_REGS (10
|
||||
* on medium-density devices, 42 on high-density devices).
|
||||
* @param val Value to write into the register.
|
||||
* @see bkp_enable_writes()
|
||||
*/
|
||||
void bkp_write(uint8 reg, uint16 val) {
|
||||
__io uint32* dr = data_register(reg);
|
||||
if (!dr) {
|
||||
ASSERT(0); /* nonexistent register */
|
||||
return;
|
||||
}
|
||||
*dr = (uint32)val;
|
||||
}
|
||||
|
||||
/*
|
||||
* Data register memory layout is not contiguous. It's split up from
|
||||
* 1--NR_LOW_DRS, beginning at BKP_BASE->DR1, through to
|
||||
* (NR_LOW_DRS+1)--BKP_NR_DATA_REGS, beginning at BKP_BASE->DR11.
|
||||
*/
|
||||
#define NR_LOW_DRS 10
|
||||
|
||||
static inline __io uint32* data_register(uint8 reg) {
|
||||
if (reg < 1 || reg > BKP_NR_DATA_REGS) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if BKP_NR_DATA_REGS == NR_LOW_DRS
|
||||
return (uint32*)BKP_BASE + reg;
|
||||
#else
|
||||
if (reg <= NR_LOW_DRS) {
|
||||
return (uint32*)BKP_BASE + reg;
|
||||
} else {
|
||||
return (uint32*)&(BKP_BASE->DR11) + (reg - NR_LOW_DRS - 1);
|
||||
}
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Bryan Newbold.
|
||||
* Copyright (c) 2011, 2012 LeafLabs, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/dac.c
|
||||
* @brief Digital to analog converter support.
|
||||
*/
|
||||
|
||||
#include <libmaple/dac.h>
|
||||
#include <libmaple/libmaple.h>
|
||||
#include <libmaple/gpio.h>
|
||||
|
||||
#if STM32_HAVE_DAC
|
||||
dac_dev dac = {
|
||||
.regs = DAC_BASE,
|
||||
};
|
||||
const dac_dev *DAC = &dac;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Initialize the digital to analog converter
|
||||
* @param dev DAC device
|
||||
* @param flags Flags:
|
||||
* DAC_CH1: Enable channel 1
|
||||
* DAC_CH2: Enable channel 2
|
||||
* @sideeffect May set PA4 or PA5 to INPUT_ANALOG
|
||||
*/
|
||||
void dac_init(const dac_dev *dev, uint32 flags) {
|
||||
/* First turn on the clock */
|
||||
rcc_clk_enable(RCC_DAC);
|
||||
rcc_reset_dev(RCC_DAC);
|
||||
|
||||
if (flags & DAC_CH1) {
|
||||
dac_enable_channel(dev, 1);
|
||||
}
|
||||
|
||||
if (flags & DAC_CH2) {
|
||||
dac_enable_channel(dev, 2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write a 12-bit value to the DAC to output
|
||||
* @param dev DAC device
|
||||
* @param channel channel to select (1 or 2)
|
||||
* @param val value to write
|
||||
*/
|
||||
void dac_write_channel(const dac_dev *dev, uint8 channel, uint16 val) {
|
||||
switch(channel) {
|
||||
case 1:
|
||||
dev->regs->DHR12R1 = DAC_DHR12R1_DACC1DHR & val;
|
||||
break;
|
||||
case 2:
|
||||
dev->regs->DHR12R2 = DAC_DHR12R2_DACC2DHR & val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable a DAC channel
|
||||
* @param dev DAC device
|
||||
* @param channel channel to enable, either 1 or 2
|
||||
* @sideeffect May change pin mode of PA4 or PA5
|
||||
*/
|
||||
void dac_enable_channel(const dac_dev *dev, uint8 channel) {
|
||||
/*
|
||||
* Setup ANALOG mode on PA4 and PA5. This mapping is consistent
|
||||
* across all supported STM32s with a DAC.
|
||||
*/
|
||||
switch (channel) {
|
||||
case 1:
|
||||
gpio_set_mode(GPIOA, 4, GPIO_MODE_ANALOG);
|
||||
dev->regs->CR |= DAC_CR_EN1;
|
||||
break;
|
||||
case 2:
|
||||
gpio_set_mode(GPIOA, 5, GPIO_MODE_ANALOG);
|
||||
dev->regs->CR |= DAC_CR_EN2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disable a DAC channel
|
||||
* @param dev DAC device
|
||||
* @param channel channel to disable, either 1 or 2
|
||||
*/
|
||||
void dac_disable_channel(const dac_dev *dev, uint8 channel) {
|
||||
switch (channel) {
|
||||
case 1:
|
||||
dev->regs->CR &= ~DAC_CR_EN1;
|
||||
break;
|
||||
case 2:
|
||||
dev->regs->CR &= ~DAC_CR_EN2;
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Michael Hope.
|
||||
* Copyright (c) 2012 LeafLabs, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/dma.c
|
||||
* @author Marti Bolivar <mbolivar@leaflabs.com>;
|
||||
* Original implementation by Michael Hope
|
||||
* @brief Portable DMA routines.
|
||||
*/
|
||||
|
||||
#include <libmaple/dma.h>
|
||||
#include "dma_private.h"
|
||||
#include "stm32_private.h"
|
||||
|
||||
/*
|
||||
* Convenience routines
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Initialize a DMA device.
|
||||
* @param dev Device to initialize.
|
||||
*/
|
||||
void dma_init(dma_dev *dev) {
|
||||
rcc_clk_enable(dev->clk_id);
|
||||
}
|
||||
|
||||
/*
|
||||
* Private API
|
||||
*/
|
||||
|
||||
enum dma_atype _dma_addr_type(__io void *addr) {
|
||||
switch (stm32_block_purpose((void*)addr)) {
|
||||
/* Notice we're treating the code block as memory here. That's
|
||||
* correct for addresses in Flash and in [0x0, 0x7FFFFFF]
|
||||
* (provided that those addresses are aliased to Flash, SRAM, or
|
||||
* FSMC, depending on BOOT[01] and possibly SYSCFG_MEMRMP). It's
|
||||
* not correct for other addresses in the code block, but those
|
||||
* will (hopefully) just fail-fast with transfer or bus errors. If
|
||||
* lots of people get confused, it might be worth being more
|
||||
* careful here. */
|
||||
case STM32_BLOCK_CODE: /* Fall through */
|
||||
case STM32_BLOCK_SRAM: /* ... */
|
||||
case STM32_BLOCK_FSMC_1_2: /* ... */
|
||||
case STM32_BLOCK_FSMC_3_4:
|
||||
return DMA_ATYPE_MEM;
|
||||
case STM32_BLOCK_PERIPH:
|
||||
return DMA_ATYPE_PER;
|
||||
case STM32_BLOCK_FSMC_REG: /* Fall through */
|
||||
/* Is this right? I can't think of a reason to DMA into or out
|
||||
* of the FSMC registers. [mbolivar] */
|
||||
case STM32_BLOCK_UNUSED: /* ... */
|
||||
case STM32_BLOCK_CORTEX_INTERNAL: /* ... */
|
||||
return DMA_ATYPE_OTHER;
|
||||
default:
|
||||
ASSERT(0); /* Can't happen */
|
||||
return DMA_ATYPE_OTHER;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,413 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Michael Hope.
|
||||
* Copyright (c) 2012 LeafLabs, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/stm32f1/dma.c
|
||||
* @author Marti Bolivar <mbolivar@leaflabs.com>;
|
||||
* Original implementation by Michael Hope
|
||||
* @brief STM32F1 DMA support.
|
||||
*/
|
||||
|
||||
#include <libmaple/dma.h>
|
||||
#include <libmaple/bitband.h>
|
||||
|
||||
/* Hack to ensure inlining in dma_irq_handler() */
|
||||
#define DMA_GET_HANDLER(dev, tube) (dev->handlers[tube - 1].handler)
|
||||
#include "dma_private.h"
|
||||
|
||||
/*
|
||||
* Devices
|
||||
*/
|
||||
|
||||
static dma_dev dma1 = {
|
||||
.regs = DMA1_BASE,
|
||||
.clk_id = RCC_DMA1,
|
||||
.handlers = {{ .handler = NULL, .irq_line = NVIC_DMA_CH1 },
|
||||
{ .handler = NULL, .irq_line = NVIC_DMA_CH2 },
|
||||
{ .handler = NULL, .irq_line = NVIC_DMA_CH3 },
|
||||
{ .handler = NULL, .irq_line = NVIC_DMA_CH4 },
|
||||
{ .handler = NULL, .irq_line = NVIC_DMA_CH5 },
|
||||
{ .handler = NULL, .irq_line = NVIC_DMA_CH6 },
|
||||
{ .handler = NULL, .irq_line = NVIC_DMA_CH7 }},
|
||||
};
|
||||
/** STM32F1 DMA1 device */
|
||||
dma_dev *DMA1 = &dma1;
|
||||
|
||||
#if defined(STM32_HIGH_DENSITY) || defined(STM32_XL_DENSITY)
|
||||
static dma_dev dma2 = {
|
||||
.regs = DMA2_BASE,
|
||||
.clk_id = RCC_DMA2,
|
||||
.handlers = {{ .handler = NULL, .irq_line = NVIC_DMA2_CH1 },
|
||||
{ .handler = NULL, .irq_line = NVIC_DMA2_CH2 },
|
||||
{ .handler = NULL, .irq_line = NVIC_DMA2_CH3 },
|
||||
{ .handler = NULL, .irq_line = NVIC_DMA2_CH_4_5 },
|
||||
{ .handler = NULL, .irq_line = NVIC_DMA2_CH_4_5 }}, /* !@#$ */
|
||||
};
|
||||
/** STM32F1 DMA2 device */
|
||||
dma_dev *DMA2 = &dma2;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Auxiliary routines
|
||||
*/
|
||||
|
||||
/* Can channel serve cfg->tube_req_src? */
|
||||
static int cfg_req_ok(dma_channel channel, dma_tube_config *cfg) {
|
||||
return (cfg->tube_req_src & 0x7) == channel;
|
||||
}
|
||||
|
||||
/* Can dev serve cfg->tube_req_src? */
|
||||
static int cfg_dev_ok(dma_dev *dev, dma_tube_config *cfg) {
|
||||
return (rcc_clk_id)(cfg->tube_req_src >> 3) == dev->clk_id;
|
||||
}
|
||||
|
||||
/* Is addr acceptable for use as DMA src/dst? */
|
||||
static int cfg_mem_ok(__io void *addr) {
|
||||
enum dma_atype atype = _dma_addr_type(addr);
|
||||
return atype == DMA_ATYPE_MEM || atype == DMA_ATYPE_PER;
|
||||
}
|
||||
|
||||
/* Is the direction implied by src->dst supported? */
|
||||
static int cfg_dir_ok(dma_tube_config *cfg) {
|
||||
/* We can't do peripheral->peripheral transfers. */
|
||||
return ((_dma_addr_type(cfg->tube_src) == DMA_ATYPE_MEM) ||
|
||||
(_dma_addr_type(cfg->tube_dst) == DMA_ATYPE_MEM));
|
||||
}
|
||||
|
||||
static int preconfig_check(dma_dev *dev, dma_channel channel,
|
||||
dma_tube_config *cfg) {
|
||||
if (!cfg_req_ok(channel, cfg)) {
|
||||
return -DMA_TUBE_CFG_EREQ;
|
||||
}
|
||||
if (cfg->tube_nr_xfers > 65535) {
|
||||
return -DMA_TUBE_CFG_ENDATA;
|
||||
}
|
||||
if (!cfg_dev_ok(dev, cfg)) {
|
||||
return -DMA_TUBE_CFG_EDEV;
|
||||
}
|
||||
if (!cfg_mem_ok(cfg->tube_src)) {
|
||||
return -DMA_TUBE_CFG_ESRC;
|
||||
}
|
||||
if (!cfg_mem_ok(cfg->tube_dst)) {
|
||||
return -DMA_TUBE_CFG_EDST;
|
||||
}
|
||||
if (!cfg_dir_ok(cfg)) {
|
||||
return -DMA_TUBE_CFG_EDIR;
|
||||
}
|
||||
return DMA_TUBE_CFG_SUCCESS;
|
||||
}
|
||||
|
||||
static inline void set_ccr(dma_tube_reg_map *chregs,
|
||||
dma_xfer_size msize, int minc,
|
||||
dma_xfer_size psize, int pinc,
|
||||
uint32 other_flags) {
|
||||
chregs->CCR = ((msize << 10) | (psize << 8) |
|
||||
(minc ? DMA_CCR_MINC : 0) | (pinc ? DMA_CCR_PINC : 0) |
|
||||
other_flags);
|
||||
}
|
||||
|
||||
static inline uint32 cfg_ccr_flags(unsigned tube_flags) {
|
||||
/* DMA_CFG_SRC_INC and DMA_CFG_DST_INC are special */
|
||||
return tube_flags & ~(DMA_CFG_SRC_INC | DMA_CFG_DST_INC);
|
||||
}
|
||||
|
||||
/* Configure chregs according to cfg, where cfg->tube_dst is peripheral. */
|
||||
static int config_to_per(dma_tube_reg_map *chregs, dma_tube_config *cfg) {
|
||||
/* Check that ->tube_src is memory (if it's anything else, we
|
||||
* shouldn't have been called). */
|
||||
ASSERT(_dma_addr_type(cfg->tube_src) == DMA_ATYPE_MEM);
|
||||
|
||||
set_ccr(chregs,
|
||||
cfg->tube_src_size, cfg->tube_flags & DMA_CFG_SRC_INC,
|
||||
cfg->tube_dst_size, cfg->tube_flags & DMA_CFG_DST_INC,
|
||||
(cfg_ccr_flags(cfg->tube_flags) | DMA_CCR_DIR_FROM_MEM));
|
||||
chregs->CNDTR = cfg->tube_nr_xfers;
|
||||
chregs->CMAR = (uint32)cfg->tube_src;
|
||||
chregs->CPAR = (uint32)cfg->tube_dst;
|
||||
return DMA_TUBE_CFG_SUCCESS;
|
||||
}
|
||||
|
||||
/* Configure chregs according to cfg, where cfg->tube_dst is memory. */
|
||||
static int config_to_mem(dma_tube_reg_map *chregs, dma_tube_config *cfg) {
|
||||
uint32 mem2mem;
|
||||
|
||||
if ((_dma_addr_type(cfg->tube_src) == DMA_ATYPE_MEM) &&
|
||||
(cfg->tube_flags & DMA_CFG_CIRC)) {
|
||||
/* Can't do mem-to-mem and circular mode */
|
||||
return -DMA_TUBE_CFG_ECFG;
|
||||
}
|
||||
|
||||
mem2mem = (_dma_addr_type(cfg->tube_src) == DMA_ATYPE_MEM ?
|
||||
DMA_CCR_MEM2MEM : 0);
|
||||
set_ccr(chregs,
|
||||
cfg->tube_dst_size, cfg->tube_flags & DMA_CFG_DST_INC,
|
||||
cfg->tube_src_size, cfg->tube_flags & DMA_CFG_SRC_INC,
|
||||
(cfg_ccr_flags(cfg->tube_flags) |
|
||||
DMA_CCR_DIR_FROM_PER |
|
||||
mem2mem));
|
||||
chregs->CNDTR = cfg->tube_nr_xfers;
|
||||
chregs->CMAR = (uint32)cfg->tube_dst;
|
||||
chregs->CPAR = (uint32)cfg->tube_src;
|
||||
return DMA_TUBE_CFG_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Routines
|
||||
*/
|
||||
|
||||
int dma_tube_cfg(dma_dev *dev, dma_channel channel, dma_tube_config *cfg) {
|
||||
dma_tube_reg_map *chregs;
|
||||
int ret = preconfig_check(dev, channel, cfg);
|
||||
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
dma_disable(dev, channel); /* Must disable before reconfiguring */
|
||||
dma_clear_isr_bits(dev, channel); /* For sanity and consistency
|
||||
* with STM32F2. */
|
||||
|
||||
chregs = dma_tube_regs(dev, channel);
|
||||
switch (_dma_addr_type(cfg->tube_dst)) {
|
||||
case DMA_ATYPE_PER:
|
||||
ret = config_to_per(chregs, cfg);
|
||||
break;
|
||||
case DMA_ATYPE_MEM:
|
||||
ret = config_to_mem(chregs, cfg);
|
||||
break;
|
||||
default:
|
||||
/* Can't happen */
|
||||
ASSERT(0);
|
||||
return -DMA_TUBE_CFG_ECFG;
|
||||
}
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
chregs->CNDTR = cfg->tube_nr_xfers;
|
||||
return DMA_TUBE_CFG_SUCCESS;
|
||||
}
|
||||
|
||||
void dma_set_priority(dma_dev *dev,
|
||||
dma_channel channel,
|
||||
dma_priority priority) {
|
||||
dma_channel_reg_map *channel_regs;
|
||||
uint32 ccr;
|
||||
|
||||
ASSERT_FAULT(!dma_is_channel_enabled(dev, channel));
|
||||
|
||||
channel_regs = dma_channel_regs(dev, channel);
|
||||
ccr = channel_regs->CCR;
|
||||
ccr &= ~DMA_CCR_PL;
|
||||
ccr |= (priority << 12);
|
||||
channel_regs->CCR = ccr;
|
||||
}
|
||||
|
||||
void dma_set_num_transfers(dma_dev *dev,
|
||||
dma_channel channel,
|
||||
uint16 num_transfers) {
|
||||
dma_channel_reg_map *channel_regs;
|
||||
|
||||
ASSERT_FAULT(!dma_is_channel_enabled(dev, channel));
|
||||
|
||||
channel_regs = dma_channel_regs(dev, channel);
|
||||
channel_regs->CNDTR = num_transfers;
|
||||
}
|
||||
|
||||
void dma_attach_interrupt(dma_dev *dev, dma_channel channel,
|
||||
void (*handler)(void)) {
|
||||
DMA_GET_HANDLER(dev, channel) = handler;
|
||||
nvic_irq_enable(dev->handlers[channel - 1].irq_line);
|
||||
}
|
||||
|
||||
void dma_detach_interrupt(dma_dev *dev, dma_channel channel) {
|
||||
/* Don't use nvic_irq_disable()! Think about DMA2 channels 4 and 5. */
|
||||
dma_channel_regs(dev, channel)->CCR &= ~0xF;
|
||||
DMA_GET_HANDLER(dev, channel) = NULL;
|
||||
}
|
||||
|
||||
void dma_enable(dma_dev *dev, dma_channel channel) {
|
||||
dma_channel_reg_map *chan_regs = dma_channel_regs(dev, channel);
|
||||
bb_peri_set_bit(&chan_regs->CCR, DMA_CCR_EN_BIT, 1);
|
||||
}
|
||||
|
||||
void dma_disable(dma_dev *dev, dma_channel channel) {
|
||||
dma_channel_reg_map *chan_regs = dma_channel_regs(dev, channel);
|
||||
bb_peri_set_bit(&chan_regs->CCR, DMA_CCR_EN_BIT, 0);
|
||||
}
|
||||
|
||||
dma_irq_cause dma_get_irq_cause(dma_dev *dev, dma_channel channel) {
|
||||
/* Grab and clear the ISR bits. */
|
||||
uint8 status_bits = dma_get_isr_bits(dev, channel);
|
||||
dma_clear_isr_bits(dev, channel);
|
||||
|
||||
/* If the channel global interrupt flag is cleared, then
|
||||
* something's very wrong. */
|
||||
ASSERT(status_bits & 0x1);
|
||||
/* If GIF is set, then some other flag should be set, barring
|
||||
* something unexpected (e.g. the user making an unforeseen IFCR
|
||||
* write). */
|
||||
ASSERT(status_bits != 0x1);
|
||||
|
||||
/* ISR flags get set even if the corresponding interrupt enable
|
||||
* bits in the channel's configuration register are cleared, so we
|
||||
* can't use a switch here.
|
||||
*
|
||||
* Don't change the order of these if statements. */
|
||||
if (status_bits & 0x8) {
|
||||
return DMA_TRANSFER_ERROR;
|
||||
} else if (status_bits & 0x2) {
|
||||
return DMA_TRANSFER_COMPLETE;
|
||||
} else if (status_bits & 0x4) {
|
||||
return DMA_TRANSFER_HALF_COMPLETE;
|
||||
}
|
||||
|
||||
/* If we get here, one of our assumptions has been violated, but
|
||||
* the debug level is too low for the above ASSERTs() to have had
|
||||
* any effect. In order to fail fast, mimic the DMA controller's
|
||||
* behavior when an error occurs. */
|
||||
dma_disable(dev, channel);
|
||||
return DMA_TRANSFER_ERROR;
|
||||
}
|
||||
|
||||
void dma_set_mem_addr(dma_dev *dev, dma_channel channel, __io void *addr) {
|
||||
dma_channel_reg_map *chan_regs;
|
||||
|
||||
ASSERT_FAULT(!dma_is_channel_enabled(dev, channel));
|
||||
|
||||
chan_regs = dma_channel_regs(dev, channel);
|
||||
chan_regs->CMAR = (uint32)addr;
|
||||
}
|
||||
|
||||
void dma_set_per_addr(dma_dev *dev, dma_channel channel, __io void *addr) {
|
||||
dma_channel_reg_map *chan_regs;
|
||||
|
||||
ASSERT_FAULT(!dma_is_channel_enabled(dev, channel));
|
||||
|
||||
chan_regs = dma_channel_regs(dev, channel);
|
||||
chan_regs->CPAR = (uint32)addr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deprecated. Use dma_tube_cfg() instead.
|
||||
*
|
||||
* Set up a DMA transfer.
|
||||
*
|
||||
* The channel will be disabled before being reconfigured. The
|
||||
* transfer will have low priority by default. You may choose another
|
||||
* priority before the transfer begins using dma_set_priority(), as
|
||||
* well as performing any other configuration you desire. When the
|
||||
* channel is configured to your liking, enable it using dma_enable().
|
||||
*
|
||||
* @param dev DMA device.
|
||||
* @param channel DMA channel.
|
||||
* @param peripheral_address Base address of peripheral data register
|
||||
* involved in the transfer.
|
||||
* @param peripheral_size Peripheral data transfer size.
|
||||
* @param memory_address Base memory address involved in the transfer.
|
||||
* @param memory_size Memory data transfer size.
|
||||
* @param mode Logical OR of dma_mode_flags
|
||||
*
|
||||
* @see dma_tube_cfg()
|
||||
*
|
||||
* @sideeffect Disables the given DMA channel.
|
||||
* @see dma_xfer_size
|
||||
* @see dma_mode_flags
|
||||
* @see dma_set_num_transfers()
|
||||
* @see dma_set_priority()
|
||||
* @see dma_attach_interrupt()
|
||||
* @see dma_enable()
|
||||
*/
|
||||
__deprecated
|
||||
void dma_setup_transfer(dma_dev *dev,
|
||||
dma_channel channel,
|
||||
__io void *peripheral_address,
|
||||
dma_xfer_size peripheral_size,
|
||||
__io void *memory_address,
|
||||
dma_xfer_size memory_size,
|
||||
uint32 mode) {
|
||||
dma_channel_reg_map *channel_regs = dma_channel_regs(dev, channel);
|
||||
|
||||
dma_disable(dev, channel); /* can't write to CMAR/CPAR otherwise */
|
||||
channel_regs->CCR = (memory_size << 10) | (peripheral_size << 8) | mode;
|
||||
channel_regs->CMAR = (uint32)memory_address;
|
||||
channel_regs->CPAR = (uint32)peripheral_address;
|
||||
}
|
||||
|
||||
/*
|
||||
* IRQ handlers
|
||||
*/
|
||||
|
||||
void __irq_dma1_channel1(void) {
|
||||
dma_irq_handler(DMA1, DMA_CH1);
|
||||
}
|
||||
|
||||
void __irq_dma1_channel2(void) {
|
||||
dma_irq_handler(DMA1, DMA_CH2);
|
||||
}
|
||||
|
||||
void __irq_dma1_channel3(void) {
|
||||
dma_irq_handler(DMA1, DMA_CH3);
|
||||
}
|
||||
|
||||
void __irq_dma1_channel4(void) {
|
||||
dma_irq_handler(DMA1, DMA_CH4);
|
||||
}
|
||||
|
||||
void __irq_dma1_channel5(void) {
|
||||
dma_irq_handler(DMA1, DMA_CH5);
|
||||
}
|
||||
|
||||
void __irq_dma1_channel6(void) {
|
||||
dma_irq_handler(DMA1, DMA_CH6);
|
||||
}
|
||||
|
||||
void __irq_dma1_channel7(void) {
|
||||
dma_irq_handler(DMA1, DMA_CH7);
|
||||
}
|
||||
|
||||
#if defined(STM32_HIGH_DENSITY) || defined(STM32_XL_DENSITY)
|
||||
void __irq_dma2_channel1(void) {
|
||||
dma_irq_handler(DMA2, DMA_CH1);
|
||||
}
|
||||
|
||||
void __irq_dma2_channel2(void) {
|
||||
dma_irq_handler(DMA2, DMA_CH2);
|
||||
}
|
||||
|
||||
void __irq_dma2_channel3(void) {
|
||||
dma_irq_handler(DMA2, DMA_CH3);
|
||||
}
|
||||
|
||||
void __irq_dma2_channel4_5(void) {
|
||||
if ((DMA2_BASE->CCR4 & DMA_CCR_EN) && (DMA2_BASE->ISR & DMA_ISR_GIF4)) {
|
||||
dma_irq_handler(DMA2, DMA_CH4);
|
||||
}
|
||||
if ((DMA2_BASE->CCR5 & DMA_CCR_EN) && (DMA2_BASE->ISR & DMA_ISR_GIF5)) {
|
||||
dma_irq_handler(DMA2, DMA_CH5);
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,101 @@
|
|||
/* *****************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Perry Hung.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
* ****************************************************************************/
|
||||
|
||||
# On an exception, push a fake stack thread mode stack frame and redirect
|
||||
# thread execution to a thread mode error handler
|
||||
|
||||
# From RM008:
|
||||
# The SP is decremented by eight words by the completion of the stack push.
|
||||
# Figure 5-1 shows the contents of the stack after an exception pre-empts the
|
||||
# current program flow.
|
||||
#
|
||||
# Old SP--> <previous>
|
||||
# xPSR
|
||||
# PC
|
||||
# LR
|
||||
# r12
|
||||
# r3
|
||||
# r2
|
||||
# r1
|
||||
# SP--> r0
|
||||
|
||||
.text
|
||||
.globl __exc_hardfault
|
||||
.globl __exc_nmi
|
||||
.globl __exc_hardfault
|
||||
.globl __exc_memmanage
|
||||
.globl __exc_busfault
|
||||
.globl __exc_usagefault
|
||||
|
||||
.code 16
|
||||
.thumb_func
|
||||
__exc_nmi:
|
||||
mov r0, #1
|
||||
b __default_exc
|
||||
|
||||
.thumb_func
|
||||
__exc_hardfault:
|
||||
mov r0, #2
|
||||
b __default_exc
|
||||
|
||||
.thumb_func
|
||||
__exc_memmanage:
|
||||
mov r0, #3
|
||||
b __default_exc
|
||||
|
||||
.thumb_func
|
||||
__exc_busfault:
|
||||
mov r0, #4
|
||||
b __default_exc
|
||||
|
||||
.thumb_func
|
||||
__exc_usagefault:
|
||||
mov r0, #5
|
||||
b __default_exc
|
||||
|
||||
.thumb_func
|
||||
__default_exc:
|
||||
ldr r2, NVIC_CCR @ Enable returning to thread mode even if there are
|
||||
mov r1 ,#1 @ pending exceptions. See flag NONEBASETHRDENA.
|
||||
str r1, [r2]
|
||||
cpsid i @ Disable global interrupts
|
||||
ldr r2, SYSTICK_CSR @ Disable systick handler
|
||||
mov r1, #0
|
||||
str r1, [r2]
|
||||
ldr r1, CPSR_MASK @ Set default CPSR
|
||||
push {r1}
|
||||
ldr r1, TARGET_PC @ Set target pc
|
||||
push {r1}
|
||||
sub sp, sp, #24 @ Don't care
|
||||
ldr r1, EXC_RETURN @ Return to thread mode
|
||||
mov lr, r1
|
||||
bx lr @ Exception exit
|
||||
|
||||
.align 4
|
||||
CPSR_MASK: .word 0x61000000
|
||||
EXC_RETURN: .word 0xFFFFFFF9
|
||||
TARGET_PC: .word __error
|
||||
NVIC_CCR: .word 0xE000ED14 @ NVIC configuration control register
|
||||
SYSTICK_CSR: .word 0xE000E010 @ Systick control register
|
||||
|
|
@ -0,0 +1,292 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Perry Hung.
|
||||
* Copyright (c) 2011, 2012 LeafLabs, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/exti.c
|
||||
* @brief External interrupt control routines
|
||||
*/
|
||||
|
||||
#include <libmaple/exti.h>
|
||||
#include <libmaple/libmaple.h>
|
||||
#include <libmaple/nvic.h>
|
||||
#include <libmaple/bitband.h>
|
||||
|
||||
static inline void dispatch_single_exti(uint32 exti_num);
|
||||
static inline void dispatch_extis(uint32 start, uint32 stop);
|
||||
|
||||
/*
|
||||
* Internal state
|
||||
*/
|
||||
|
||||
typedef struct exti_channel {
|
||||
void (*handler)(void *);
|
||||
void *arg;
|
||||
} exti_channel;
|
||||
|
||||
static exti_channel exti_channels[] = {
|
||||
{ .handler = NULL, .arg = NULL }, // EXTI0
|
||||
{ .handler = NULL, .arg = NULL }, // EXTI1
|
||||
{ .handler = NULL, .arg = NULL }, // EXTI2
|
||||
{ .handler = NULL, .arg = NULL }, // EXTI3
|
||||
{ .handler = NULL, .arg = NULL }, // EXTI4
|
||||
{ .handler = NULL, .arg = NULL }, // EXTI5
|
||||
{ .handler = NULL, .arg = NULL }, // EXTI6
|
||||
{ .handler = NULL, .arg = NULL }, // EXTI7
|
||||
{ .handler = NULL, .arg = NULL }, // EXTI8
|
||||
{ .handler = NULL, .arg = NULL }, // EXTI9
|
||||
{ .handler = NULL, .arg = NULL }, // EXTI10
|
||||
{ .handler = NULL, .arg = NULL }, // EXTI11
|
||||
{ .handler = NULL, .arg = NULL }, // EXTI12
|
||||
{ .handler = NULL, .arg = NULL }, // EXTI13
|
||||
{ .handler = NULL, .arg = NULL }, // EXTI14
|
||||
{ .handler = NULL, .arg = NULL }, // EXTI15
|
||||
};
|
||||
|
||||
/*
|
||||
* Portable routines
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Register a handler to run upon external interrupt.
|
||||
*
|
||||
* This function assumes that the interrupt request corresponding to
|
||||
* the given external interrupt is masked.
|
||||
*
|
||||
* @param num External interrupt line number.
|
||||
* @param port Port to use as source input for external interrupt.
|
||||
* @param handler Function handler to execute when interrupt is triggered.
|
||||
* @param mode Type of transition to trigger on, one of:
|
||||
* EXTI_RISING, EXTI_FALLING, EXTI_RISING_FALLING.
|
||||
* @see exti_num
|
||||
* @see exti_cfg
|
||||
* @see voidFuncPtr
|
||||
* @see exti_trigger_mode
|
||||
*/
|
||||
void exti_attach_interrupt(exti_num num,
|
||||
exti_cfg port,
|
||||
voidFuncPtr handler,
|
||||
exti_trigger_mode mode) {
|
||||
// Call callback version with arg being null
|
||||
exti_attach_callback(num, port, (voidArgumentFuncPtr)handler, NULL, mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Register a handler with an argument to run upon external interrupt.
|
||||
*
|
||||
* This function assumes that the interrupt request corresponding to
|
||||
* the given external interrupt is masked.
|
||||
*
|
||||
* @param num External interrupt line number.
|
||||
* @param port Port to use as source input for external interrupt.
|
||||
* @param handler Function handler to execute when interrupt is triggered.
|
||||
* @param arg Argument to pass to the interrupt handler.
|
||||
* @param mode Type of transition to trigger on, one of:
|
||||
* EXTI_RISING, EXTI_FALLING, EXTI_RISING_FALLING.
|
||||
* @see exti_num
|
||||
* @see exti_cfg
|
||||
* @see voidFuncPtr
|
||||
* @see exti_trigger_mode
|
||||
*/
|
||||
void exti_attach_callback(exti_num num,
|
||||
exti_cfg port,
|
||||
voidArgumentFuncPtr handler,
|
||||
void *arg,
|
||||
exti_trigger_mode mode) {
|
||||
ASSERT(handler);
|
||||
|
||||
/* Register the handler */
|
||||
exti_channels[num].handler = handler;
|
||||
exti_channels[num].arg = arg;
|
||||
|
||||
/* Set trigger mode */
|
||||
switch (mode) {
|
||||
case EXTI_RISING:
|
||||
bb_peri_set_bit(&EXTI_BASE->RTSR, num, 1);
|
||||
break;
|
||||
case EXTI_FALLING:
|
||||
bb_peri_set_bit(&EXTI_BASE->FTSR, num, 1);
|
||||
break;
|
||||
case EXTI_RISING_FALLING:
|
||||
bb_peri_set_bit(&EXTI_BASE->RTSR, num, 1);
|
||||
bb_peri_set_bit(&EXTI_BASE->FTSR, num, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Use the chip-specific exti_select() to map num to port */
|
||||
exti_select(num, port);
|
||||
|
||||
/* Unmask external interrupt request */
|
||||
bb_peri_set_bit(&EXTI_BASE->IMR, num, 1);
|
||||
|
||||
/* Enable the interrupt line */
|
||||
switch(num)
|
||||
{
|
||||
case EXTI0:
|
||||
nvic_irq_enable(NVIC_EXTI0);
|
||||
break;
|
||||
case EXTI1:
|
||||
nvic_irq_enable(NVIC_EXTI1);
|
||||
break;
|
||||
case EXTI2:
|
||||
nvic_irq_enable(NVIC_EXTI2);
|
||||
break;
|
||||
case EXTI3:
|
||||
nvic_irq_enable(NVIC_EXTI3);
|
||||
break;
|
||||
case EXTI4:
|
||||
nvic_irq_enable(NVIC_EXTI4);
|
||||
break;
|
||||
case EXTI5:
|
||||
case EXTI6:
|
||||
case EXTI7:
|
||||
case EXTI8:
|
||||
case EXTI9:
|
||||
nvic_irq_enable(NVIC_EXTI_9_5);
|
||||
break;
|
||||
case EXTI10:
|
||||
case EXTI11:
|
||||
case EXTI12:
|
||||
case EXTI13:
|
||||
case EXTI14:
|
||||
case EXTI15:
|
||||
nvic_irq_enable(NVIC_EXTI_15_10);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Unregister an external interrupt handler
|
||||
* @param num External interrupt line to disable.
|
||||
* @see exti_num
|
||||
*/
|
||||
void exti_detach_interrupt(exti_num num) {
|
||||
/* First, mask the interrupt request */
|
||||
bb_peri_set_bit(&EXTI_BASE->IMR, num, 0);
|
||||
|
||||
/* Then, clear the trigger selection registers */
|
||||
bb_peri_set_bit(&EXTI_BASE->FTSR, num, 0);
|
||||
bb_peri_set_bit(&EXTI_BASE->RTSR, num, 0);
|
||||
|
||||
/* Finally, unregister the user's handler */
|
||||
exti_channels[num].handler = NULL;
|
||||
exti_channels[num].arg = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Private routines
|
||||
*/
|
||||
|
||||
void exti_do_select(__io uint32 *exti_cr, exti_num num, exti_cfg port) {
|
||||
uint32 shift = 4 * (num % 4);
|
||||
uint32 cr = *exti_cr;
|
||||
cr &= ~(0xF << shift);
|
||||
cr |= port << shift;
|
||||
*exti_cr = cr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Interrupt handlers
|
||||
*/
|
||||
|
||||
void __irq_exti0(void) {
|
||||
dispatch_single_exti(EXTI0);
|
||||
}
|
||||
|
||||
void __irq_exti1(void) {
|
||||
dispatch_single_exti(EXTI1);
|
||||
}
|
||||
|
||||
void __irq_exti2(void) {
|
||||
dispatch_single_exti(EXTI2);
|
||||
}
|
||||
|
||||
void __irq_exti3(void) {
|
||||
dispatch_single_exti(EXTI3);
|
||||
}
|
||||
|
||||
void __irq_exti4(void) {
|
||||
dispatch_single_exti(EXTI4);
|
||||
}
|
||||
|
||||
void __irq_exti9_5(void) {
|
||||
dispatch_extis(5, 9);
|
||||
}
|
||||
|
||||
void __irq_exti15_10(void) {
|
||||
dispatch_extis(10, 15);
|
||||
}
|
||||
|
||||
/*
|
||||
* Auxiliary functions
|
||||
*/
|
||||
|
||||
/* Clear the pending bits for EXTIs whose bits are set in exti_msk.
|
||||
*
|
||||
* If a pending bit is cleared as the last instruction in an ISR, it
|
||||
* won't actually be cleared in time and the ISR will fire again. To
|
||||
* compensate, this function NOPs for 2 cycles after clearing the
|
||||
* pending bits to ensure it takes effect. */
|
||||
static __always_inline void clear_pending_msk(uint32 exti_msk) {
|
||||
EXTI_BASE->PR = exti_msk;
|
||||
asm volatile("nop");
|
||||
asm volatile("nop");
|
||||
}
|
||||
|
||||
/* This dispatch routine is for non-multiplexed EXTI lines only; i.e.,
|
||||
* it doesn't check EXTI_PR. */
|
||||
static __always_inline void dispatch_single_exti(uint32 exti) {
|
||||
voidArgumentFuncPtr handler = exti_channels[exti].handler;
|
||||
|
||||
if (!handler) {
|
||||
return;
|
||||
}
|
||||
|
||||
handler(exti_channels[exti].arg);
|
||||
clear_pending_msk(1U << exti);
|
||||
}
|
||||
|
||||
/* Dispatch routine for EXTIs which share an IRQ. */
|
||||
static __always_inline void dispatch_extis(uint32 start, uint32 stop) {
|
||||
uint32 pr = EXTI_BASE->PR;
|
||||
uint32 handled_msk = 0;
|
||||
uint32 exti;
|
||||
|
||||
/* Dispatch user handlers for pending EXTIs. */
|
||||
for (exti = start; exti <= stop; exti++) {
|
||||
uint32 eb = (1U << exti);
|
||||
if (pr & eb) {
|
||||
voidArgumentFuncPtr handler = exti_channels[exti].handler;
|
||||
if (handler) {
|
||||
handler(exti_channels[exti].arg);
|
||||
handled_msk |= eb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Clear the pending bits for handled EXTIs. */
|
||||
clear_pending_msk(handled_msk);
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2012 LeafLabs, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
#include <libmaple/gpio.h>
|
||||
#include "exti_private.h"
|
||||
|
||||
void exti_select(exti_num num, exti_cfg port) {
|
||||
exti_do_select(&AFIO_BASE->EXTICR1 + num / 4, num, port);
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Perry Hung.
|
||||
* Copyright (c) 2011, 2012 LeafLabs, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/flash.c
|
||||
* @brief Flash management functions
|
||||
*/
|
||||
|
||||
#include <libmaple/libmaple_types.h>
|
||||
#include <libmaple/flash.h>
|
||||
|
||||
/**
|
||||
* @brief Set flash wait states
|
||||
*
|
||||
* Note that not all wait states are available on every MCU. See the
|
||||
* Flash programming manual for your MCU for restrictions on the
|
||||
* allowed value of wait_states for a given system clock (SYSCLK)
|
||||
* frequency.
|
||||
*
|
||||
* @param wait_states number of wait states (one of
|
||||
* FLASH_WAIT_STATE_0, FLASH_WAIT_STATE_1,
|
||||
* ..., FLASH_WAIT_STATE_7).
|
||||
*/
|
||||
void flash_set_latency(uint32 wait_states) {
|
||||
uint32 val = FLASH_BASE->ACR;
|
||||
|
||||
val &= ~FLASH_ACR_LATENCY;
|
||||
val |= wait_states;
|
||||
|
||||
FLASH_BASE->ACR = val;
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2012 LeafLabs, LLC.
|
||||
* Copyright (c) 2010 Bryan Newbold.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/stm32f1/fsmc.c
|
||||
* @author Marti Bolivar <mbolivar@leaflabs.com>,
|
||||
* Bryan Newbold <bnewbold@robocracy.org>
|
||||
* @brief STM32F1 FSMC support.
|
||||
*/
|
||||
|
||||
#include <libmaple/stm32.h>
|
||||
|
||||
#if STM32_HAVE_FSMC /* Don't try building the rest for MCUs without FSMC */
|
||||
|
||||
#include <libmaple/fsmc.h>
|
||||
#include <libmaple/gpio.h>
|
||||
|
||||
void fsmc_sram_init_gpios(void) {
|
||||
/* Data lines... */
|
||||
gpio_set_mode(GPIOD, 0, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOD, 1, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOD, 8, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOD, 9, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOD, 10, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOD, 14, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOD, 15, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOE, 7, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOE, 8, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOE, 9, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOE, 10, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOE, 11, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOE, 12, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOE, 13, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOE, 14, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOE, 15, GPIO_AF_OUTPUT_PP);
|
||||
|
||||
/* Address lines... */
|
||||
gpio_set_mode(GPIOD, 11, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOD, 12, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOD, 13, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOF, 0, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOF, 1, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOF, 2, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOF, 3, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOF, 4, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOF, 5, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOF, 12, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOF, 13, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOF, 14, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOF, 15, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOG, 0, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOG, 1, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOG, 2, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOG, 3, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOG, 4, GPIO_AF_OUTPUT_PP);
|
||||
gpio_set_mode(GPIOG, 5, GPIO_AF_OUTPUT_PP);
|
||||
|
||||
/* And control lines... */
|
||||
gpio_set_mode(GPIOD, 4, GPIO_AF_OUTPUT_PP); // NOE
|
||||
gpio_set_mode(GPIOD, 5, GPIO_AF_OUTPUT_PP); // NWE
|
||||
|
||||
gpio_set_mode(GPIOD, 7, GPIO_AF_OUTPUT_PP); // NE1
|
||||
gpio_set_mode(GPIOG, 9, GPIO_AF_OUTPUT_PP); // NE2
|
||||
gpio_set_mode(GPIOG, 10, GPIO_AF_OUTPUT_PP); // NE3
|
||||
gpio_set_mode(GPIOG, 12, GPIO_AF_OUTPUT_PP); // NE4
|
||||
|
||||
gpio_set_mode(GPIOE, 0, GPIO_AF_OUTPUT_PP); // NBL0
|
||||
gpio_set_mode(GPIOE, 1, GPIO_AF_OUTPUT_PP); // NBL1
|
||||
}
|
||||
|
||||
#endif /* STM32_HAVE_FSMC */
|
|
@ -0,0 +1,50 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Perry Hung.
|
||||
* Copyright (c) 2011, 2012 LeafLabs, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/gpio.c
|
||||
* @brief Generic STM32 GPIO support.
|
||||
*/
|
||||
|
||||
#include <libmaple/gpio.h>
|
||||
#include <libmaple/rcc.h>
|
||||
|
||||
/*
|
||||
* GPIO routines
|
||||
*/
|
||||
|
||||
/**
|
||||
* Initialize a GPIO device.
|
||||
*
|
||||
* Enables the clock for and resets the given device.
|
||||
*
|
||||
* @param dev GPIO device to initialize.
|
||||
*/
|
||||
void gpio_init(gpio_dev *dev) {
|
||||
rcc_clk_enable(dev->clk_id);
|
||||
rcc_reset_dev(dev->clk_id);
|
||||
}
|
|
@ -0,0 +1,166 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Perry Hung.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/stm32f1/gpio.c
|
||||
* @brief STM32F1 GPIO support.
|
||||
*/
|
||||
|
||||
#include <libmaple/gpio.h>
|
||||
#include <libmaple/rcc.h>
|
||||
|
||||
/*
|
||||
* GPIO devices
|
||||
*/
|
||||
|
||||
gpio_dev gpioa = {
|
||||
.regs = GPIOA_BASE,
|
||||
.clk_id = RCC_GPIOA,
|
||||
.exti_port = EXTI_PA,
|
||||
};
|
||||
/** GPIO port A device. */
|
||||
gpio_dev* const GPIOA = &gpioa;
|
||||
|
||||
gpio_dev gpiob = {
|
||||
.regs = GPIOB_BASE,
|
||||
.clk_id = RCC_GPIOB,
|
||||
.exti_port = EXTI_PB,
|
||||
};
|
||||
/** GPIO port B device. */
|
||||
gpio_dev* const GPIOB = &gpiob;
|
||||
|
||||
gpio_dev gpioc = {
|
||||
.regs = GPIOC_BASE,
|
||||
.clk_id = RCC_GPIOC,
|
||||
.exti_port = EXTI_PC,
|
||||
};
|
||||
/** GPIO port C device. */
|
||||
gpio_dev* const GPIOC = &gpioc;
|
||||
|
||||
gpio_dev gpiod = {
|
||||
.regs = GPIOD_BASE,
|
||||
.clk_id = RCC_GPIOD,
|
||||
.exti_port = EXTI_PD,
|
||||
};
|
||||
/** GPIO port D device. */
|
||||
gpio_dev* const GPIOD = &gpiod;
|
||||
|
||||
#ifdef STM32_HIGH_DENSITY
|
||||
gpio_dev gpioe = {
|
||||
.regs = GPIOE_BASE,
|
||||
.clk_id = RCC_GPIOE,
|
||||
.exti_port = EXTI_PE,
|
||||
};
|
||||
/** GPIO port E device. */
|
||||
gpio_dev* const GPIOE = &gpioe;
|
||||
|
||||
gpio_dev gpiof = {
|
||||
.regs = GPIOF_BASE,
|
||||
.clk_id = RCC_GPIOF,
|
||||
.exti_port = EXTI_PF,
|
||||
};
|
||||
/** GPIO port F device. */
|
||||
gpio_dev* const GPIOF = &gpiof;
|
||||
|
||||
gpio_dev gpiog = {
|
||||
.regs = GPIOG_BASE,
|
||||
.clk_id = RCC_GPIOG,
|
||||
.exti_port = EXTI_PG,
|
||||
};
|
||||
/** GPIO port G device. */
|
||||
gpio_dev* const GPIOG = &gpiog;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* GPIO routines
|
||||
*/
|
||||
|
||||
/**
|
||||
* Initialize and reset all available GPIO devices.
|
||||
*/
|
||||
void gpio_init_all(void) {
|
||||
gpio_init(GPIOA);
|
||||
gpio_init(GPIOB);
|
||||
gpio_init(GPIOC);
|
||||
gpio_init(GPIOD);
|
||||
#ifdef STM32_HIGH_DENSITY
|
||||
gpio_init(GPIOE);
|
||||
gpio_init(GPIOF);
|
||||
gpio_init(GPIOG);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the mode of a GPIO pin.
|
||||
*
|
||||
* @param dev GPIO device.
|
||||
* @param pin Pin on the device whose mode to set, 0--15.
|
||||
* @param mode General purpose or alternate function mode to set the pin to.
|
||||
* @see gpio_pin_mode
|
||||
*/
|
||||
void gpio_set_mode(gpio_dev *dev, uint8 pin, gpio_pin_mode mode) {
|
||||
gpio_reg_map *regs = dev->regs;
|
||||
__io uint32 *cr = ®s->CRL + (pin >> 3);
|
||||
uint32 shift = (pin & 0x7) * 4;
|
||||
uint32 tmp = *cr;
|
||||
|
||||
tmp &= ~(0xF << shift);
|
||||
tmp |= (mode == GPIO_INPUT_PU ? GPIO_INPUT_PD : mode) << shift;
|
||||
*cr = tmp;
|
||||
|
||||
if (mode == GPIO_INPUT_PD) {
|
||||
regs->ODR &= ~(1U << pin);
|
||||
} else if (mode == GPIO_INPUT_PU) {
|
||||
regs->ODR |= (1U << pin);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* AFIO
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Initialize the AFIO clock, and reset the AFIO registers.
|
||||
*/
|
||||
void afio_init(void) {
|
||||
rcc_clk_enable(RCC_AFIO);
|
||||
rcc_reset_dev(RCC_AFIO);
|
||||
}
|
||||
|
||||
#define AFIO_EXTI_SEL_MASK 0xF
|
||||
|
||||
/**
|
||||
* @brief Perform an alternate function remap.
|
||||
* @param remapping Remapping to perform.
|
||||
*/
|
||||
void afio_remap(afio_remap_peripheral remapping) {
|
||||
if (remapping & AFIO_REMAP_USE_MAPR2) {
|
||||
remapping &= ~AFIO_REMAP_USE_MAPR2;
|
||||
AFIO_BASE->MAPR2 |= remapping;
|
||||
} else {
|
||||
AFIO_BASE->MAPR |= remapping;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,509 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Perry Hung.
|
||||
* Copyright (c) 2012 LeafLabs, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/i2c.c
|
||||
* @author Perry Hung <perry@leaflabs.com>
|
||||
* @brief Inter-Integrated Circuit (I2C) support.
|
||||
*
|
||||
* Currently, only master mode is supported.
|
||||
*/
|
||||
|
||||
#include "i2c_private.h"
|
||||
|
||||
#include <libmaple/libmaple.h>
|
||||
#include <libmaple/rcc.h>
|
||||
#include <libmaple/gpio.h>
|
||||
#include <libmaple/nvic.h>
|
||||
#include <libmaple/i2c.h>
|
||||
#include <libmaple/systick.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static inline int32 wait_for_state_change(i2c_dev *dev,
|
||||
i2c_state state,
|
||||
uint32 timeout);
|
||||
static void set_ccr_trise(i2c_dev *dev, uint32 flags);
|
||||
|
||||
/**
|
||||
* @brief Fill data register with slave address
|
||||
* @param dev I2C device
|
||||
* @param addr Slave address
|
||||
* @param rw Read/write bit
|
||||
*/
|
||||
static inline void i2c_send_slave_addr(i2c_dev *dev, uint32 addr, uint32 rw) {
|
||||
dev->regs->DR = (addr << 1) | rw;
|
||||
}
|
||||
|
||||
/*
|
||||
* Simple debugging trail. Define I2C_DEBUG to turn on.
|
||||
*/
|
||||
#ifdef I2C_DEBUG
|
||||
|
||||
#define NR_CRUMBS 128
|
||||
static struct crumb crumbs[NR_CRUMBS];
|
||||
static uint32 cur_crumb = 0;
|
||||
|
||||
static inline void i2c_drop_crumb(uint32 event, uint32 arg0, uint32 arg1) {
|
||||
if (cur_crumb < NR_CRUMBS) {
|
||||
struct crumb *crumb = &crumbs[cur_crumb++];
|
||||
crumb->event = event;
|
||||
crumb->arg0 = arg0;
|
||||
crumb->arg1 = arg1;
|
||||
}
|
||||
}
|
||||
#define I2C_CRUMB(event, arg0, arg1) i2c_drop_crumb(event, arg0, arg1)
|
||||
|
||||
#else
|
||||
#define I2C_CRUMB(event, arg0, arg1)
|
||||
#endif
|
||||
|
||||
struct crumb {
|
||||
uint32 event;
|
||||
uint32 arg0;
|
||||
uint32 arg1;
|
||||
};
|
||||
|
||||
enum {
|
||||
IRQ_ENTRY = 1,
|
||||
TXE_ONLY = 2,
|
||||
TXE_BTF = 3,
|
||||
STOP_SENT = 4,
|
||||
TEST = 5,
|
||||
RX_ADDR_START = 6,
|
||||
RX_ADDR_STOP = 7,
|
||||
RXNE_ONLY = 8,
|
||||
RXNE_SENDING = 9,
|
||||
RXNE_START_SENT = 10,
|
||||
RXNE_STOP_SENT = 11,
|
||||
RXNE_DONE = 12,
|
||||
ERROR_ENTRY = 13,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Reset an I2C bus.
|
||||
*
|
||||
* Reset is accomplished by clocking out pulses until any hung slaves
|
||||
* release SDA and SCL, then generating a START condition, then a STOP
|
||||
* condition.
|
||||
*
|
||||
* @param dev I2C device
|
||||
*/
|
||||
void i2c_bus_reset(const i2c_dev *dev) {
|
||||
/* Release both lines */
|
||||
i2c_master_release_bus(dev);
|
||||
|
||||
/*
|
||||
* Make sure the bus is free by clocking it until any slaves release the
|
||||
* bus.
|
||||
*/
|
||||
while (!gpio_read_bit(sda_port(dev), dev->sda_pin)) {
|
||||
/* Wait for any clock stretching to finish */
|
||||
while (!gpio_read_bit(scl_port(dev), dev->scl_pin))
|
||||
;
|
||||
delay_us(10);
|
||||
|
||||
/* Pull low */
|
||||
gpio_write_bit(scl_port(dev), dev->scl_pin, 0);
|
||||
delay_us(10);
|
||||
|
||||
/* Release high again */
|
||||
gpio_write_bit(scl_port(dev), dev->scl_pin, 1);
|
||||
delay_us(10);
|
||||
}
|
||||
|
||||
/* Generate start then stop condition */
|
||||
gpio_write_bit(sda_port(dev), dev->sda_pin, 0);
|
||||
delay_us(10);
|
||||
gpio_write_bit(scl_port(dev), dev->scl_pin, 0);
|
||||
delay_us(10);
|
||||
gpio_write_bit(scl_port(dev), dev->scl_pin, 1);
|
||||
delay_us(10);
|
||||
gpio_write_bit(sda_port(dev), dev->sda_pin, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize an I2C device and reset its registers to their
|
||||
* default values.
|
||||
* @param dev Device to initialize.
|
||||
*/
|
||||
void i2c_init(i2c_dev *dev) {
|
||||
rcc_reset_dev(dev->clk_id);
|
||||
rcc_clk_enable(dev->clk_id);
|
||||
}
|
||||
|
||||
/* Hack for deprecated bit of STM32F1 functionality */
|
||||
#ifndef _I2C_HAVE_DEPRECATED_I2C_REMAP
|
||||
#define _i2c_handle_remap(dev, flags) ((void)0)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Initialize an I2C device as bus master
|
||||
* @param dev Device to enable
|
||||
* @param flags Bitwise or of the following I2C options:
|
||||
* I2C_FAST_MODE: 400 khz operation,
|
||||
* I2C_DUTY_16_9: 16/9 Tlow/Thigh duty cycle (only applicable for
|
||||
* fast mode),
|
||||
* I2C_BUS_RESET: Reset the bus and clock out any hung slaves on
|
||||
* initialization,
|
||||
* I2C_10BIT_ADDRESSING: Enable 10-bit addressing,
|
||||
* I2C_REMAP: (deprecated, STM32F1 only) Remap I2C1 to SCL/PB8
|
||||
* SDA/PB9.
|
||||
*/
|
||||
void i2c_master_enable(i2c_dev *dev, uint32 flags) {
|
||||
/* PE must be disabled to configure the device */
|
||||
ASSERT(!(dev->regs->CR1 & I2C_CR1_PE));
|
||||
|
||||
/* Ugh */
|
||||
_i2c_handle_remap(dev, flags);
|
||||
|
||||
/* Reset the bus. Clock out any hung slaves. */
|
||||
if (flags & I2C_BUS_RESET) {
|
||||
i2c_bus_reset(dev);
|
||||
}
|
||||
|
||||
/* Turn on clock and set GPIO modes */
|
||||
i2c_init(dev);
|
||||
i2c_config_gpios(dev);
|
||||
|
||||
/* Configure clock and rise time */
|
||||
set_ccr_trise(dev, flags);
|
||||
|
||||
/* Enable event and buffer interrupts */
|
||||
nvic_irq_enable(dev->ev_nvic_line);
|
||||
nvic_irq_enable(dev->er_nvic_line);
|
||||
i2c_enable_irq(dev, I2C_IRQ_EVENT | I2C_IRQ_BUFFER | I2C_IRQ_ERROR);
|
||||
|
||||
/* Make it go! */
|
||||
i2c_peripheral_enable(dev);
|
||||
|
||||
dev->state = I2C_STATE_IDLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Process an i2c transaction.
|
||||
*
|
||||
* Transactions are composed of one or more i2c_msg's, and may be read
|
||||
* or write tranfers. Multiple i2c_msg's will generate a repeated
|
||||
* start in between messages.
|
||||
*
|
||||
* @param dev I2C device
|
||||
* @param msgs Messages to send/receive
|
||||
* @param num Number of messages to send/receive
|
||||
* @param timeout Bus idle timeout in milliseconds before aborting the
|
||||
* transfer. 0 denotes no timeout.
|
||||
* @return 0 on success,
|
||||
* I2C_ERROR_PROTOCOL if there was a protocol error,
|
||||
* I2C_ERROR_TIMEOUT if the transfer timed out.
|
||||
*/
|
||||
int32 i2c_master_xfer(i2c_dev *dev,
|
||||
i2c_msg *msgs,
|
||||
uint16 num,
|
||||
uint32 timeout) {
|
||||
int32 rc;
|
||||
|
||||
ASSERT(dev->state == I2C_STATE_IDLE);
|
||||
|
||||
dev->msg = msgs;
|
||||
dev->msgs_left = num;
|
||||
dev->timestamp = systick_uptime();
|
||||
dev->state = I2C_STATE_BUSY;
|
||||
|
||||
i2c_enable_irq(dev, I2C_IRQ_EVENT);
|
||||
i2c_start_condition(dev);
|
||||
|
||||
rc = wait_for_state_change(dev, I2C_STATE_XFER_DONE, timeout);
|
||||
if (rc < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
dev->state = I2C_STATE_IDLE;
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Wait for an I2C event, or time out in case of error.
|
||||
* @param dev I2C device
|
||||
* @param state I2C_state state to wait for
|
||||
* @param timeout Timeout, in milliseconds
|
||||
* @return 0 if target state is reached, a negative value on error.
|
||||
*/
|
||||
static inline int32 wait_for_state_change(i2c_dev *dev,
|
||||
i2c_state state,
|
||||
uint32 timeout) {
|
||||
i2c_state tmp;
|
||||
|
||||
while (1) {
|
||||
tmp = dev->state;
|
||||
|
||||
if (tmp == I2C_STATE_ERROR) {
|
||||
return I2C_STATE_ERROR;
|
||||
}
|
||||
|
||||
if (tmp == state) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (timeout) {
|
||||
if (systick_uptime() > (dev->timestamp + timeout)) {
|
||||
/* TODO: overflow? */
|
||||
/* TODO: racy? */
|
||||
return I2C_ERROR_TIMEOUT;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Private API
|
||||
*/
|
||||
|
||||
/*
|
||||
* IRQ handler for I2C master. Handles transmission/reception.
|
||||
*/
|
||||
void _i2c_irq_handler(i2c_dev *dev) {
|
||||
/* WTFs:
|
||||
* - Where is I2C_MSG_10BIT_ADDR handled?
|
||||
*/
|
||||
i2c_msg *msg = dev->msg;
|
||||
|
||||
uint8 read = msg->flags & I2C_MSG_READ;
|
||||
|
||||
uint32 sr1 = dev->regs->SR1;
|
||||
uint32 sr2 = dev->regs->SR2;
|
||||
I2C_CRUMB(IRQ_ENTRY, sr1, sr2);
|
||||
|
||||
/*
|
||||
* Reset timeout counter
|
||||
*/
|
||||
dev->timestamp = systick_uptime();
|
||||
|
||||
/*
|
||||
* EV5: Start condition sent
|
||||
*/
|
||||
if (sr1 & I2C_SR1_SB) {
|
||||
msg->xferred = 0;
|
||||
i2c_enable_irq(dev, I2C_IRQ_BUFFER);
|
||||
|
||||
/*
|
||||
* Master receiver
|
||||
*/
|
||||
if (read) {
|
||||
i2c_enable_ack(dev);
|
||||
}
|
||||
|
||||
i2c_send_slave_addr(dev, msg->addr, read);
|
||||
sr1 = sr2 = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* EV6: Slave address sent
|
||||
*/
|
||||
if (sr1 & I2C_SR1_ADDR) {
|
||||
/*
|
||||
* Special case event EV6_1 for master receiver.
|
||||
* Generate NACK and restart/stop condition after ADDR
|
||||
* is cleared.
|
||||
*/
|
||||
if (read) {
|
||||
if (msg->length == 1) {
|
||||
i2c_disable_ack(dev);
|
||||
if (dev->msgs_left > 1) {
|
||||
i2c_start_condition(dev);
|
||||
I2C_CRUMB(RX_ADDR_START, 0, 0);
|
||||
} else {
|
||||
i2c_stop_condition(dev);
|
||||
I2C_CRUMB(RX_ADDR_STOP, 0, 0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Master transmitter: write first byte to fill shift
|
||||
* register. We should get another TXE interrupt
|
||||
* immediately to fill DR again.
|
||||
*/
|
||||
if (msg->length != 1) {
|
||||
i2c_write(dev, msg->data[msg->xferred++]);
|
||||
}
|
||||
}
|
||||
sr1 = sr2 = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* EV8: Master transmitter
|
||||
* Transmit buffer empty, but we haven't finished transmitting the last
|
||||
* byte written.
|
||||
*/
|
||||
if ((sr1 & I2C_SR1_TXE) && !(sr1 & I2C_SR1_BTF)) {
|
||||
I2C_CRUMB(TXE_ONLY, 0, 0);
|
||||
if (dev->msgs_left) {
|
||||
i2c_write(dev, msg->data[msg->xferred++]);
|
||||
if (msg->xferred == msg->length) {
|
||||
/*
|
||||
* End of this message. Turn off TXE/RXNE and wait for
|
||||
* BTF to send repeated start or stop condition.
|
||||
*/
|
||||
i2c_disable_irq(dev, I2C_IRQ_BUFFER);
|
||||
dev->msgs_left--;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* This should be impossible...
|
||||
*/
|
||||
ASSERT(0);
|
||||
}
|
||||
sr1 = sr2 = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* EV8_2: Master transmitter
|
||||
* Last byte sent, program repeated start/stop
|
||||
*/
|
||||
if ((sr1 & I2C_SR1_TXE) && (sr1 & I2C_SR1_BTF)) {
|
||||
I2C_CRUMB(TXE_BTF, 0, 0);
|
||||
if (dev->msgs_left) {
|
||||
I2C_CRUMB(TEST, 0, 0);
|
||||
/*
|
||||
* Repeated start insanity: We can't disable ITEVTEN or else SB
|
||||
* won't interrupt, but if we don't disable ITEVTEN, BTF will
|
||||
* continually interrupt us. What the fuck ST?
|
||||
*/
|
||||
i2c_start_condition(dev);
|
||||
while (!(dev->regs->SR1 & I2C_SR1_SB))
|
||||
;
|
||||
dev->msg++;
|
||||
} else {
|
||||
i2c_stop_condition(dev);
|
||||
|
||||
/*
|
||||
* Turn off event interrupts to keep BTF from firing until
|
||||
* the end of the stop condition. Why on earth they didn't
|
||||
* have a start/stop condition request clear BTF is beyond
|
||||
* me.
|
||||
*/
|
||||
i2c_disable_irq(dev, I2C_IRQ_EVENT);
|
||||
I2C_CRUMB(STOP_SENT, 0, 0);
|
||||
dev->state = I2C_STATE_XFER_DONE;
|
||||
}
|
||||
sr1 = sr2 = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* EV7: Master Receiver
|
||||
*/
|
||||
if (sr1 & I2C_SR1_RXNE) {
|
||||
I2C_CRUMB(RXNE_ONLY, 0, 0);
|
||||
msg->data[msg->xferred++] = dev->regs->DR;
|
||||
|
||||
/*
|
||||
* EV7_1: Second to last byte in the reception? Set NACK and generate
|
||||
* stop/restart condition in time for the last byte. We'll get one more
|
||||
* RXNE interrupt before shutting things down.
|
||||
*/
|
||||
if (msg->xferred == (msg->length - 1)) {
|
||||
i2c_disable_ack(dev);
|
||||
if (dev->msgs_left > 2) {
|
||||
i2c_start_condition(dev);
|
||||
I2C_CRUMB(RXNE_START_SENT, 0, 0);
|
||||
} else {
|
||||
i2c_stop_condition(dev);
|
||||
I2C_CRUMB(RXNE_STOP_SENT, 0, 0);
|
||||
}
|
||||
} else if (msg->xferred == msg->length) {
|
||||
dev->msgs_left--;
|
||||
if (dev->msgs_left == 0) {
|
||||
/*
|
||||
* We're done.
|
||||
*/
|
||||
I2C_CRUMB(RXNE_DONE, 0, 0);
|
||||
dev->state = I2C_STATE_XFER_DONE;
|
||||
} else {
|
||||
dev->msg++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Interrupt handler for I2C error conditions. Aborts any pending I2C
|
||||
* transactions.
|
||||
*/
|
||||
void _i2c_irq_error_handler(i2c_dev *dev) {
|
||||
I2C_CRUMB(ERROR_ENTRY, dev->regs->SR1, dev->regs->SR2);
|
||||
|
||||
dev->error_flags = dev->regs->SR2 & (I2C_SR1_BERR |
|
||||
I2C_SR1_ARLO |
|
||||
I2C_SR1_AF |
|
||||
I2C_SR1_OVR);
|
||||
/* Clear flags */
|
||||
dev->regs->SR1 = 0;
|
||||
dev->regs->SR2 = 0;
|
||||
|
||||
i2c_stop_condition(dev);
|
||||
i2c_disable_irq(dev, I2C_IRQ_BUFFER | I2C_IRQ_EVENT | I2C_IRQ_ERROR);
|
||||
dev->state = I2C_STATE_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* CCR/TRISE configuration helper
|
||||
*/
|
||||
static void set_ccr_trise(i2c_dev *dev, uint32 flags) {
|
||||
uint32 ccr = 0;
|
||||
uint32 trise = 0;
|
||||
uint32 clk_mhz = _i2c_bus_clk(dev);
|
||||
uint32 clk_hz = clk_mhz * (1000 * 1000);
|
||||
|
||||
i2c_set_input_clk(dev, clk_mhz);
|
||||
|
||||
if (flags & I2C_FAST_MODE) {
|
||||
ccr |= I2C_CCR_FS;
|
||||
|
||||
if (flags & I2C_DUTY_16_9) {
|
||||
/* Tlow/Thigh = 16/9 */
|
||||
ccr |= I2C_CCR_DUTY_16_9;
|
||||
ccr |= clk_hz / (400000 * 25);
|
||||
} else {
|
||||
/* Tlow/Thigh = 2 */
|
||||
ccr |= clk_hz / (400000 * 3);
|
||||
}
|
||||
|
||||
trise = (300 * clk_mhz / 1000) + 1;
|
||||
} else {
|
||||
/* Tlow/Thigh = 1 */
|
||||
ccr = clk_hz / (100000 * 2);
|
||||
trise = clk_mhz + 1;
|
||||
}
|
||||
|
||||
/* Set minimum required value if CCR < 1*/
|
||||
if ((ccr & I2C_CCR_CCR) == 0) {
|
||||
ccr |= 0x1;
|
||||
}
|
||||
|
||||
i2c_set_clk_control(dev, ccr);
|
||||
i2c_set_trise(dev, trise);
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2012 LeafLabs, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/stm32f1/i2c.c
|
||||
* @brief STM32F1 I2C support
|
||||
*/
|
||||
|
||||
#include "i2c_private.h"
|
||||
#include <libmaple/i2c.h>
|
||||
|
||||
/*
|
||||
* Devices
|
||||
*/
|
||||
|
||||
static i2c_dev i2c1 = I2C_DEV_OLD(1, &gpiob, 7, 6);
|
||||
static i2c_dev i2c2 = I2C_DEV_OLD(2, &gpiob, 11, 10);
|
||||
|
||||
/** STM32F1 I2C device 1 */
|
||||
i2c_dev* const I2C1 = &i2c1;
|
||||
/** STM32F1 I2C device 2 */
|
||||
i2c_dev* const I2C2 = &i2c2;
|
||||
|
||||
/*
|
||||
* Routines
|
||||
*/
|
||||
|
||||
static int i2c1_wants_remap(const i2c_dev *dev) {
|
||||
/* Check if we've got I2C1 configured for SDA/SCL remap on PB9/PB8 */
|
||||
return (dev->clk_id == RCC_I2C1) &&
|
||||
(scl_port(dev)->clk_id == RCC_GPIOB) &&
|
||||
(sda_port(dev)->clk_id == RCC_GPIOB) &&
|
||||
(dev->sda_pin == 9) &&
|
||||
(dev->scl_pin == 8);
|
||||
}
|
||||
|
||||
void i2c_config_gpios(const i2c_dev *dev) {
|
||||
if (i2c1_wants_remap(dev)) {
|
||||
afio_remap(AFIO_REMAP_I2C1);
|
||||
}
|
||||
gpio_set_mode(sda_port(dev), dev->sda_pin, GPIO_AF_OUTPUT_OD);
|
||||
gpio_set_mode(scl_port(dev), dev->scl_pin, GPIO_AF_OUTPUT_OD);
|
||||
}
|
||||
|
||||
void i2c_master_release_bus(const i2c_dev *dev) {
|
||||
gpio_write_bit(scl_port(dev), dev->scl_pin, 1);
|
||||
gpio_write_bit(sda_port(dev), dev->sda_pin, 1);
|
||||
gpio_set_mode(scl_port(dev), dev->scl_pin, GPIO_OUTPUT_OD);
|
||||
gpio_set_mode(sda_port(dev), dev->sda_pin, GPIO_OUTPUT_OD);
|
||||
}
|
||||
|
||||
/*
|
||||
* IRQ handlers
|
||||
*/
|
||||
|
||||
void __irq_i2c1_ev(void) {
|
||||
_i2c_irq_handler(I2C1);
|
||||
}
|
||||
|
||||
void __irq_i2c2_ev(void) {
|
||||
_i2c_irq_handler(I2C2);
|
||||
}
|
||||
|
||||
void __irq_i2c1_er(void) {
|
||||
_i2c_irq_error_handler(I2C1);
|
||||
}
|
||||
|
||||
void __irq_i2c2_er(void) {
|
||||
_i2c_irq_error_handler(I2C2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Internal APIs
|
||||
*/
|
||||
|
||||
void _i2c_irq_priority_fixup(i2c_dev *dev) {
|
||||
/*
|
||||
* Important STM32 Errata:
|
||||
*
|
||||
* See STM32F10xx8 and STM32F10xxB Errata sheet (Doc ID 14574 Rev 8),
|
||||
* Section 2.11.1, 2.11.2.
|
||||
*
|
||||
* 2.11.1:
|
||||
* When the EV7, EV7_1, EV6_1, EV6_3, EV2, EV8, and EV3 events are not
|
||||
* managed before the current byte is being transferred, problems may be
|
||||
* encountered such as receiving an extra byte, reading the same data twice
|
||||
* or missing data.
|
||||
*
|
||||
* 2.11.2:
|
||||
* In Master Receiver mode, when closing the communication using
|
||||
* method 2, the content of the last read data can be corrupted.
|
||||
*
|
||||
* If the user software is not able to read the data N-1 before the STOP
|
||||
* condition is generated on the bus, the content of the shift register
|
||||
* (data N) will be corrupted. (data N is shifted 1-bit to the left).
|
||||
*
|
||||
* ----------------------------------------------------------------------
|
||||
*
|
||||
* In order to ensure that events are not missed, the i2c interrupt must
|
||||
* not be preempted. We set the i2c interrupt priority to be the highest
|
||||
* interrupt in the system (priority level 0). All other interrupts have
|
||||
* been initialized to priority level 16. See nvic_init().
|
||||
*/
|
||||
nvic_irq_set_priority(dev->ev_nvic_line, 0);
|
||||
nvic_irq_set_priority(dev->er_nvic_line, 0);
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Michael Hope.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/iwdg.c
|
||||
* @brief Independent watchdog (IWDG) support
|
||||
*/
|
||||
|
||||
#include <libmaple/iwdg.h>
|
||||
|
||||
/**
|
||||
* @brief Initialise and start the watchdog
|
||||
*
|
||||
* The prescaler and reload set the timeout. For example, a prescaler
|
||||
* of IWDG_PRE_32 divides the 40 kHz clock by 32 and gives roughly 1
|
||||
* ms per reload.
|
||||
*
|
||||
* @param prescaler Prescaler for the 40 kHz IWDG clock.
|
||||
* @param reload Independent watchdog counter reload value.
|
||||
*/
|
||||
void iwdg_init(iwdg_prescaler prescaler, uint16 reload) {
|
||||
IWDG_BASE->KR = IWDG_KR_UNLOCK;
|
||||
IWDG_BASE->PR = prescaler;
|
||||
IWDG_BASE->RLR = reload;
|
||||
|
||||
/* Start things off */
|
||||
IWDG_BASE->KR = IWDG_KR_START;
|
||||
iwdg_feed();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reset the IWDG counter.
|
||||
*
|
||||
* Calling this function will cause the IWDG counter to be reset to
|
||||
* its reload value.
|
||||
*/
|
||||
void iwdg_feed(void) {
|
||||
IWDG_BASE->KR = IWDG_KR_FEED;
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Perry Hung.
|
||||
* Copyright (c) 2011 LeafLabs, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/nvic.c
|
||||
* @brief Nested vector interrupt controller support.
|
||||
*/
|
||||
|
||||
#include <libmaple/nvic.h>
|
||||
#include <libmaple/scb.h>
|
||||
#include <libmaple/stm32.h>
|
||||
|
||||
/**
|
||||
* @brief Set interrupt priority for an interrupt line
|
||||
*
|
||||
* Note: The STM32 only implements 4 bits of priority, ignoring the
|
||||
* lower 4 bits. This means there are only 16 levels of priority.
|
||||
* Bits[3:0] read as zero and ignore writes.
|
||||
*
|
||||
* @param irqn device to set
|
||||
* @param priority Priority to set, 0 being highest priority and 15
|
||||
* being lowest.
|
||||
*/
|
||||
void nvic_irq_set_priority(nvic_irq_num irqn, uint8 priority) {
|
||||
if (irqn < 0) {
|
||||
/* This interrupt is in the system handler block */
|
||||
SCB_BASE->SHP[((uint32)irqn & 0xF) - 4] = (priority & 0xF) << 4;
|
||||
} else {
|
||||
NVIC_BASE->IP[irqn] = (priority & 0xF) << 4;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the NVIC, setting interrupts to a default priority.
|
||||
*/
|
||||
void nvic_init(uint32 address, uint32 offset) {
|
||||
uint32 i;
|
||||
|
||||
nvic_set_vector_table(address, offset);
|
||||
|
||||
/*
|
||||
* Lower priority level for all peripheral interrupts to lowest
|
||||
* possible.
|
||||
*/
|
||||
for (i = 0; i < STM32_NR_INTERRUPTS; i++) {
|
||||
nvic_irq_set_priority((nvic_irq_num)i, 0xF);
|
||||
}
|
||||
|
||||
/* Lower systick interrupt priority to lowest level */
|
||||
nvic_irq_set_priority(NVIC_SYSTICK, 0xF);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the vector table base address.
|
||||
*
|
||||
* For stand-alone products, the vector table base address is normally
|
||||
* the start of Flash (0x08000000).
|
||||
*
|
||||
* @param address Vector table base address.
|
||||
* @param offset Offset from address. Some restrictions apply to the
|
||||
* use of nonzero offsets; see the ARM Cortex M3
|
||||
* Technical Reference Manual.
|
||||
*/
|
||||
void nvic_set_vector_table(uint32 address, uint32 offset) {
|
||||
SCB_BASE->VTOR = address | (offset & 0x1FFFFF80);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Force a system reset.
|
||||
*
|
||||
* Resets all major system components, excluding debug.
|
||||
*/
|
||||
void nvic_sys_reset() {
|
||||
uint32 prigroup = SCB_BASE->AIRCR & SCB_AIRCR_PRIGROUP;
|
||||
SCB_BASE->AIRCR = SCB_AIRCR_VECTKEY | SCB_AIRCR_SYSRESETREQ | prigroup;
|
||||
asm volatile("dsb");
|
||||
while (1)
|
||||
;
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2011 LeafLabs, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/pwr.c
|
||||
* @brief Power control (PWR) support.
|
||||
*/
|
||||
|
||||
#include <libmaple/pwr.h>
|
||||
#include <libmaple/rcc.h>
|
||||
|
||||
/**
|
||||
* Enables the power interface clock, and resets the power device.
|
||||
*/
|
||||
void pwr_init(void) {
|
||||
rcc_clk_enable(RCC_PWR);
|
||||
rcc_reset_dev(RCC_PWR);
|
||||
}
|
|
@ -0,0 +1,169 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Perry Hung.
|
||||
* Copyright (c) 2011 LeafLabs, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/rcc.c
|
||||
* @brief Portable RCC routines.
|
||||
*/
|
||||
|
||||
#include <libmaple/rcc.h>
|
||||
|
||||
#include "rcc_private.h"
|
||||
|
||||
/**
|
||||
* @brief Get a peripheral's clock domain
|
||||
* @param id Clock ID of the peripheral whose clock domain to return
|
||||
* @return Clock source for the given clock ID
|
||||
*/
|
||||
rcc_clk_domain rcc_dev_clk(rcc_clk_id id) {
|
||||
return rcc_dev_table[id].clk_domain;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Switch the clock used as the source of the system clock.
|
||||
*
|
||||
* After switching the source, this function blocks until the new
|
||||
* clock source is in use.
|
||||
*
|
||||
* @param sysclk_src New system clock source.
|
||||
* @see rcc_sysclk_src
|
||||
*/
|
||||
void rcc_switch_sysclk(rcc_sysclk_src sysclk_src) {
|
||||
uint32 cfgr = RCC_BASE->CFGR;
|
||||
cfgr &= ~RCC_CFGR_SW;
|
||||
cfgr |= sysclk_src;
|
||||
|
||||
/* Switch SYSCLK source. */
|
||||
RCC_BASE->CFGR = cfgr;
|
||||
|
||||
/* Wait for new source to come into use. */
|
||||
while ((RCC_BASE->CFGR & RCC_CFGR_SWS) != (sysclk_src << 2))
|
||||
;
|
||||
}
|
||||
|
||||
/*
|
||||
* Turning clocks off and on, querying their status.
|
||||
*/
|
||||
|
||||
/* IMPORTANT NOTE FOR IMPLEMENTORS:
|
||||
*
|
||||
* libmaple assumes that enum rcc_clk enumerators are two-byte
|
||||
* values, stored in a uint16, in the following way:
|
||||
*
|
||||
* - The high-order byte is the byte offset (from RCC_BASE) of the register
|
||||
* to touch when turning on or off the given clock.
|
||||
*
|
||||
* - The low-order byte is the bit in that register that turns the
|
||||
* clock on or off.
|
||||
*
|
||||
* Example for STM32F1: Turning on the high-speed external clock (HSE)
|
||||
* involves setting HSEON, bit 16, of RCC_CR. The high-order byte is
|
||||
* then offsetof(struct rcc_reg_map, CR) = 0, and the low-order byte
|
||||
* is 16.
|
||||
*
|
||||
* The corresponding value of RCC_CLK_HSE is thus (0 << 8) | 16 = 16.
|
||||
*
|
||||
* On all known STM32 series, this encoding has the property that
|
||||
* adding one to the low byte also gives the bit to check to determine
|
||||
* if the clock is ready. For example, on STM32F1, RCC_CR_HSERDY is
|
||||
* bit 17. If that's not the case on your series, rcc_is_clk_ready()
|
||||
* won't work for you. */
|
||||
|
||||
/* Returns the RCC register which controls the clock source. */
|
||||
static inline __io uint32* rcc_clk_reg(rcc_clk clock) {
|
||||
return (__io uint32*)((__io uint8*)RCC_BASE + (clock >> 8));
|
||||
}
|
||||
|
||||
/* Returns a mask in rcc_clk_reg(clock) to be used for turning the
|
||||
* clock on and off */
|
||||
static inline uint32 rcc_clk_on_mask(rcc_clk clock) {
|
||||
return 1 << (clock & 0xFF);
|
||||
}
|
||||
|
||||
/* Returns a mask in rcc_clk_reg(clock) to be used when checking the
|
||||
* readiness of the clock. */
|
||||
static inline uint32 rcc_clk_ready_mask(rcc_clk clock) {
|
||||
return rcc_clk_on_mask(clock) << 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Turn on a clock source.
|
||||
*
|
||||
* After this routine exits, callers should ensure that the clock
|
||||
* source is ready by waiting until rcc_is_clk_ready(clock) returns
|
||||
* true.
|
||||
*
|
||||
* @param clock Clock to turn on.
|
||||
* @see rcc_turn_off_clk()
|
||||
* @see rcc_is_clk_ready()
|
||||
*/
|
||||
void rcc_turn_on_clk(rcc_clk clock) {
|
||||
*rcc_clk_reg(clock) |= rcc_clk_on_mask(clock);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Turn off a clock source.
|
||||
*
|
||||
* In certain configurations, certain clock sources cannot be safely
|
||||
* turned off. (For example, the main PLL on STM32F1 devices cannot be
|
||||
* turned off if it has been selected as the SYSCLK source). Consult
|
||||
* the reference material for your MCU to ensure it is safe to call
|
||||
* this function.
|
||||
*
|
||||
* @param clock Clock to turn off.
|
||||
* @see rcc_turn_on_clk()
|
||||
* @see rcc_is_clk_ready()
|
||||
*/
|
||||
void rcc_turn_off_clk(rcc_clk clock) {
|
||||
*rcc_clk_reg(clock) &= ~rcc_clk_on_mask(clock);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if a clock is on.
|
||||
* @param clock Clock to check.
|
||||
* @return 1 if the clock is on, 0 if the clock is off.
|
||||
*/
|
||||
int rcc_is_clk_on(rcc_clk clock) {
|
||||
return !!(*rcc_clk_reg(clock) & rcc_clk_on_mask(clock));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if a clock source is ready.
|
||||
*
|
||||
* In general, it is not safe to rely on a clock source unless this
|
||||
* function returns nonzero. Also note that this function may return
|
||||
* nonzero for a short period of time after a clock has been turned
|
||||
* off. Consult the reference material for your MCU for more details.
|
||||
*
|
||||
* @param clock Clock whose readiness to check for.
|
||||
* @return Nonzero if the clock is ready, zero otherwise.
|
||||
* @see rcc_turn_on_clk()
|
||||
* @see rcc_turn_off_clk()
|
||||
*/
|
||||
int rcc_is_clk_ready(rcc_clk clock) {
|
||||
return (int)(*rcc_clk_reg(clock) & rcc_clk_ready_mask(clock));
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
/******************************************************************************
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2010 Perry Hung.
|
||||
* Copyright (c) 2011 LeafLabs, LLC.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy,
|
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
* of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*****************************************************************************/
|
||||
|
||||
/**
|
||||
* @file libmaple/stm32f1/rcc.c
|
||||
* @brief STM32F1 RCC.
|
||||
*/
|
||||
|
||||
#include <libmaple/rcc.h>
|
||||
#include <libmaple/libmaple.h>
|
||||
#include <libmaple/bitband.h>
|
||||
|
||||
#include "rcc_private.h"
|
||||
|
||||
#define APB1 RCC_APB1
|
||||
#define APB2 RCC_APB2
|
||||
#define AHB RCC_AHB
|
||||
|
||||
/* Device descriptor table, maps rcc_clk_id onto bus and enable/reset
|
||||
* register bit numbers. */
|
||||
const struct rcc_dev_info rcc_dev_table[] = {
|
||||
[RCC_GPIOA] = { .clk_domain = APB2, .line_num = 2 },
|
||||
[RCC_GPIOB] = { .clk_domain = APB2, .line_num = 3 },
|
||||
[RCC_GPIOC] = { .clk_domain = APB2, .line_num = 4 },
|
||||
[RCC_GPIOD] = { .clk_domain = APB2, .line_num = 5 },
|
||||
[RCC_AFIO] = { .clk_domain = APB2, .line_num = 0 },
|
||||
[RCC_ADC1] = { .clk_domain = APB2, .line_num = 9 },
|
||||
[RCC_ADC2] = { .clk_domain = APB2, .line_num = 10 },
|
||||
[RCC_ADC3] = { .clk_domain = APB2, .line_num = 15 },
|
||||
[RCC_USART1] = { .clk_domain = APB2, .line_num = 14 },
|
||||
[RCC_USART2] = { .clk_domain = APB1, .line_num = 17 },
|
||||
[RCC_USART3] = { .clk_domain = APB1, .line_num = 18 },
|
||||
[RCC_TIMER1] = { .clk_domain = APB2, .line_num = 11 },
|
||||
[RCC_TIMER2] = { .clk_domain = APB1, .line_num = 0 },
|
||||
[RCC_TIMER3] = { .clk_domain = APB1, .line_num = 1 },
|
||||
[RCC_TIMER4] = { .clk_domain = APB1, .line_num = 2 },
|
||||
[RCC_SPI1] = { .clk_domain = APB2, .line_num = 12 },
|
||||
[RCC_SPI2] = { .clk_domain = APB1, .line_num = 14 },
|
||||
[RCC_DMA1] = { .clk_domain = AHB, .line_num = 0 },
|
||||
[RCC_PWR] = { .clk_domain = APB1, .line_num = 28},
|
||||
[RCC_BKP] = { .clk_domain = APB1, .line_num = 27},
|
||||
[RCC_I2C1] = { .clk_domain = APB1, .line_num = 21 },
|
||||
[RCC_I2C2] = { .clk_domain = APB1, .line_num = 22 },
|
||||
[RCC_CRC] = { .clk_domain = AHB, .line_num = 6},
|
||||
[RCC_FLITF] = { .clk_domain = AHB, .line_num = 4},
|
||||
[RCC_SRAM] = { .clk_domain = AHB, .line_num = 2},
|
||||
[RCC_USB] = { .clk_domain = APB1, .line_num = 23},
|
||||
#if defined(STM32_HIGH_DENSITY) || defined(STM32_XL_DENSITY)
|
||||
[RCC_GPIOE] = { .clk_domain = APB2, .line_num = 6 },
|
||||
[RCC_GPIOF] = { .clk_domain = APB2, .line_num = 7 },
|
||||
[RCC_GPIOG] = { .clk_domain = APB2, .line_num = 8 },
|
||||
[RCC_UART4] = { .clk_domain = APB1, .line_num = 19 },
|
||||
[RCC_UART5] = { .clk_domain = APB1, .line_num = 20 },
|
||||
[RCC_TIMER5] = { .clk_domain = APB1, .line_num = 3 },
|
||||
[RCC_TIMER6] = { .clk_domain = APB1, .line_num = 4 },
|
||||
[RCC_TIMER7] = { .clk_domain = APB1, .line_num = 5 },
|
||||
[RCC_TIMER8] = { .clk_domain = APB2, .line_num = 13 },
|
||||
[RCC_FSMC] = { .clk_domain = AHB, .line_num = 8 },
|
||||
[RCC_DAC] = { .clk_domain = APB1, .line_num = 29 },
|
||||
[RCC_DMA2] = { .clk_domain = AHB, .line_num = 1 },
|
||||
[RCC_SDIO] = { .clk_domain = AHB, .line_num = 10 },
|
||||
[RCC_SPI3] = { .clk_domain = APB1, .line_num = 15 },
|
||||
#endif
|
||||
#ifdef STM32_XL_DENSITY
|
||||
[RCC_TIMER9] = { .clk_domain = APB2, .line_num = 19 },
|
||||
[RCC_TIMER10] = { .clk_domain = APB2, .line_num = 20 },
|
||||
[RCC_TIMER11] = { .clk_domain = APB2, .line_num = 21 },
|
||||
[RCC_TIMER12] = { .clk_domain = APB1, .line_num = 6 },
|
||||
[RCC_TIMER13] = { .clk_domain = APB1, .line_num = 7 },
|
||||
[RCC_TIMER14] = { .clk_domain = APB1, .line_num = 8 },
|
||||
#endif
|
||||
};
|
||||
|
||||
__deprecated
|
||||
void rcc_clk_init(rcc_sysclk_src sysclk_src,
|
||||
rcc_pllsrc pll_src,
|
||||
rcc_pll_multiplier pll_mul) {
|
||||
/* Assume that we're going to clock the chip off the PLL, fed by
|
||||
* the HSE */
|
||||
ASSERT(sysclk_src == RCC_CLKSRC_PLL &&
|
||||
pll_src == RCC_PLLSRC_HSE);
|
||||
|
||||
RCC_BASE->CFGR = pll_src | pll_mul;
|
||||
|
||||
/* Turn on, and wait for, HSE. */
|
||||
rcc_turn_on_clk(RCC_CLK_HSE);
|
||||
while (!rcc_is_clk_ready(RCC_CLK_HSE))
|
||||
;
|
||||
|
||||
/* Do the same for the main PLL. */
|
||||
rcc_turn_on_clk(RCC_CLK_PLL);
|
||||
while(!rcc_is_clk_ready(RCC_CLK_PLL))
|
||||
;
|
||||
|
||||
/* Finally, switch over to the PLL. */
|
||||
rcc_switch_sysclk(RCC_CLKSRC_PLL);
|
||||
}
|
||||
|
||||
/* pll_cfg->data must point to a valid struct stm32f1_rcc_pll_data. */
|
||||
void rcc_configure_pll(rcc_pll_cfg *pll_cfg) {
|
||||
stm32f1_rcc_pll_data *data = pll_cfg->data;
|
||||
rcc_pll_multiplier pll_mul = data->pll_mul;
|
||||
uint32 cfgr;
|
||||
|
||||
/* Check that the PLL is disabled. */
|
||||
ASSERT_FAULT(!rcc_is_clk_on(RCC_CLK_PLL));
|
||||
|
||||
cfgr = RCC_BASE->CFGR;
|
||||
cfgr &= ~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLMUL);
|
||||
cfgr |= pll_cfg->pllsrc | pll_mul;
|
||||
RCC_BASE->CFGR = cfgr;
|
||||
}
|
||||
|
||||
void rcc_clk_enable(rcc_clk_id id) {
|
||||
static __io uint32* enable_regs[] = {
|
||||
[APB1] = &RCC_BASE->APB1ENR,
|
||||
[APB2] = &RCC_BASE->APB2ENR,
|
||||
[AHB] = &RCC_BASE->AHBENR,
|
||||
};
|
||||
rcc_do_clk_enable(enable_regs, id);
|
||||
}
|
||||
|
||||
void rcc_reset_dev(rcc_clk_id id) {
|
||||
static __io uint32* reset_regs[] = {
|
||||
[APB1] = &RCC_BASE->APB1RSTR,
|
||||
[APB2] = &RCC_BASE->APB2RSTR,
|
||||
};
|
||||
rcc_do_reset_dev(reset_regs, id);
|
||||
}
|
||||
|
||||
void rcc_set_prescaler(rcc_prescaler prescaler, uint32 divider) {
|
||||
static const uint32 masks[] = {
|
||||
[RCC_PRESCALER_AHB] = RCC_CFGR_HPRE,
|
||||
[RCC_PRESCALER_APB1] = RCC_CFGR_PPRE1,
|
||||
[RCC_PRESCALER_APB2] = RCC_CFGR_PPRE2,
|
||||
[RCC_PRESCALER_USB] = RCC_CFGR_USBPRE,
|
||||
[RCC_PRESCALER_ADC] = RCC_CFGR_ADCPRE,
|
||||
};
|
||||
rcc_do_set_prescaler(masks, prescaler, divider);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue