diff options
Diffstat (limited to 'libmaple')
| -rw-r--r-- | libmaple/delay.h | 22 | ||||
| -rw-r--r-- | libmaple/exc.S | 4 | ||||
| -rw-r--r-- | libmaple/i2c.c | 407 | ||||
| -rw-r--r-- | libmaple/i2c.h | 259 | ||||
| -rw-r--r-- | libmaple/libmaple.h | 2 | ||||
| -rw-r--r-- | libmaple/nvic.c | 33 | ||||
| -rw-r--r-- | libmaple/nvic.h | 41 | ||||
| -rw-r--r-- | libmaple/rcc.c | 4 | ||||
| -rw-r--r-- | libmaple/rcc.h | 2 | ||||
| -rw-r--r-- | libmaple/rules.mk | 1 | ||||
| -rw-r--r-- | libmaple/scb.h | 58 | ||||
| -rw-r--r-- | libmaple/stm32.h | 22 | 
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 +  | 
