aboutsummaryrefslogtreecommitdiffstats
path: root/libmaple
diff options
context:
space:
mode:
authorPerry Hung <iperry@gmail.com>2011-03-21 02:45:58 -0400
committerPerry Hung <iperry@gmail.com>2011-03-21 02:47:53 -0400
commit7241820179bb729d14900676fbff943f7f44cb97 (patch)
treed60a38f908eda00b934b6d3452f6a777c4277be6 /libmaple
parent1591fc9e3f851327c16bbcb88e6abee1706f9cfc (diff)
parentc73306508820705eef4f2cb9f8542acdba599cd8 (diff)
downloadlibrambutan-7241820179bb729d14900676fbff943f7f44cb97.tar.gz
librambutan-7241820179bb729d14900676fbff943f7f44cb97.zip
Merge branch 'i2c-wip' into refactor
Conflicts: examples/test-fsmc.cpp libmaple/rules.mk wirish/boards.h
Diffstat (limited to 'libmaple')
-rw-r--r--libmaple/delay.h22
-rw-r--r--libmaple/exc.S4
-rw-r--r--libmaple/i2c.c407
-rw-r--r--libmaple/i2c.h259
-rw-r--r--libmaple/libmaple.h2
-rw-r--r--libmaple/nvic.c33
-rw-r--r--libmaple/nvic.h41
-rw-r--r--libmaple/rcc.c4
-rw-r--r--libmaple/rcc.h2
-rw-r--r--libmaple/rules.mk1
-rw-r--r--libmaple/scb.h58
-rw-r--r--libmaple/stm32.h22
12 files changed, 851 insertions, 4 deletions
diff --git a/libmaple/delay.h b/libmaple/delay.h
new file mode 100644
index 0000000..10839c9
--- /dev/null
+++ b/libmaple/delay.h
@@ -0,0 +1,22 @@
+/**
+ * @brief
+ */
+
+#ifndef _DELAY_H_
+#define _DELAY_H_
+
+static inline void delay_us(uint32 us) {
+ /* So (2^32)/12 micros max, or less than 6 minutes */
+ us *= 12;
+
+ /* fudge for function call overhead */
+ us--;
+ asm volatile(" mov r0, %[us] \n\t"
+ "1: subs r0, #1 \n\t"
+ " bhi 1b \n\t"
+ :
+ : [us] "r" (us)
+ : "r0");
+}
+#endif
+
diff --git a/libmaple/exc.S b/libmaple/exc.S
index f31fccd..7631e48 100644
--- a/libmaple/exc.S
+++ b/libmaple/exc.S
@@ -76,8 +76,8 @@ __exc_usagefault:
.thumb_func
__default_exc:
- ldr r2, NVIC_CCR @ Enabling returning to thread mode from an
- mov r1 ,#1 @ exception. See flag NONEBASETHRDENA.
+ 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
diff --git a/libmaple/i2c.c b/libmaple/i2c.c
new file mode 100644
index 0000000..a6638be
--- /dev/null
+++ b/libmaple/i2c.c
@@ -0,0 +1,407 @@
+/* *****************************************************************************
+ * 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.
+ * ****************************************************************************/
+
+/**
+ * @brief
+ */
+
+#include "libmaple.h"
+#include "rcc.h"
+#include "nvic.h"
+#include "gpio.h"
+#include "nvic.h"
+#include "i2c.h"
+#include "string.h"
+
+static inline int32 wait_for_state_change(i2c_dev *dev, i2c_state state);
+
+static i2c_dev i2c_dev1 = {
+ .regs = I2C1_BASE,
+ .gpio_port = &gpiob,
+ .sda_pin = 7,
+ .scl_pin = 6,
+ .clk_line = RCC_I2C1,
+ .ev_nvic_line = NVIC_I2C1_EV,
+ .er_nvic_line = NVIC_I2C1_ER,
+ .state = I2C_STATE_IDLE
+};
+
+i2c_dev* const I2C1 = &i2c_dev1;
+
+struct crumb {
+ uint32 event;
+ uint32 sr1;
+ uint32 sr2;
+};
+
+#define NR_CRUMBS 128
+static struct crumb crumbs[NR_CRUMBS];
+static uint32 cur_crumb = 0;
+
+static inline void leave_big_crumb(uint32 event, uint32 sr1, uint32 sr2) {
+ if (cur_crumb < NR_CRUMBS) {
+ struct crumb *crumb = &crumbs[cur_crumb++];
+ crumb->event = event;
+ crumb->sr1 = sr1;
+ crumb->sr2 = sr2;
+ }
+}
+
+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 IRQ handler for i2c master. Handles transmission/reception.
+ * @param dev i2c device
+ */
+static void i2c_irq_handler(i2c_dev *dev) {
+ i2c_msg *msg = dev->msg;
+
+ uint8 read = msg->flags & I2C_MSG_READ;
+
+ uint32 sr1 = dev->regs->SR1;
+ uint32 sr2 = dev->regs->SR2;
+ leave_big_crumb(IRQ_ENTRY, sr1, sr2);
+
+ /*
+ * 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);
+ leave_big_crumb(RX_ADDR_START, 0, 0);
+ } else {
+ i2c_stop_condition(dev);
+ leave_big_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.
+ */
+ 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)) {
+ leave_big_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...
+ */
+ throb();
+ }
+ sr1 = sr2 = 0;
+ }
+
+ /*
+ * EV8_2: Master transmitter
+ * Last byte sent, program repeated start/stop
+ */
+ if ((sr1 & I2C_SR1_TXE) && (sr1 & I2C_SR1_BTF)) {
+ leave_big_crumb(TXE_BTF, 0, 0);
+ if (dev->msgs_left) {
+ leave_big_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);
+ leave_big_crumb(STOP_SENT, 0, 0);
+ dev->state = I2C_STATE_XFER_DONE;
+ }
+ sr1 = sr2 = 0;
+ }
+
+ /*
+ * EV7: Master Receiver
+ */
+ if (sr1 & I2C_SR1_RXNE) {
+ leave_big_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);
+ leave_big_crumb(RXNE_START_SENT, 0, 0);
+ } else {
+ i2c_stop_condition(dev);
+ leave_big_crumb(RXNE_STOP_SENT, 0, 0);
+ }
+ } else if (msg->xferred == msg->length) {
+ dev->msgs_left--;
+ if (dev->msgs_left == 0) {
+ /*
+ * We're done.
+ */
+ leave_big_crumb(RXNE_DONE, 0, 0);
+ dev->state = I2C_STATE_XFER_DONE;
+ } else {
+ dev->msg++;
+ }
+ }
+ }
+}
+
+void __irq_i2c1_ev(void) {
+ i2c_irq_handler(&i2c_dev1);
+}
+
+static void i2c_irq_error_handler(i2c_dev *dev) {
+ uint32 sr1 = dev->regs->SR1;
+ uint32 sr2 = dev->regs->SR2;
+ leave_big_crumb(ERROR_ENTRY, sr1, sr2);
+
+ i2c_stop_condition(dev);
+ i2c_disable_irq(dev, I2C_IRQ_BUFFER | I2C_IRQ_EVENT | I2C_IRQ_ERROR);
+ dev->state = I2C_STATE_ERROR;
+}
+
+void __irq_i2c1_er(void) {
+ i2c_irq_error_handler(&i2c_dev1);
+}
+
+static void i2c_bus_reset(const i2c_dev *dev) {
+ /* Release both lines */
+ gpio_write_bit(dev->gpio_port, dev->scl_pin, 1);
+ gpio_write_bit(dev->gpio_port, dev->sda_pin, 1);
+ gpio_set_mode(dev->gpio_port, dev->scl_pin, GPIO_OUTPUT_OD);
+ gpio_set_mode(dev->gpio_port, dev->sda_pin, GPIO_OUTPUT_OD);
+
+ /*
+ * Make sure the bus is free by clocking it until any slaves release the
+ * bus.
+ */
+ while (!gpio_read_bit(dev->gpio_port, dev->sda_pin)) {
+ /* Wait for any clock stretching to finish */
+ while (!gpio_read_bit(dev->gpio_port, dev->scl_pin))
+ ;
+ delay_us(10);
+
+ /* Pull low */
+ gpio_write_bit(dev->gpio_port, dev->scl_pin, 0);
+ delay_us(10);
+
+ /* Release high again */
+ gpio_write_bit(dev->gpio_port, dev->scl_pin, 1);
+ delay_us(10);
+ }
+
+ /* Generate start then stop condition */
+ gpio_write_bit(dev->gpio_port, dev->sda_pin, 0);
+ delay_us(10);
+ gpio_write_bit(dev->gpio_port, dev->scl_pin, 0);
+ delay_us(10);
+ gpio_write_bit(dev->gpio_port, dev->scl_pin, 1);
+ delay_us(10);
+ gpio_write_bit(dev->gpio_port, dev->sda_pin, 1);
+}
+
+/**
+ * @brief Initialize an i2c device as bus master
+ * @param device to enable
+ * @param flags bitwise or of the following I2C options:
+ * I2C_FAST_MODE: 400 khz operation
+ * I2C_10BIT_ADDRESSING: Enable 10-bit addressing
+ */
+void i2c_master_enable(i2c_dev *dev, uint32 flags) {
+#define STANDARD_CCR (PCLK1/(100000*2))
+#define STANDARD_TRISE 37
+ /* Reset the bus. Clock out any hung slaves. */
+ i2c_bus_reset(dev);
+
+ /* Turn on clock and set GPIO modes */
+ rcc_reset_dev(dev->clk_line);
+ rcc_clk_enable(dev->clk_line);
+ gpio_set_mode(dev->gpio_port, dev->sda_pin, GPIO_AF_OUTPUT_OD);
+ gpio_set_mode(dev->gpio_port, dev->scl_pin, GPIO_AF_OUTPUT_OD);
+
+ /* I2C1 and I2C2 are fed from APB1, clocked at 36MHz */
+ i2c_set_input_clk(dev, 36);
+
+ /* 100 khz only for now */
+ i2c_set_clk_control(dev, STANDARD_CCR);
+
+ /*
+ * Set scl rise time, standard mode for now.
+ * Max rise time in standard mode: 1000 ns
+ * Max rise time in fast mode: 300ns
+ */
+ i2c_set_trise(dev, STANDARD_TRISE);
+
+ /* 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);
+
+ /*
+ * 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);
+
+ /* Make it go! */
+ i2c_peripheral_enable(dev);
+}
+
+
+int32 i2c_master_xfer(i2c_dev *dev, i2c_msg *msgs, uint16 num) {
+ int32 rc;
+
+ dev->msg = msgs;
+ dev->msgs_left = num;
+
+ while (dev->regs->SR2 & I2C_SR2_BUSY)
+ ;
+
+ 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);
+ if (rc < 0) {
+ goto out;
+ }
+
+ dev->state = I2C_STATE_IDLE;
+ rc = num;
+out:
+ return rc;
+}
+
+static inline int32 wait_for_state_change(i2c_dev *dev, i2c_state state) {
+ int32 rc;
+ i2c_state tmp;
+
+ while (1) {
+ tmp = dev->state;
+ if ((tmp == state) || (tmp == I2C_STATE_ERROR)) {
+ return (tmp == I2C_STATE_ERROR) ? -1 : 0;
+ }
+ }
+}
+
+
+
+
+
diff --git a/libmaple/i2c.h b/libmaple/i2c.h
new file mode 100644
index 0000000..21c17c1
--- /dev/null
+++ b/libmaple/i2c.h
@@ -0,0 +1,259 @@
+/* *****************************************************************************
+ * 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.
+ * ****************************************************************************/
+
+/**
+ * @brief libmaple i2c header
+ */
+
+#ifndef _I2C_H_
+#define _I2C_H_
+
+typedef struct i2c_reg_map {
+ __io uint32 CR1;
+ __io uint32 CR2;
+ __io uint32 OAR1;
+ __io uint32 OAR2;
+ __io uint32 DR;
+ __io uint32 SR1;
+ __io uint32 SR2;
+ __io uint32 CCR;
+ __io uint32 TRISE;
+} i2c_reg_map;
+
+typedef enum i2c_state {
+ I2C_STATE_IDLE,
+ I2C_STATE_XFER_DONE,
+ I2C_STATE_BUSY,
+ I2C_STATE_ERROR = -1
+} i2c_state;
+
+typedef struct i2c_msg {
+ uint16 addr;
+#define I2C_MSG_READ 0x1
+#define I2C_MSG_10BIT_ADDR 0x2
+ uint16 flags;
+ uint16 length;
+ uint16 xferred;
+ uint8 *data;
+} i2c_msg;
+
+typedef struct i2c_dev {
+ i2c_reg_map *regs;
+ gpio_dev *gpio_port;
+ uint8 sda_pin;
+ uint8 scl_pin;
+ uint8 clk_line;
+ uint8 ev_nvic_line;
+ uint8 er_nvic_line;
+ volatile uint8 state;
+ uint16 msgs_left;
+ i2c_msg *msg;
+} i2c_dev;
+
+
+extern i2c_dev* const I2C1;
+
+#define I2C1_BASE (i2c_reg_map*)0x40005400
+#define I2C2_BASE (i2c_reg_map*)0x40005800
+
+/* i2c enable options */
+#define I2C_FAST_MODE 0x1 // 400 khz
+#define I2C_DUTY_16_9 0x2 // 16/9 duty ratio
+
+/* Control register 1 bits */
+#define I2C_CR1_SWRST BIT(15) // Software reset
+#define I2C_CR1_ALERT BIT(13) // SMBus alert
+#define I2C_CR1_PEC BIT(12) // Packet error checking
+#define I2C_CR1_POS BIT(11) // Acknowledge/PEC position
+#define I2C_CR1_ACK BIT(10) // Acknowledge enable
+#define I2C_CR1_START BIT(8) // Start generation
+#define I2C_CR1_STOP BIT(9) // Stop generation
+#define I2C_CR1_PE BIT(0) // Peripheral Enable
+
+/* Control register 2 bits */
+#define I2C_CR2_LAST BIT(12) // DMA last transfer
+#define I2C_CR2_DMAEN BIT(11) // DMA requests enable
+#define I2C_CR2_ITBUFEN BIT(10) // Buffer interrupt enable
+#define I2C_CR2_ITEVTEN BIT(9) // Event interupt enable
+#define I2C_CR2_ITERREN BIT(8) // Error interupt enable
+#define I2C_CR2_FREQ 0xFFF // Peripheral input frequency
+
+/* Clock control register bits */
+#define I2C_CCR_FS BIT(15) // Master mode selection
+#define I2C_CCR_CCR 0xFFF // Clock control bits
+
+/* Status register 1 bits */
+#define I2C_SR1_SB BIT(0) // Start bit
+#define I2C_SR1_ADDR BIT(1) // Address sent/matched
+#define I2C_SR1_BTF BIT(2) // Byte transfer finished
+#define I2C_SR1_ADD10 BIT(3) // 10-bit header sent
+#define I2C_SR1_STOPF BIT(4) // Stop detection
+#define I2C_SR1_RXNE BIT(6) // Data register not empty
+#define I2C_SR1_TXE BIT(7) // Data register empty
+#define I2C_SR1_BERR BIT(8) // Bus error
+#define I2C_SR1_ARLO BIT(9) // Arbitration lost
+#define I2C_SR1_AF BIT(10) // Acknowledge failure
+#define I2C_SR1_OVR BIT(11) // Overrun/underrun
+#define I2C_SR1_PECERR BIT(12) // PEC Error in reception
+#define I2C_SR1_TIMEOUT BIT(14) // Timeout or Tlow error
+#define I2C_SR1_SMBALERT BIT(15) // SMBus alert
+
+/* Status register 2 bits */
+#define I2C_SR2_MSL BIT(0) // Master/slave
+#define I2C_SR2_BUSY BIT(1) // Bus busy
+#define I2C_SR2_TRA BIT(2) // Transmitter/receiver
+#define I2C_SR2_GENCALL BIT(4) // General call address
+#define I2C_SR2_SMBDEFAULT BIT(5) // SMBus device default address
+#define I2C_SR2_SMBHOST BIT(6) // SMBus host header
+#define I2C_SR2_DUALF BIT(7) // Dual flag
+#define I2C_SR2_PEC 0xFF00 // Packet error checking register
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void i2c_master_enable(i2c_dev *dev, uint32 flags);
+int32 i2c_master_xfer(i2c_dev *dev, i2c_msg *msgs, uint16 num);
+
+/*
+ * Low level register twiddling functions
+ */
+
+/**
+ * @brief turn on an i2c peripheral
+ * @param dev i2c device
+ */
+static inline void i2c_peripheral_enable(i2c_dev *dev) {
+ dev->regs->CR1 |= I2C_CR1_PE;
+}
+
+/**
+ * @brief turn off an i2c peripheral
+ * @param dev i2c device
+ */
+static inline void i2c_peripheral_disable(i2c_dev *dev) {
+ dev->regs->CR1 &= ~I2C_CR1_PE;
+}
+
+/**
+ * @brief Fill transmit register
+ * @param dev i2c device
+ * @param byte byte to write
+ */
+static inline void i2c_write(i2c_dev *dev, uint8 byte) {
+ dev->regs->DR = byte;
+}
+
+
+/**
+ * @brief Set input clock frequency, in mhz
+ * @param dev i2c
+ * @param freq frequency in megahertz (2-36)
+ */
+static inline void i2c_set_input_clk(i2c_dev *dev, uint32 freq) {
+ uint32 cr2 = dev->regs->CR2;
+ cr2 &= ~I2C_CR2_FREQ;
+ cr2 |= freq;
+ dev->regs->CR2 = freq;
+}
+
+
+/**
+ * @brief Set i2c clock control register. See RM008
+ * @param dev i2c device
+ * @return
+ * @sideeffect
+ */
+static inline void i2c_set_clk_control(i2c_dev *dev, uint32 val) {
+ uint32 ccr = dev->regs->CCR;
+ ccr &= ~I2C_CCR_CCR;
+ ccr |= val;
+ dev->regs->CCR = ccr;
+}
+
+static inline void i2c_set_fast_mode(i2c_dev *dev) {
+ dev->regs->CCR |= I2C_CCR_FS;
+}
+
+static inline void i2c_set_standard_mode(i2c_dev *dev) {
+ dev->regs->CCR &= ~I2C_CCR_FS;
+}
+
+/**
+ * @brief Set SCL rise time
+ * @param
+ */
+static inline void i2c_set_trise(i2c_dev *dev, uint32 trise) {
+ dev->regs->TRISE = trise;
+}
+
+static inline void i2c_start_condition(i2c_dev *dev) {
+ uint32 cr1;
+ while ((cr1 = dev->regs->CR1) & (I2C_CR1_START |
+ I2C_CR1_STOP |
+ I2C_CR1_PEC)) {
+ ;
+ }
+ dev->regs->CR1 |= I2C_CR1_START;
+}
+
+static inline void i2c_stop_condition(i2c_dev *dev) {
+ uint32 cr1;
+ while ((cr1 = dev->regs->CR1) & (I2C_CR1_START |
+ I2C_CR1_STOP |
+ I2C_CR1_PEC)) {
+ ;
+ }
+ dev->regs->CR1 |= I2C_CR1_STOP;
+}
+
+static inline void i2c_send_slave_addr(i2c_dev *dev, uint32 addr, uint32 rw) {
+ dev->regs->DR = (addr << 1) | rw;
+}
+
+#define I2C_IRQ_ERROR I2C_CR2_ITERREN
+#define I2C_IRQ_EVENT I2C_CR2_ITEVTEN
+#define I2C_IRQ_BUFFER I2C_CR2_ITBUFEN
+static inline void i2c_enable_irq(i2c_dev *dev, uint32 irqs) {
+ dev->regs->CR2 |= irqs;
+}
+
+static inline void i2c_disable_irq(i2c_dev *dev, uint32 irqs) {
+ dev->regs->CR2 &= ~irqs;
+}
+
+static inline void i2c_enable_ack(i2c_dev *dev) {
+ dev->regs->CR1 |= I2C_CR1_ACK;
+}
+
+static inline void i2c_disable_ack(i2c_dev *dev) {
+ dev->regs->CR1 &= ~I2C_CR1_ACK;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/libmaple/libmaple.h b/libmaple/libmaple.h
index 7814730..8e11660 100644
--- a/libmaple/libmaple.h
+++ b/libmaple/libmaple.h
@@ -31,7 +31,9 @@
#define _LIBMAPLE_H_
#include "libmaple_types.h"
+#include "stm32.h"
#include "util.h"
+#include "delay.h"
/*
* Where to put usercode, based on space reserved for bootloader.
diff --git a/libmaple/nvic.c b/libmaple/nvic.c
index b1da605..28ca169 100644
--- a/libmaple/nvic.c
+++ b/libmaple/nvic.c
@@ -27,6 +27,7 @@
*/
#include "libmaple.h"
+#include "scb.h"
#include "nvic.h"
#include "systick.h"
@@ -64,11 +65,32 @@ void nvic_irq_disable_all(void) {
__write(NVIC_ICER1, 0xFFFFFFFF);
}
+
+/**
+ * @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(int32 irqn, uint8 priority) {
+ if (irqn < 0) {
+ /* This interrupt is in the system handler block */
+ SCB->SHP[((uint32)irqn & 0xF) - 4] = (priority & 0xF) << 4;
+ } else {
+ NVIC->IP[irqn] = (priority & 0xF) << 4;
+ }
+}
+
/**
* @brief Initialize the NVIC according to VECT_TAB_FLASH,
* VECT_TAB_RAM, or VECT_TAB_BASE.
*/
void nvic_init(void) {
+ uint32 i;
+
#ifdef VECT_TAB_FLASH
nvic_set_vector_table(USER_ADDR_ROM, 0x0);
#elif defined VECT_TAB_RAM
@@ -78,4 +100,15 @@ void nvic_init(void) {
#else
#error "You must set a base address for the vector table!"
#endif
+
+ /*
+ * Lower priority level for all peripheral interrupts to lowest
+ * possible.
+ */
+ for (i = 0; i < NR_INTERRUPTS; i++) {
+ nvic_irq_set_priority(i, 0xF);
+ }
+
+ /* Lower systick interrupt priority to lowest level */
+ nvic_irq_set_priority(NVIC_SYSTICK, 0xF);
}
diff --git a/libmaple/nvic.h b/libmaple/nvic.h
index 6004c36..fe9990f 100644
--- a/libmaple/nvic.h
+++ b/libmaple/nvic.h
@@ -47,10 +47,43 @@ extern "C"{
#define NVIC_ICER1 0xE000E184
/* NVIC_ICER2 only on connectivity line */
+/* NVIC Priority */
+#define NVIC_PRI_BASE 0xE000E400
+
/* System control registers */
#define SCB_VTOR 0xE000ED08 // Vector table offset register
+#define NVIC_VectTab_RAM ((u32)0x20000000)
+#define NVIC_VectTab_FLASH ((u32)0x08000000)
+
+#define NVIC_BASE 0xE000E100
+#define NVIC ((nvic_reg_map*)NVIC_BASE)
+
+typedef struct nvic_reg_map {
+ __io uint32 ISER[8]; // Interrupt Set Enable Registers
+ uint32 RESERVED0[24];
+ __io uint32 ICER[8]; // Interrupt Clear Enable Registers
+ uint32 RSERVED1[24];
+ __io uint32 ISPR[8]; // Interrupt Set Pending Registers
+ uint32 RESERVED2[24];
+ __io uint32 ICPR[8]; // Interrupt Clear Pending Registers
+ uint32 RESERVED3[24];
+ __io uint32 IABR[8]; // Interrupt Active bit Registers
+ uint32 RESERVED4[56];
+ __io uint8 IP[240]; // Interrupt Priority Registers
+ uint32 RESERVED5[644];
+ __io uint32 STIR; // Software Trigger Interrupt Registers
+} nvic_reg_map;
+
enum {
+ NVIC_NMI = -14,
+ NVIC_MEM_MANAGE = -12,
+ NVIC_BUS_FAULT = -11,
+ NVIC_USAGE_FAULT = -10,
+ NVIC_SVC = -5,
+ NVIC_DEBUG_MON = -4,
+ NVIC_PEND_SVC = -2,
+ NVIC_SYSTICK = -1,
NVIC_TIMER1 = 27,
NVIC_TIMER2 = 28,
NVIC_TIMER3 = 29,
@@ -80,7 +113,12 @@ enum {
NVIC_DMA_CH4 = 14,
NVIC_DMA_CH5 = 15,
NVIC_DMA_CH6 = 16,
- NVIC_DMA_CH7 = 17
+ NVIC_DMA_CH7 = 17,
+
+ NVIC_I2C1_EV = 31,
+ NVIC_I2C1_ER = 32,
+ NVIC_I2C2_EV = 33,
+ NVIC_I2C2_ER = 34
};
@@ -91,6 +129,7 @@ void nvic_init(void);
void nvic_irq_enable(uint32 device);
void nvic_irq_disable(uint32 device);
void nvic_irq_disable_all(void);
+void nvic_set_priority(int32 irqn, uint8 priority);
#ifdef __cplusplus
}
diff --git a/libmaple/rcc.c b/libmaple/rcc.c
index 2841af3..6445958 100644
--- a/libmaple/rcc.c
+++ b/libmaple/rcc.c
@@ -75,7 +75,9 @@ static const struct rcc_dev_info rcc_dev_table[] = {
[RCC_DMA1] = { .clk_domain = AHB, .line_num = 0 },
[RCC_DMA2] = { .clk_domain = AHB, .line_num = 1 }, // High-density only
[RCC_PWR] = { .clk_domain = APB1, .line_num = 28},
- [RCC_BKP] = { .clk_domain = APB1, .line_num = 27}
+ [RCC_BKP] = { .clk_domain = APB1, .line_num = 27},
+ [RCC_I2C1] = { .clk_domain = APB1, .line_num = 21 }, // High-density only
+ [RCC_I2C2] = { .clk_domain = APB1, .line_num = 22 }, // High-density only
};
/**
diff --git a/libmaple/rcc.h b/libmaple/rcc.h
index 8697e01..dcb9ea2 100644
--- a/libmaple/rcc.h
+++ b/libmaple/rcc.h
@@ -182,6 +182,8 @@ typedef enum {
RCC_DMA2, // High-density devices only (Maple Native)
RCC_PWR,
RCC_BKP,
+ RCC_I2C1,
+ RCC_I2C2
} rcc_clk_id;
void rcc_clk_init(uint32 sysclk_src, uint32 pll_src, uint32 pll_mul);
diff --git a/libmaple/rules.mk b/libmaple/rules.mk
index b264b96..48b5ed4 100644
--- a/libmaple/rules.mk
+++ b/libmaple/rules.mk
@@ -23,6 +23,7 @@ cSRCS_$(d) := adc.c \
iwdg.c \
nvic.c \
pwr.c \
+ i2c.c \
rcc.c \
spi.c \
syscalls.c \
diff --git a/libmaple/scb.h b/libmaple/scb.h
new file mode 100644
index 0000000..f851a08
--- /dev/null
+++ b/libmaple/scb.h
@@ -0,0 +1,58 @@
+/* *****************************************************************************
+ * 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.
+ * ****************************************************************************/
+
+/**
+ * @brief System control block header
+ */
+
+#ifndef _SCB_H_
+#define _SCB_H_
+
+typedef struct scb_reg_map {
+ __io uint32 CPUID; // CPU ID Base Register
+ __io uint32 ICSR; // Interrupt Control State Register
+ __io uint32 VTOR; // Vector Table Offset Register
+ __io uint32 AIRCR; // Application Interrupt / Reset Control Register
+ __io uint32 SCR; // System Control Register
+ __io uint32 CCR; // Configuration Control Register
+ __io uint8 SHP[12]; // System Handlers Priority Registers (4-7, 8-11, 12-15)
+ __io uint32 SHCSR; // System Handler Control and State Register
+ __io uint32 CFSR; // Configurable Fault Status Register
+ __io uint32 HFSR; // Hard Fault Status Register
+ __io uint32 DFSR; // Debug Fault Status Register
+ __io uint32 MMFAR; // Mem Manage Address Register
+ __io uint32 BFAR; // Bus Fault Address Register
+ __io uint32 AFSR; // Auxiliary Fault Status Register
+ __io uint32 PFR[2]; // Processor Feature Register
+ __io uint32 DFR; // Debug Feature Register
+ __io uint32 ADR; // Auxiliary Feature Register
+ __io uint32 MMFR[4]; // Memory Model Feature Register
+ __io uint32 ISAR[5]; // ISA Feature Register
+} scb_reg_map;
+
+#define SCB_BASE 0xE000ED00
+#define SCB ((scb_reg_map*)(SCB_BASE))
+
+#endif
+
diff --git a/libmaple/stm32.h b/libmaple/stm32.h
new file mode 100644
index 0000000..21c18df
--- /dev/null
+++ b/libmaple/stm32.h
@@ -0,0 +1,22 @@
+/**
+ * @brief General STM32 specific definitions
+ */
+
+#ifndef _STM32_H_
+#define _STM32_H_
+
+#define PCLK1 36000000U
+#define PCLK2 72000000U
+
+#ifdef STM32_MEDIUM_DENSITY
+ #define NR_INTERRUPTS 43
+#else
+#ifdef STM32_HIGH_DENSITY
+ #define NR_INTERRUPTS 60
+#else
+#error "No STM32 board type defined!"
+#endif
+#endif
+
+#endif
+