aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gdbinit9
-rw-r--r--libmaple/i2c.c502
-rw-r--r--libmaple/i2c.h172
-rw-r--r--libmaple/nvic.c33
-rw-r--r--libmaple/nvic.h41
-rw-r--r--libmaple/rcc.c2
-rw-r--r--libmaple/rcc.h2
-rw-r--r--libmaple/rules.mk1
-rw-r--r--libmaple/scb.h58
-rw-r--r--libmaple/stm32.h12
-rw-r--r--main.cpp84
-rw-r--r--test.gdb112
12 files changed, 1027 insertions, 1 deletions
diff --git a/.gdbinit b/.gdbinit
new file mode 100644
index 0000000..9e88d09
--- /dev/null
+++ b/.gdbinit
@@ -0,0 +1,9 @@
+target remote localhost:3333
+symbol-file build/maple.elf
+source test.gdb
+delete breakpoints
+##break main.cpp:setup()
+##monitor reset halt
+#display/i $pc
+# display/x *0xe000ed2c
+# display/x *0xE000ED28
diff --git a/libmaple/i2c.c b/libmaple/i2c.c
new file mode 100644
index 0000000..2064723
--- /dev/null
+++ b/libmaple/i2c.c
@@ -0,0 +1,502 @@
+/* *****************************************************************************
+ * 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
+ * TODO: Rejigger hard fault handler and error throbs to turn off all
+ * interrupts and jump to error throb to reenable usb bootloader
+ */
+
+#include "libmaple.h"
+#include "rcc.h"
+#include "nvic.h"
+#include "gpio.h"
+#include "nvic.h"
+#include "i2c.h"
+#include "string.h"
+
+/* 2/17 Started 10pm-4am
+ * 2/19 Started 7pm-2am
+ * 2/20 Started 7pm-7pm
+ * 2/23 Started 8pm */
+
+static inline int32 wait_for_state_change(i2c_dev *dev, i2c_state state);
+
+i2c_dev i2c_dev1 = {
+ .regs = (i2c_reg_map*)I2C1_BASE,
+ .gpio_port = GPIOB_BASE,
+ .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
+};
+
+/**
+ * @brief IRQ handler for i2c master. Handles transmission/reception.
+ * @param dev i2c device
+ */
+
+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");
+}
+static inline void debug_toggle(uint32 delay) {
+ gpio_write_bit(GPIOA_BASE, 5, 1);
+ delay_us(delay);
+ gpio_write_bit(GPIOA_BASE, 5, 0);
+}
+
+struct crumb {
+ uint32 event;
+ uint32 sr1;
+ uint32 sr2;
+};
+
+static struct crumb crumbs[100];
+static uint32 cur_crumb = 0;
+
+static inline void leave_big_crumb(uint32 event, uint32 sr1, uint32 sr2) {
+ if (cur_crumb < 100) {
+ 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,
+};
+
+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->regs, I2C_IRQ_BUFFER);
+
+ /*
+ * Master receiver
+ */
+ if (read) {
+ i2c_enable_ack(dev->regs);
+ }
+ i2c_send_slave_addr(dev->regs, 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->regs);
+ dev->msgs_left--;
+ if (dev->msgs_left) {
+ i2c_start_condition(dev->regs);
+ leave_big_crumb(RX_ADDR_START, 0, 0);
+ } else {
+ i2c_stop_condition(dev->regs);
+ 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->regs, 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->regs, 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->regs, 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->regs);
+ while (!(dev->regs->SR1 & I2C_SR1_SB))
+ ;
+ dev->msg++;
+ } else {
+ i2c_stop_condition(dev->regs);
+ /*
+ * 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->regs, 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->regs);
+ if (dev->msgs_left > 2) {
+ i2c_start_condition(dev->regs);
+ leave_big_crumb(RXNE_START_SENT, 0, 0);
+ } else {
+ i2c_stop_condition(dev->regs);
+ 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 I2C1_EV_IRQHandler(void) {
+ i2c_dev *dev = I2C1;
+ i2c_irq_handler(dev);
+}
+
+static void i2c_bus_reset(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_MODE_OUTPUT_OD);
+ gpio_set_mode(dev->gpio_port, dev->sda_pin, GPIO_MODE_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_MODE_AF_OUTPUT_OD);
+ gpio_set_mode(dev->gpio_port, dev->scl_pin, GPIO_MODE_AF_OUTPUT_OD);
+
+ /* I2C1 and I2C2 are fed from APB1, clocked at 36MHz */
+ i2c_set_input_clk(dev->regs, 36);
+
+ /* 100 khz only for now */
+ i2c_set_clk_control(dev->regs, 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->regs, STANDARD_TRISE);
+
+ /* Enable event and buffer interrupts */
+ nvic_irq_enable(dev->ev_nvic_line);
+ i2c_enable_irq(dev->regs, I2C_IRQ_EVENT | I2C_IRQ_BUFFER);
+
+ /*
+ * 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);
+
+ /* Make it go! */
+ i2c_peripheral_enable(dev->regs);
+}
+
+
+int32 i2c_master_xfer(i2c_dev *dev, i2c_msg *msgs, uint16 num) {
+ int32 rc;
+ static int times = 0;
+
+ dev->msg = msgs;
+ dev->msgs_left = num;
+
+ memset(crumbs, 0, sizeof crumbs);
+
+ dev->regs->CR2 |= I2C_CR2_ITEVTEN;
+
+ /* Is this necessary? */
+ while (dev->regs->SR2 & I2C_SR2_BUSY)
+ ;
+
+ dev->state = I2C_STATE_BUSY;
+
+ i2c_start_condition(dev->regs);
+ 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;
+ }
+ }
+}
+
+
+/*
+ * Low level register twiddling functions
+ */
+
+/**
+ * @brief turn on an i2c peripheral
+ * @param map i2c peripheral register base
+ */
+void i2c_peripheral_enable(i2c_reg_map *regs) {
+ regs->CR1 |= I2C_CR1_PE;
+}
+
+/**
+ * @brief turn off an i2c peripheral
+ * @param map i2c peripheral register base
+ */
+void i2c_peripheral_disable(i2c_reg_map *regs) {
+ regs->CR1 &= ~I2C_CR1_PE;
+}
+
+/**
+ * @brief Set input clock frequency, in mhz
+ * @param device to configure
+ * @param freq frequency in megahertz (2-36)
+ */
+void i2c_set_input_clk(i2c_reg_map *regs, uint32 freq) {
+ uint32 cr2 = regs->CR2;
+ cr2 &= ~I2C_CR2_FREQ;
+ cr2 |= freq;
+ regs->CR2 = freq;
+}
+
+void i2c_set_clk_control(i2c_reg_map *regs, uint32 val) {
+ uint32 ccr = regs->CCR;
+ ccr &= ~I2C_CCR_CCR;
+ ccr |= val;
+ regs->CCR = ccr;
+}
+
+void i2c_set_fast_mode(i2c_reg_map *regs) {
+ regs->CCR |= I2C_CCR_FS;
+}
+
+void i2c_set_standard_mode(i2c_reg_map *regs) {
+ regs->CCR &= ~I2C_CCR_FS;
+}
+
+/**
+ * @brief Set SCL rise time
+ * @param
+ */
+
+void i2c_set_trise(i2c_reg_map *regs, uint32 trise) {
+ regs->TRISE = trise;
+}
+
+void i2c_start_condition(i2c_reg_map *regs) {
+ regs->CR1 |= I2C_CR1_START;
+}
+
+void i2c_stop_condition(i2c_reg_map *regs) {
+ regs->CR1 |= I2C_CR1_STOP;
+}
+
+void i2c_send_slave_addr(i2c_reg_map *regs, uint32 addr, uint32 rw) {
+ regs->DR = (addr << 1) | rw;
+}
+
+void i2c_enable_irq(i2c_reg_map *regs, uint32 irqs) {
+ regs->CR2 |= irqs;
+}
+
+void i2c_disable_irq(i2c_reg_map *regs, uint32 irqs) {
+ regs->CR2 &= ~irqs;
+}
+
+void i2c_enable_ack(i2c_reg_map *regs) {
+ regs->CR1 |= I2C_CR1_ACK;
+}
+
+void i2c_disable_ack(i2c_reg_map *regs) {
+ regs->CR1 &= ~I2C_CR1_ACK;
+}
+
+
+
diff --git a/libmaple/i2c.h b/libmaple/i2c.h
new file mode 100644
index 0000000..2e6d00d
--- /dev/null
+++ b/libmaple/i2c.h
@@ -0,0 +1,172 @@
+/* *****************************************************************************
+ * 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_Port *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 i2c_dev1;
+extern i2c_dev i2c_dev2;
+
+#define I2C1 (i2c_dev*)&i2c_dev1
+#define I2C2 (i2c_dev*)&i2c_dev2
+
+#define I2C1_BASE 0x40005400
+#define I2C2_BASE 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);
+
+void i2c_start_condition(i2c_reg_map *regs);
+void i2c_stop_condition(i2c_reg_map *regs);
+void i2c_send_slave_addr(i2c_reg_map *regs, uint32 addr, uint32 rw);
+void i2c_set_input_clk(i2c_reg_map *regs, uint32 freq);
+void i2c_set_clk_control(i2c_reg_map *regs, uint32 val);
+void i2c_set_fast_mode(i2c_reg_map *regs);
+void i2c_set_standard_mode(i2c_reg_map *regs);
+void i2c_set_trise(i2c_reg_map *regs, uint32 trise);
+void i2c_enable_ack(i2c_reg_map *regs);
+void i2c_disable_ack(i2c_reg_map *regs);
+
+/* interrupt flags */
+#define I2C_IRQ_ERROR I2C_CR2_ITERREN
+#define I2C_IRQ_EVENT I2C_CR2_ITEVTEN
+#define I2C_IRQ_BUFFER I2C_CR2_ITBUFEN
+void i2c_enable_irq(i2c_reg_map *regs, uint32 irqs);
+void i2c_disable_irq(i2c_reg_map *regs, uint32 irqs);
+void i2c_peripheral_enable(i2c_reg_map *regs);
+void i2c_peripheral_disable(i2c_reg_map *regs);
+
+static inline void i2c_write(i2c_reg_map *regs, uint8 byte) {
+ regs->DR = byte;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
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..d3fb6a3 100644
--- a/libmaple/rcc.c
+++ b/libmaple/rcc.c
@@ -76,6 +76,8 @@ static const struct rcc_dev_info rcc_dev_table[] = {
[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_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 410ab8b..569da57 100644
--- a/libmaple/rcc.h
+++ b/libmaple/rcc.h
@@ -180,6 +180,8 @@ typedef enum {
RCC_DMA2, // High-density devices only (Maple Native)
RCC_PWR,
RCC_BKP,
+ RCC_I2C1,
+ RCC_I2C2
} rcc_clk_id;
diff --git a/libmaple/rules.mk b/libmaple/rules.mk
index b87595d..3d8171f 100644
--- a/libmaple/rules.mk
+++ b/libmaple/rules.mk
@@ -22,6 +22,7 @@ cSRCS_$(d) := adc.c \
gpio.c \
iwdg.c \
nvic.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..e65b28c
--- /dev/null
+++ b/libmaple/stm32.h
@@ -0,0 +1,12 @@
+/**
+ * @brief General STM32 specific definitions
+ */
+
+#ifndef _STM32_H_
+#define _STM32_H_
+
+#define PCLK1 36000000U
+#define PCLK2 72000000U
+
+#endif
+
diff --git a/main.cpp b/main.cpp
new file mode 100644
index 0000000..0480b5d
--- /dev/null
+++ b/main.cpp
@@ -0,0 +1,84 @@
+// Sample main.cpp file. Blinks the built-in LED, sends a message out
+// USART2, and turns on PWM on pin 2.
+
+#include "wirish.h"
+#include "i2c.h"
+
+static const uint8 slave_address = 0b1010001;
+
+#define NR_ELEMENTS 64
+uint8 buf0[NR_ELEMENTS + 2] = {0x0, 0x0};
+uint8 buf1[] = {0x0, 0x0};
+uint8 buf2[NR_ELEMENTS];
+
+void setup() {
+ i2c_msg msgs[3];
+ uint32 i;
+
+ pinMode(BOARD_LED_PIN, OUTPUT);
+
+ i2c_master_enable(I2C1, 0);
+ for (i = 0; i < sizeof buf0; i++) {
+ buf0[i + 2] = i & 0xFF;
+ }
+
+#if 0
+ /* Write some bytes */
+ msgs[0].addr = slave_address;
+ msgs[0].flags = 0;
+ msgs[0].length = sizeof buf0;
+ msgs[0].data = buf0;
+
+ i2c_master_xfer(I2C1, msgs, 1);
+ delay(5);
+ return;
+#endif
+
+ msgs[1].addr = slave_address;
+ msgs[1].flags = 0;
+ msgs[1].length = sizeof buf1;
+ msgs[1].data = buf1;
+
+ msgs[2].addr = slave_address;
+ msgs[2].flags = I2C_MSG_READ;
+ msgs[2].length = sizeof buf2;
+ msgs[2].data = buf2;
+ i2c_master_xfer(I2C1, msgs + 1, 2);
+#if 0
+ for (i = 0; i < 256; i++) {
+ /* Read it back */
+ buf1[1] = i;
+ msgs[1].addr = slave_address;
+ msgs[1].flags = 0;
+ msgs[1].length = 2;
+ msgs[1].data = buf1;
+
+ msgs[2].addr = slave_address;
+ msgs[2].flags = I2C_MSG_READ;
+ msgs[2].length = 1;
+ msgs[2].data = buf2;
+ i2c_master_xfer(I2C1, msgs + 1, 2);
+ delay(10);
+ }
+#endif
+}
+
+void loop() {
+ toggleLED();
+ delay(100);
+}
+
+// Force init to be called *first*, i.e. before static object allocation.
+// Otherwise, statically allocated object that need libmaple may fail.
+__attribute__(( constructor )) void premain() {
+ init();
+}
+
+int main(void) {
+ setup();
+
+ while (1) {
+ loop();
+ }
+ return 0;
+}
diff --git a/test.gdb b/test.gdb
new file mode 100644
index 0000000..8b71320
--- /dev/null
+++ b/test.gdb
@@ -0,0 +1,112 @@
+define i2c_sr1_flags
+set $s = $arg0
+printf "SR1: "
+
+if (($s & (1 << 15)))
+ printf "SMBALERT "
+end
+
+if (($s & (1 << 14)))
+ printf "TIMEOUT "
+end
+
+if (($s & (1 << 12)))
+ printf "PECERR "
+end
+
+if (($s & (1 << 11)))
+ printf "OVR "
+end
+
+if (($s & (1 << 10)))
+ printf "AF "
+end
+
+if (($s & (1 << 9)))
+ printf "ARLO "
+end
+
+if (($s & (1 << 8)))
+ printf "BERR "
+end
+
+if (($s & (1 << 7)))
+ printf "TXE "
+end
+
+if (($s & (1 << 6)))
+ printf "RXNE "
+end
+
+if (($s & (1 << 4)))
+ printf "STOPF "
+end
+
+if (($s & (1 << 3)))
+ printf "ADD10 "
+end
+
+if (($s & (1 << 2)))
+ printf "BTF "
+end
+
+if (($s & (1 << 1)))
+ printf "ADDR "
+end
+
+if (($s & (1 << 0)))
+ printf "SB "
+end
+end
+
+define i2c_sr2_flags
+set $s = $arg0
+printf "SR2: "
+
+if (($s & (1 << 7)))
+ printf "DUALF "
+end
+
+if (($s & (1 << 6)))
+ printf "SMBHOST "
+end
+
+if (($s & (1 << 5)))
+ printf "SMBDEFAULT "
+end
+
+if (($s & (1 << 4)))
+ printf "GENCALL "
+end
+
+
+if (($s & (1 << 2)))
+ printf "TRA "
+end
+
+if (($s & (1 << 1)))
+ printf "BUSY "
+end
+
+if (($s & (1 << 0)))
+ printf "MSL "
+end
+
+end
+
+define pbc
+set $c = crumbs
+while ($c->event)
+ if ($c->event != 0)
+ printf "Event: %d ", $c->event
+ if ($c->event == 1)
+ i2c_sr1_flags $c->sr1
+ printf "\t"
+ i2c_sr2_flags $c->sr2
+ end
+ printf "\n"
+ end
+ set $c = $c + 1
+end
+
+