stm32l152 as I2C slave not acknowledging address

653 views Asked by At

Im using two stm32l152 discovery boars. One is configured as master and the other as slave. I have acknowladge enabled on both of them, but when the master sends the address the slave doesn't send the ack bit at the 9 clock pulse. The ports are set to alternate function 4 and to open drain. I am using external 4.7k pullup resistors to 3.3V. I've checked all the registers multiple times and I don't know why the slave doesn't recognizes it's address.

This is the output from the logic analyser

D3 is the start bit from the master
D4 is the addres matched bit on the slave.
This is the slave code:

#define USE_STDPERIPH_DRIVER
#include "stm32l1xx.h"
#include "stm32l1xx_conf.h"

//Quick hack, approximately 1ms delay
void ms_delay(int ms)
{
    while (ms> 0) {
        volatile int x = 5971;
        while (x> 0) {
            x--;
        }
        ms--;
    }
}
#define SCL 8
#define SDA 9

int main(void)
{
    RCC->AHBENR |= (0x1 << 1);

    //set port to alternate function
    GPIOB->MODER &= ~((0x3 << (2 * SCL)) | (0x3 << (2 * SDA)) | (0x3 << (2 * 5)));
    GPIOB->MODER |= ((0x2 << (2 * SCL)) | (0x2 << (2 * SDA)) | (0x1 << (2 * 5)));

    GPIOB->OTYPER |= ((1 << SCL) | (1 << SDA)); //set output PB6 and PB7 to open drain

    //set PB6 and PB7 to no pullup no pulldown
    GPIOB->PUPDR &= ~((0x3 << (2 * SCL)) | (0x3 << (2 * SDA)) | (0x3 << (2 * 5))); 

    //set PB6 and PB7 to alternate function 4(I2C)
    GPIOB->AFR[1] &= ~((0b1111 << (4 * 0)) | (0b1111 << (4 * 1)));
    //set PB6 and PB7 to alternate function 4(I2C)
    GPIOB->AFR[1] |= ((0b0100 << (4 * 0)) | (0b0100 << (4 * 1)));

    RCC->APB1ENR |= (1 << 21);

    //reset I2C
    I2C1->CR1 |= (1 << 15);
    ms_delay(1);
    I2C1->CR1 &= ~(1 << 15);

    I2C1->CR2 |= 0b001000; //peripheral clock set to 8MHz
    I2C1->CR1 |= (1 << 10); //ACK enabled

    I2C1->OAR1 |= (0x05 << 1); //setting primary address

    I2C1->CR1 |= 1; //I2C peripheral enabled when configuration is done

    for (;;) {
        if ((I2C1->SR1&(1 << 1)) != 0) {
            GPIOB->ODR |= (1 << 5);
        }
        else {
            GPIOB->ODR &= ~(1 << 5);
        }
    }
}

This is the master code:

#define USE_STDPERIPH_DRIVER
#include "stm32l1xx.h"
#include "stm32l1xx_conf.h"

#define SCL 8
#define SDA 9

int main(void)
{
    RCC->AHBENR |= (0x1 << 1);

    //set port to alternate function
    GPIOB->MODER &= ~((0x3 << (2 * SCL)) | (0x3 << (2 * SDA)) | (0x3 << (2 * 5)));
    GPIOB->MODER |= ((0x2 << (2 * SCL)) | (0x2 << (2 * SDA)) | (0x1 << (2 * 5)));

    //set output PB6 and PB7 to open drain
    GPIOB->OTYPER |= ((1 << SCL) | (1 << SDA));
    //set PB6 and PB7 to no pullup no pulldown
    GPIOB->PUPDR &= ~((0x3 << (2 * SCL)) | (0x3 << (2 * SDA)) | (0x3 << (2 * 5)));

    //set PB6 and PB7 to alternate function 4(I2C)
    GPIOB->AFR[1] &= ~((0b1111 << (4 * 0)) | (0b1111 << (4 * 1)));
    //set PB6 and PB7 to alternate function 4(I2C)
    GPIOB->AFR[1] |= ((0b0100 << (4 * 0)) | (0b0100 << (4 * 1)));

    I2C1->CR1 |= (1 << 15);
    I2C1->CR1 &= ~(1 << 15);

    RCC->APB1ENR |= (1 << 21);

    I2C1->CR2 |= 0x08; //peripheral clock set to 8MHz
    I2C1->CCR |= 0x28; //
    I2C1->TRISE |= 0x09;
    I2C1->CR1 |= (1 << 10); //ACK enabled

    I2C1->CR1 |= 1; //I2C peripheral enabled when configuration is done
    I2C1->CR1 |= (1 << 8); //generate start condition (master mode)

    for (;;) {
        //check start condition
        if ((I2C1->SR1&(1 << 0)) != 0) {
            GPIOB->ODR |= (1 << 5);
            I2C1->DR = 0x0b << 0; //send slave addres
        }
        else {
            GPIOB->ODR &= ~(1 << 5);
        }

        if ((I2C1->SR1&(1 << 1)) != 0) {
            GPIOB->ODR |= (1 << 5);
        }
        else {
            GPIOB->ODR &= ~(1 << 5);
        }
    }
}

Im compiling with arm-none-eabi-gcc and using the stsw-stm32077 librarys from stm

1

There are 1 answers

0
urbu On BEST ANSWER

The problem with the code is that you have to set the ack bit after the peripheral is enabled, if you do it before, the ack bit gets automatically reset.