3
votes

I am learning embedded development on the STM3220G-EVAL board with the STM32F207 microcontroller. I have tried to test the I2C interface by interfacing the two I2C2 and I2C3 modules on the same chip and sending/receiving a character. Here is the code I have currently written (using mdk-arm 5):

#include <stm32f2xx.h>

volatile uint8_t data = 'a', recv = 'x';

void i2c_init(void);

void I2C2_EV_IRQHandler(void)
{
    volatile uint16_t stat, dummy;
    stat = I2C2->SR1;
    switch(stat)
    {
        // SB set; read SR1 and write slave address in DR to clear
        case 0x01:
            dummy = I2C2->SR1;
            // Send address of slave
            I2C2->DR = (0x08 << 1);
            break;
        // ADDR set; read SR1 and SR2 to clear
        case 0x02:
            dummy = I2C2->SR1;
            dummy = I2C2->SR2;
            break;
        // TxE set; write to DR to clear
        case 0x80:
            I2C2->DR = data;
            break;
        // TxE and BTF set; generate stop condition to clear
        case 0x84:
            // Generate stop
            I2C2->CR1 |= (1 << 9);
            break;
    }
}

void I2C3_EV_IRQHandler(void)
{
    volatile uint16_t stat, dummy;
    stat = I2C3->SR1;
    switch(stat)
    {
        // ADDR set; read SR1 and SR2 to clear
        case 0x02:
            dummy = I2C3->SR1;
            dummy = I2C3->SR2;
            break;
        // STOPF set; read SR1 and write CR1 to clear
        case 0x10:
            dummy = I2C3->SR1;
            I2C3->CR1 &= ~(1 << 0);
            break;
        // RxNE set; read DR to clear
        case 0x40:
            recv = I2C3->DR;
            break;
    }
}

int main()
{
    i2c_init();
    // Generate START condition
    I2C2->CR1 |= (1 << 8);
    while(1)
    {
        if(!(I2C2->OAR1 & (1 << 14)))
            I2C2->OAR1 |= (1 << 14);
        if(!(I2C3->OAR1 & (1 << 14)))
            I2C3->OAR1 |= (1 << 14);
        if(recv != 'x')
            break;
    }
    return 0;
}

void i2c_init(void)
{
    // Enable GPIOA, GPIOC, GPIOF, I2C2 and I2C3 peripherals
    RCC->AHB1ENR |= (1 << 0);
    RCC->AHB1ENR |= (1 << 2);
    RCC->AHB1ENR |= (1 << 5);
    RCC->APB1ENR |= (1 << 22);
    RCC->APB1ENR |= (1 << 23);
    // Set GPIO mode to AF
    GPIOA->MODER |= (1 << 17);
    GPIOC->MODER |= (1 << 19);
    GPIOF->MODER |= (1 << 1);
    GPIOF->MODER |= (1 << 3);
    // Set GPIO type to OD
    GPIOA->OTYPER |= (1 << 8);
    GPIOC->OTYPER |= (1 << 9);
    GPIOF->OTYPER |= (1 << 0);
    GPIOF->OTYPER |= (1 << 1);
    // Set GPIO speed to 50MHz
    GPIOA->OSPEEDR |= (1 << 17);
    GPIOC->OSPEEDR |= (1 << 19);
    GPIOF->OSPEEDR |= (1 << 1);
    GPIOF->OSPEEDR |= (1 << 3);
    // Link to AFs
    GPIOA->AFR[1] |= (1 << 2);
    GPIOC->AFR[1] |= (1 << 6);
    GPIOF->AFR[0] |= (1 << 2);
    GPIOF->AFR[0] |= (1 << 6);
    // Reset clocks
    I2C2->CR2 = 0x00;
    I2C3->CR2 = 0x00;
    I2C2->CCR = 0x00;
    I2C3->CCR = 0x00;
    // Enable interrupts
    I2C2->CR2 |= (1 << 9);
    I2C2->CR2 |= (1 << 10);
    I2C3->CR2 |= (1 << 9);
    I2C3->CR2 |= (1 << 10);
    NVIC_EnableIRQ(I2C2_EV_IRQn);
    NVIC_EnableIRQ(I2C3_EV_IRQn);
    // Must set bit 14 in OAR1 to 1
    I2C2->OAR1 |= (1 << 14);
    I2C3->OAR1 |= (1 << 14);
    // Set addresses
    I2C2->OAR1 = (0x04 << 1);
    I2C3->OAR1 = (0x08 << 1);
    // Set peripheral clock frequency
    I2C2->CR2 |= 0x08;
    I2C3->CR2 |= 0x08;
    I2C2->CCR |= 0x28;
    I2C3->CCR |= 0x28;
    I2C2->TRISE = 0x09;
    I2C3->TRISE = 0x09;
    // Enable ACK
    I2C2->CR1 |= (1 << 10);
    I2C3->CR1 |= (1 << 10);
    // Enable I2C peripherals
    I2C2->CR1 |= (1 << 0);
    I2C3->CR1 |= (1 << 0);
}

The problems I am facing are:

  1. The execution never goes into the interrupt handlers (verified by breakpoints)
  2. The SB bit in SR1 of the master (I2C2) is never set even though i have set the START bit in CR1
  3. The SDA line is HIGH but the SCL line is pulled LOW

I am using a pullup of 13K on SDA and 10K on SCL. Pin numbers used are PF0, PF1 (I2C2 SDA, SCL) and PA8, PC9 (I2C3 SCL, SDA). Using the internal or external pullups causes the SR2 register to display that the bus is busy. Also I have not enabled I2C2 and I2C3 in RTE_Device.h. It just seems to provide convenience typedefs. (EDIT : Tried to enable these, it does not help)

Could anyone help me in solving this problem? I seem to have hit a dead end.

(EDIT : After setting up a few jumpers on the board, the master event handler is successfully being called. But still some problems persist. Now the acknowledge failure bit is being set, and the slave handler is not called. Bus lines have been verified to be HIGH when idle.)

1
There's a lot of configuration to get right here. Often it's helpful to break the problem down. Do you measure the data lines as ever changing? Can you get them to change if you just configure them as GPIOs and toggle them in a counter loop? Once you know you are sending, then you can try to figure out why you are not receiving. Also check that the system is using your vector table and not some other one, and that the entry is pointing to your ISR and not a default one.Chris Stratton
Thanks for your suggestion. I haven't checked the data lines yet. I have updated the question and some progress has been made regarding the interrupt calls. How can I check if the system is using my vector table?hl2gordon

1 Answers

4
votes

Sorry for the delay in mentioning this, but I have successfully solved this problem by using the STM32 CPAL library available from ST. I have tested this library with the onboard accelerometer by reading the 'WHO_AM_I' register in the accelerometer. The code for this is:

#include "cpal_i2c.h"

int main()
{
    // Configuration
    CPAL_TransferTypeDef RxStruct;
    uint8_t RxBuf;
    RxStruct.pbBuffer = &RxBuf;
    RxStruct.wAddr1 = 0x39;
    // Initialization
    CPAL_I2C_StructInit(&I2C1_DevStructure);
    I2C1_DevStructure.CPAL_Mode = CPAL_MODE_MASTER;
    I2C1_DevStructure.CPAL_ProgModel = CPAL_PROGMODEL_DMA;
    I2C1_DevStructure.pCPAL_I2C_Struct->I2C_ClockSpeed = 100000;
    I2C1_DevStructure.pCPAL_TransferRx = &RxStruct;
    I2C1_DevStructure.pCPAL_TransferTx = pNULL;
    CPAL_I2C_Init(&I2C1_DevStructure);
    // Communication
    RxStruct.wNumData = 1;
    RxStruct.wAddr2 = 0x0F;
    if(CPAL_I2C_Read(&I2C1_DevStructure) != CPAL_PASS)
    {
        // Error
    }
    while(I2C1_DevStructure.CPAL_State != CPAL_STATE_READY);
    while(1);
    return 0;
}