Forum: Mikrocontroller und Digitale Elektronik STM3103RBT6 ist mein I²C Master mit CMSIS so korrekt?


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Stefan ⛄ F. (stefanus)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Leute,
ich probiere gerade zum ersten mal die I²C Schnittstelle meines Nucleo64 
Boardes mit dem STM32F103RBT6 aus. Zunächst geht es mit um den Master 
Modus mit Polling. Mit IRQ und DMA befasse ich mich ein anderes mal.

Der Slave ist ein Ardunino Nano mit einem eilig zusammen geklöppelten 
Testprogramm, weil ich gerade keinen anderen Slave da habe. Er simuliert 
ein Gerät mit 6 Registern, welche beim Lesen immer die folgenden 
hardcodierten Werte liefern (Schreibzugriffe bewirken nichts):

Register 0: 0x10 = 16
Register 1: 0x11 = 17
Register 2: 0x22 = 34
Register 3: 0x44 = 64
Register 4: 0x88 = 136
Register 5: 0xFF = 255

Das Programm soll die ersten 3 Register mit den Werten 1,2,3 
beschreiben, danach soll es die ersten 4 Register lesen.

Ich wüsste gerne, ob ich das folgende Programm richtig umgesetzt habe. 
Es funktioniert offensichtlich, da ich die erwarteten Testdaten vom 
Slave empfange. Auch der Mitschnitt des Logic Analyzers sieht gut aus, 
soweit ich das beurteilen kann.
#include "stm32f1xx.h"

// receive buffer
uint8_t buffer[20];


int main(void)
{
    // Enable Port B and alternate functions
    SET_BIT(RCC->APB2ENR, RCC_APB2ENR_IOPBEN);
    SET_BIT(RCC->APB2ENR, RCC_APB2ENR_AFIOEN);

    // Configure I2C
    SET_BIT(RCC->APB1ENR, RCC_APB1ENR_I2C1EN);           // Enable clock
    MODIFY_REG(I2C1->CR2, I2C_CR2_FREQ, 8);              // Peripheral clock is 8MHz
    MODIFY_REG(I2C1->CCR, I2C_CCR_CCR, 40);              // Clock pulse width 5µs+5µs
    MODIFY_REG(I2C1->TRISE, I2C_TRISE_TRISE, 9);         // Maximum rise time 1µs (+1)
    SET_BIT(I2C1->CR1, I2C_CR1_PE);                      // enable the peripheral

    // remap I2C to PB8=SCL, PB9=SDA as alternate function open-drain 2MHz
    // Must be done after the I2C initialization otherwise the I/O pins 
    // would start with wrong logic level
    SET_BIT(AFIO->MAPR, AFIO_MAPR_I2C1_REMAP); 
    MODIFY_REG(GPIOB->CRH, GPIO_CRH_CNF8  + GPIO_CRH_MODE8,
                           GPIO_CRH_CNF8_0 + GPIO_CRH_CNF8_1 + GPIO_CRH_MODE8_1);
    MODIFY_REG(GPIOB->CRH, GPIO_CRH_CNF9  + GPIO_CRH_MODE9,
                           GPIO_CRH_CNF9_0 + GPIO_CRH_CNF9_1 + GPIO_CRH_MODE9_1);


        // Send some bytes
        SET_BIT(I2C1->CR1, I2C_CR1_START);               // send START condition
        while (!READ_BIT(I2C1->SR1, I2C_SR1_SB));        // wait until START has been generated
        WRITE_REG(I2C1->DR,8<<1);                        // send 7bit slave address 8
        while (!READ_BIT(I2C1->SR1, I2C_SR1_ADDR));      // wait until address has been sent
        READ_REG(I2C1->SR2);                             // clear ADDR
        WRITE_REG(I2C1->DR,0);                           // send register no
        while (!READ_BIT(I2C1->SR1, I2C_SR1_TXE));       // wait until Tx register is empty
        WRITE_REG(I2C1->DR,1);                           // send data byte
        while (!READ_BIT(I2C1->SR1, I2C_SR1_TXE));       // wait until Tx register is empty
        WRITE_REG(I2C1->DR,2);                           // send data byte
        while (!READ_BIT(I2C1->SR1, I2C_SR1_TXE));       // wait until Tx register is empty
        WRITE_REG(I2C1->DR,3);                           // send data byte
        while (!READ_BIT(I2C1->SR1, I2C_SR1_BTF));       // wait until last byte transfer has finished
        SET_BIT(I2C1->CR1, I2C_CR1_STOP);                // send STOP condition
        while (READ_BIT(I2C1->CR1, I2C_CR1_STOP));       // wait until STOP has been generated

        // Read some bytes (send register no)
        SET_BIT(I2C1->CR1, I2C_CR1_START);               // send START condition
        while (!READ_BIT(I2C1->SR1, I2C_SR1_SB));        // wait until START has been generated
        WRITE_REG(I2C1->DR,8<<1);                        // send 7bit slave address 8
        while (!READ_BIT(I2C1->SR1, I2C_SR1_ADDR));      // wait until address has been sent
        READ_REG(I2C1->SR2);                             // clear ADDR
        WRITE_REG(I2C1->DR,0);                           // send register no
        while (!READ_BIT(I2C1->SR1, I2C_SR1_BTF));       // wait until last byte transfer has finished
        SET_BIT(I2C1->CR1, I2C_CR1_STOP);                // send STOP condition
        while (READ_BIT(I2C1->CR1, I2C_CR1_STOP));       // wait until STOP has been generated

        // Read some bytes (read data)
        SET_BIT(I2C1->CR1, I2C_CR1_ACK);
        SET_BIT(I2C1->CR1, I2C_CR1_START);               // send START condition
        while (!READ_BIT(I2C1->SR1, I2C_SR1_SB));        // wait until START has been generated
        WRITE_REG(I2C1->DR,(8<<1)+1);                    // send 7bit slave address 8 + read mode
        while (!READ_BIT(I2C1->SR1, I2C_SR1_ADDR));      // wait until address has been sent
        READ_REG(I2C1->SR2);                             // clear ADDR
        while (!READ_BIT(I2C1->SR1, I2C_SR1_RXNE));      // wait until a data byte has been received
        buffer[0]=READ_REG(I2C1->DR);                    // read data
        while (!READ_BIT(I2C1->SR1, I2C_SR1_RXNE));      // wait until a data byte has been received
        buffer[1]=READ_REG(I2C1->DR);                    // read data
        while (!READ_BIT(I2C1->SR1, I2C_SR1_BTF));       // for the penultimate data bye, we have to wait a bit longer before clearing the ACK
        CLEAR_BIT(I2C1->CR1, I2C_CR1_ACK);               // prepare to send a NACK
        __disable_irq(); // workaround AN2824
        SET_BIT(I2C1->CR1, I2C_CR1_STOP);                // prepare to send a STOP condition
        buffer[2]=READ_REG(I2C1->DR);                    // read the penultimate data byte
        __enable_irq();  // workaround AN2824
        while (!READ_BIT(I2C1->SR1, I2C_SR1_RXNE));      // wait until a data byte has been received
        buffer[3]=READ_REG(I2C1->DR);                    // read the last data byte
        while (READ_BIT(I2C1->SR1, I2C_CR1_STOP));       // wait until STOP has been generated
}

Mit ist ein kleiner Fehler aufgefallen: Ganz am Ende sehe ich ein ACK, 
dort hätte es ein NACK sein sollen. Ich erkenne aber die Ursache dieses 
fehlers nicht. Kann mir dabei jemand helfen?

: Bearbeitet durch User
von Stefan ⛄ F. (stefanus)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
ich glaube ich hab's jetzt. Entweder ist die Appnote AN2824 fehlerhaft 
oder ich habe sie falsch verstanden. Wenn ich mich strikt ans 
Referenzmanual halte, klappt es nämlich.

Der letzte Code-Block zum Empfangen sieht jetzt so aus:
// Read some bytes (data)
SET_BIT(I2C1->CR1, I2C_CR1_ACK);
SET_BIT(I2C1->CR1, I2C_CR1_START);               // send START condition
while (!READ_BIT(I2C1->SR1, I2C_SR1_SB));        // wait until START has been generated
WRITE_REG(I2C1->DR,(8<<1)+1);                    // send 7bit slave address 8 + read mode
while (!READ_BIT(I2C1->SR1, I2C_SR1_ADDR));      // wait until address has been sent
READ_REG(I2C1->SR2);                             // clear ADDR
while (!READ_BIT(I2C1->SR1, I2C_SR1_RXNE));      // wait until a data byte has been received
buffer[0]=READ_REG(I2C1->DR);                    // read data
while (!READ_BIT(I2C1->SR1, I2C_SR1_RXNE));      // wait until a data byte has been received
;                                                // do not read data to initiate clock stretching
while (!READ_BIT(I2C1->SR1, I2C_SR1_BTF));       // wait until clock stretching begins
CLEAR_BIT(I2C1->CR1, I2C_CR1_ACK);               // prepare to send a NACK
buffer[1]=READ_REG(I2C1->DR);                    // read data
__disable_irq();
SET_BIT(I2C1->CR1, I2C_CR1_STOP);                // prepare to send a STOP condition
buffer[2]=READ_REG(I2C1->DR);                    // read data
__enable_irq();
while (!READ_BIT(I2C1->SR1, I2C_SR1_RXNE));      // wait until a data byte has been received
buffer[3]=READ_REG(I2C1->DR);                    // read data
while (READ_BIT(I2C1->SR1, I2C_CR1_STOP));       // wait until STOP has been generated

http://www.st.com/content/ccc/resource/technical/document/application_note/5d/ae/a3/6f/08/69/4e/9b/CD00209826.pdf/files/CD00209826.pdf/jcr:content/translations/en.CD00209826.pdf

Ich denke, die Figure1 auf Seite 7 ist falsch, da fehlt "Read Data 
N-2)".

: Bearbeitet durch User
von Stefan ⛄ F. (stefanus)


Bewertung
0 lesenswert
nicht lesenswert
Vielen Danke für eure Hilfe (in dem anderen Thread bezüglich des NACK). 
Ich habe noch eine Fehler entdeckt: Statt Repeat-Start hat kein Programm 
Stop+Start gesendet. Das ist jetzt auch korrigiert.

Folgender Quelltext ist jetzt dabei heraus gekommen:

Initialisierung der I2C Schnittstelle:
/**
 * Initialize the I²C interface.
 * Note that the variable SystemCoreClock must be set before.
 * The related I/O pins must be configured for alternate I/O open-drain AFTER this function.
 * 
 * @param registerStruct May be either I2C1 or I2C2
 * @param fastMode low=100kHz, high=400kHz
 */
void i2c_init(I2C_TypeDef* registerStruct, bool fastMode)
{
    // Enable clock
    if (registerStruct==I2C1)
    {
        SET_BIT(RCC->APB1ENR, RCC_APB1ENR_I2C1EN);
    }
    else if (registerStruct==I2C2)
    {
        SET_BIT(RCC->APB1ENR, RCC_APB1ENR_I2C2EN);
    }
    MODIFY_REG(registerStruct->CR2, I2C_CR2_FREQ, SystemCoreClock/1000000);     // System clock in MHz
    if (fastMode)
    {
        MODIFY_REG(registerStruct->CCR, I2C_CCR_CCR, SystemCoreClock/800000);   // Clock pulse width 125ns+125ns
        MODIFY_REG(registerStruct->TRISE, I2C_TRISE_TRISE, 3);                  // Maximum rise time 250ns (+1)
    }
    else
    {
        MODIFY_REG(registerStruct->CCR, I2C_CCR_CCR, SystemCoreClock/200000);   // Clock pulse width 500ns+500ns
        MODIFY_REG(registerStruct->TRISE, I2C_TRISE_TRISE, 9);                  // Maximum rise time 100ns (+1)
    }
    SET_BIT(registerStruct->CR1, I2C_CR1_PE); // enable the peripheral
}

Senden und empfangen:
/**
 * Perform an I²C transaction, which sends 0 or more data bytes, followed by receiving 0 or more data bytes.
 * 
 * @param registerStruct May be either I2C1 or I2C2
 * @param slave_addr 7bit slave_addr (will be shifted within this function)
 * @param send_buffer Points to the buffer that contains the data bytes that shall be sent (may be 0 if not used)
 * @param send_size Number of bytes to send
 * @param receive_buffer Points to the buffer will be filled with the received bytes (may be 0 if ot used, may be the same as the send_buffer)
 * @param receive_size Number of bytes to receive
 * @return Number of received data bytes
 */
unsigned int i2c_communicate(I2C_TypeDef* registerStruct, uint8_t slave_addr, void* send_buffer, unsigned int send_size, void* receive_buffer, unsigned int receive_size)
{
    unsigned int receive_count=0;

    // shift the 7bit address to the right position
    slave_addr=slave_addr<<1;

    // Send data
    if (send_size>0)
    {
        // Send START and slave address
        SET_BIT(registerStruct->CR1, I2C_CR1_START);                 // send START condition
        while (!READ_BIT(registerStruct->SR1, I2C_SR1_SB));          // wait until START has been generated
        WRITE_REG(registerStruct->DR,slave_addr);                    // send slave address
        while (!READ_BIT(registerStruct->SR1, I2C_SR1_ADDR))         // wait until address has been sent
        {
            if (READ_BIT(registerStruct->SR1, I2C_SR1_AF))
            {
                // did not receive ACK after address
                goto error;
            }
        }

        READ_REG(registerStruct->SR2);                               // clear ADDR
        while (send_size>0)
        {
            WRITE_REG(registerStruct->DR,*((uint8_t*)send_buffer));  // send register no
            while (!READ_BIT(registerStruct->SR1, I2C_SR1_TXE))      // wait until Tx register is empty
            {
                if (READ_BIT(registerStruct->SR1, I2C_SR1_AF))
                {
                    // did not receive ACK after data byte
                    goto error;
                }
            }
            send_buffer++;
            send_size--;
        }
        while (!READ_BIT(registerStruct->SR1, I2C_SR1_BTF))          // wait until last byte transfer has finished
        {
            if (READ_BIT(registerStruct->SR1, I2C_SR1_AF))
            {
                // did not receive ACK after data byte
                goto error;
            }
        }
    }

    CLEAR_BIT(registerStruct->CR1, I2C_CR1_POS);                     // POS=0
    SET_BIT(registerStruct->CR1, I2C_CR1_ACK);                       // acknowledge each byte

    // Receive data
    // The procedure includes workaround as described in AN2824
    if (receive_size>0)
    {
        // Send (RE-)START and slave address
        SET_BIT(registerStruct->CR1, I2C_CR1_START);                  // send START condition
        while (!READ_BIT(registerStruct->SR1, I2C_SR1_SB));           // wait until START has been generated
        WRITE_REG(registerStruct->DR,slave_addr+1);                   // send slave address + read mode
        while (!READ_BIT(registerStruct->SR1, I2C_SR1_ADDR))          // wait until address has been sent
        {
            if (READ_BIT(registerStruct->SR1, I2C_SR1_AF))
            {
                // did not receive ACK after address
                goto error;
            }
        }

        if (receive_size>2)
        {
            READ_REG(registerStruct->SR2);                                // clear ADDR
            while (receive_size>3)
            {
                while (!READ_BIT(registerStruct->SR1, I2C_SR1_RXNE));     // wait until a data byte has been received
                *((uint8_t*)receive_buffer)=READ_REG(registerStruct->DR); // read data
                receive_size--;
                receive_count++;
                receive_buffer++;
            }
            while (!READ_BIT(registerStruct->SR1, I2C_SR1_BTF));           // wait until 2 bytes are received
            CLEAR_BIT(registerStruct->CR1, I2C_CR1_ACK);                  // prepare to send a NACK
            *((uint8_t*)receive_buffer)=READ_REG(registerStruct->DR);     // read data
            receive_size--;
            receive_count++;
            receive_buffer++;
            __disable_irq();
            {
                SET_BIT(registerStruct->CR1, I2C_CR1_STOP);               // prepare to send a STOP condition
                *((uint8_t*)receive_buffer)=READ_REG(registerStruct->DR); // read data
                receive_size--;
                receive_count++;
                receive_buffer++;
            }
            __enable_irq();
        }
        else if (receive_size==2)
        {
            SET_BIT(registerStruct->CR1, I2C_CR1_POS);                    // NACK shall be applied to the next byte, not the current byte
            __disable_irq();
            {
                READ_REG(registerStruct->SR2);                            // clear ADDR
                CLEAR_BIT(registerStruct->CR1, I2C_CR1_ACK);              // prepare to send a NACK
            }
            __enable_irq();
            while (!READ_BIT(registerStruct->SR1, I2C_SR1_BTF));          // wait until 2 bytes are received
            __disable_irq();
            {
                SET_BIT(registerStruct->CR1, I2C_CR1_STOP);               // prepare to send a STOP condition
                *((uint8_t*)receive_buffer)=READ_REG(registerStruct->DR); // read data
                receive_size--;
                receive_count++;
                receive_buffer++;
             }
            __enable_irq();
        }
        else if (receive_size==1)
        {
            CLEAR_BIT(registerStruct->CR1, I2C_CR1_ACK);                  // prepare to send a NACK
            __disable_irq();
            {
                READ_REG(registerStruct->SR2);                            // clear ADDR
                SET_BIT(registerStruct->CR1, I2C_CR1_STOP);               // prepare to send a STOP condition
            }
            __enable_irq();
            while (!READ_BIT(registerStruct->SR1, I2C_SR1_RXNE));         // wait until a data byte has been received
        }

        *((uint8_t*)receive_buffer)=READ_REG(registerStruct->DR);         // read the last data byte
        receive_size--;
        receive_count++;
        receive_buffer++;
        while (READ_BIT(registerStruct->SR1, I2C_CR1_STOP));              // wait until STOP has been generated
    }

    else if (receive_size==0 && send_size>0)
    {
        SET_BIT(registerStruct->CR1, I2C_CR1_STOP);                       // send STOP condition
        while (READ_BIT(registerStruct->CR1, I2C_CR1_STOP));              // wait until STOP has been generated
    }

    return receive_count;

    error:
    SET_BIT(registerStruct->CR1, I2C_CR1_STOP);                           // send STOP condition
    while (READ_BIT(registerStruct->CR1, I2C_CR1_STOP));                  // wait until STOP has been generated
    CLEAR_BIT(registerStruct->CR1, I2C_CR1_PE);                           // restart the I2C interface clear all error flags
    SET_BIT(registerStruct->CR1, I2C_CR1_PE);
    ITM_SendString("I2C bus error!\n");
    return receive_count;
}

Beispiel Hauptprogramm:
int main(void)
{
    // Initialize system timer
    SysTick_Config(SystemCoreClock/1000);

    // Enable Port B and alternate functions
    SET_BIT(RCC->APB2ENR, RCC_APB2ENR_IOPBEN);
    SET_BIT(RCC->APB2ENR, RCC_APB2ENR_AFIOEN);

    // Initialize I2C1, no fast mode
    i2c_init(I2C1,false);

    // I2C1 uses remapped PB8=SCL, PB9=SDA, alternate function open-drain 2MHz
    // Must be done after the I2C initialization otherwise the I/O pins would start with wrong logic level
    SET_BIT(AFIO->MAPR, AFIO_MAPR_I2C1_REMAP);
    MODIFY_REG(GPIOB->CRH, GPIO_CRH_CNF8  + GPIO_CRH_MODE8,  GPIO_CRH_CNF8_0 + GPIO_CRH_CNF8_1 + GPIO_CRH_MODE8_1);
    MODIFY_REG(GPIOB->CRH, GPIO_CRH_CNF9  + GPIO_CRH_MODE9,  GPIO_CRH_CNF9_0 + GPIO_CRH_CNF9_1 + GPIO_CRH_MODE9_1);

    // Send one byte with value 0, then receive 5 bytes
    uint8_t buffer[20];
    buffer[0]=0;
    int received=i2c_communicate(I2C1, 8, buffer, 1, buffer, 5);
}

von Regel Erkleerbehr (Gast)


Angehängte Dateien:

Bewertung
1 lesenswert
nicht lesenswert
Stefanus F. schrieb:
> Folgender Quelltext ist jetzt dabei heraus gekommen:

... und du meinst das ist kein längerer Sourcecode?

von Stefan ⛄ F. (stefanus)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Für Hilfreiche Beiträge wäre ich dankbarer.

Wie dem auch sei, ich habe noch einen Screenshot vom Logic Analyzer für 
die gleiche Sequenz gemacht, wie bei den vorherigen Screenshots:
        uint8_t buffer[30];
        buffer[0]=0;
        buffer[1]=0;
        buffer[2]=0;
        buffer[3]=0;

        // Send 3 Bytes starting at register 0
        int received=i2c_communicate(I2C1, 8, buffer, 4, 0, 0);

        // Receive 4 Bytes starting at register 0
        received=i2c_communicate(I2C1, 8, buffer, 1, buffer, 4);

Ich sehe im Bild keinen Fehler mehr.

: Bearbeitet durch User
von Regel Erkleerbehr (Gast)


Bewertung
-1 lesenswert
nicht lesenswert
Stefanus F. schrieb:
> Für Hilfreiche Beiträge wäre ich dankbarer.

Dieser Beitrag von mir ist für dein Posting-Verhalten
äusserst hilfreich, es sei denn du bist beratungsresistent
oder betonköpfig.

von Stefan ⛄ F. (stefanus)


Bewertung
0 lesenswert
nicht lesenswert
Eine Korrektur für die init Funktion, die funktionierte nur bei 8Mhz 
korrekt:
/**
 * Initialize the I²C interface.
 * The related I/O pins must be configured for alternate I/O open-drain AFTER this function.
 *
 * @param registerStruct May be either I2C1 or I2C2
 * @param fastMode low=100kHz, high=400kHz
 * @param apb1_clock clock frequency of APB1 peripherals
 */
void i2c_init(I2C_TypeDef* registerStruct, bool fastMode, long int apb1_clock)
{
    // Enable clock
    if (registerStruct==I2C1)
    {
        SET_BIT(RCC->APB1ENR, RCC_APB1ENR_I2C1EN);
    }
    else if (registerStruct==I2C2)
    {
        SET_BIT(RCC->APB1ENR, RCC_APB1ENR_I2C2EN);
    }
    
    // Configure timing
    MODIFY_REG(registerStruct->CR2, I2C_CR2_FREQ, apb1_clock/1000000); 
    if (fastMode)
    {
        MODIFY_REG(registerStruct->CCR, I2C_CCR_CCR, apb1_clock/800000); 
        MODIFY_REG(registerStruct->TRISE, I2C_TRISE_TRISE, apb1_clock/4000000+1); 
    }
    else
    {
        MODIFY_REG(registerStruct->CCR, I2C_CCR_CCR, apb1_clock/200000); 
        MODIFY_REG(registerStruct->TRISE, I2C_TRISE_TRISE, apb1_clock/1000000+1);
    }
    
    // enable the peripheral
    SET_BIT(registerStruct->CR1, I2C_CR1_PE); 
}

: Bearbeitet durch User

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.