Forum: Mikrocontroller und Digitale Elektronik I2C mit FreeRTOS auf STM32F103


von Vorname N. (mcu32)


Lesenswert?

Hi,

ich versuche derzeit ein wenig in die Materie von FreeRTOS einzutauchen. 
Das klappt soweit auch ganz gut, das Prinzip hab ich verstanden und 
nicht kommunikative Dinge funktionieren soweit auch nachvollziehbar.

Jetzt habe ich gestern versucht, einen BME280 (Umweltsensor von Bosch) 
der I2C unterstüzt zu integrieren und bin dabei nach der Empfehlung des 
entsprechenden GitHub Repos vorgegangen (Pointer des BME280 Device auf 
entsprechende Funktionen für I2C).

Dafür verwendete ich folgende API die offiziell von Bosch stammt:
https://github.com/BoschSensortec/BME280_driver

Was soll ich sagen,  kompilieren tut es, allerdings hänge ich kurz nach 
dem starten des Debuggings in einer Schleife fest wo er den Systemtick 
erwartet.

Ich nutze die HAL von ST sowie FreeRTOS. Falls jemand helfen kann stelle 
ich auch gerne meinen (relevanten) Code ein. Ich bin mir relativ sicher 
dass es nichts mit der API von Bosch zu tun hat, sondern eher das mir 
das entsprechende Wissen fehlt wie man Kommunikation wie I2C o.ä 
vernünftig in FreeRTOS umsetzt :/.

Das ganze passiert auf einem BluePill Board (mit echtem STM32^^ liegt 
schon ne Weile hier herum). Verkabelung sollte soweit stimmen.

Schonmal vielen Dank.

: Bearbeitet durch User
von hard werker (Gast)


Lesenswert?

Vorname N. schrieb:
> Pointer des BME280 Device auf
> entsprechende Funktionen für I2C

Das ruft nach Klärungsbedarf. Da müsstest du schon etwas
genauer und verbindlicher werden.

"Pointer des BME280 Device" klingt schon sehr nach Geschwurbel.
Die Sprache die hier zur Kommunikation verwendet wird ist
der Quellcode ....

von Vorname N. (mcu32)


Lesenswert?

Hallo nochmal,
wie bereits erwähnt poste ich den Code gerne wenn jemand Helfen mag :)
1
//Associate BME280 with I2C HAL
2
    //Typecast of I2C HAL receive/write function ptr to the suitable format for bme280 driver
3
    bme280.read =  (bme280_read_fptr_t)HAL_I2C_Master_Receive;
4
    bme280.write = (bme280_write_fptr_t)HAL_I2C_Master_Transmit;
5
    bme280.delay_us = (bme280_delay_us_fptr_t)HAL_Delay;

Ich habe das so verstanden, dass die API von Bosch die I2C Funktionen 
der Hal selbstständig aufruft in dem ich die entsprechenden Pointer des 
structs bme280 auf diese zeigen lasse.
1
struct bme280_dev
2
{
3
    /*< Chip Id */
4
    uint8_t chip_id;
5
6
    /*< Interface function pointer used to enable the device address for I2C and chip selection for SPI */
7
    void *intf_ptr;
8
9
    /*< Interface Selection
10
     * For SPI, intf = BME280_SPI_INTF
11
     * For I2C, intf = BME280_I2C_INTF
12
     * */
13
    enum bme280_intf intf;
14
15
    /*< Read function pointer */
16
    bme280_read_fptr_t read;
17
18
    /*< Write function pointer */
19
    bme280_write_fptr_t write;
20
21
    /*< Delay function pointer */
22
    bme280_delay_us_fptr_t delay_us;
23
24
    /*< Trim data */
25
    struct bme280_calib_data calib_data;
26
27
    /*< Sensor settings */
28
    struct bme280_settings settings;
29
30
    /*< Variable to store result of read/write function */
31
    BME280_INTF_RET_TYPE intf_rslt;
32
};

Das gesamte SetUp sieht dann so aus:
1
struct bme280_dev dev;
2
int8_t rslt = BME280_OK;
3
uint8_t dev_addr = BME280_I2C_ADDR_PRIM;
4
5
dev.intf_ptr = &dev_addr;
6
dev.intf = BME280_I2C_INTF;
7
dev.read = user_i2c_read;
8
dev.write = user_i2c_write;
9
dev.delay_ms = user_delay_ms;
10
11
rslt = bme280_init(&dev);

Wenn ich beim Debuggen durch das Programm skippe funktioniert auch alles 
problemlos bis ich diese Funktion aufrufe:
1
int8_t stream_sensor_data_normal_mode(struct bme280_dev *dev)
2
{
3
  int8_t rslt;
4
  uint8_t settings_sel;
5
  struct bme280_data comp_data;
6
7
  /* Recommended mode of operation: Indoor navigation */
8
  dev->settings.osr_h = BME280_OVERSAMPLING_1X;
9
  dev->settings.osr_p = BME280_OVERSAMPLING_16X;
10
  dev->settings.osr_t = BME280_OVERSAMPLING_2X;
11
  dev->settings.filter = BME280_FILTER_COEFF_16;
12
  dev->settings.standby_time = BME280_STANDBY_TIME_62_5_MS;
13
14
  settings_sel = BME280_OSR_PRESS_SEL;
15
  settings_sel |= BME280_OSR_TEMP_SEL;
16
  settings_sel |= BME280_OSR_HUM_SEL;
17
  settings_sel |= BME280_STANDBY_SEL;
18
  settings_sel |= BME280_FILTER_SEL;
19
  rslt = bme280_set_sensor_settings(settings_sel, dev);
20
  rslt = bme280_set_sensor_mode(BME280_NORMAL_MODE, dev);
21
22
  printf("Temperature, Pressure, Humidity\r\n");
23
  while (1) {
24
    /* Delay while the sensor completes a measurement */
25
    dev->delay_us(5, dev->intf_ptr);
26
    rslt = bme280_get_sensor_data(BME280_ALL, &comp_data, dev);
27
    print_sensor_data(&comp_data);
28
  }
29
30
  return rslt;
31
}

Er springt auch problemlos in die I2C HAL Funktion, landet dann aber 
einen Takt später in der SysTick Funktion und kommt da nicht mehr raus. 
Eine Ausgabe erhalte ich auch nicht obwohl das durch die implementierte 
print Funktion in der oben geschriebenen Funktion eigentlich passieren 
sollte (Wenn das Device was gesendet hat und das empfangen wurde).

: Bearbeitet durch User
von Johannes S. (Gast)


Lesenswert?

das kann ja nur funktionieren wenn die HAL mit der erwarteten Signatur 
der I2C Funktionen kompatibel sind, und das sieht nicht so aus.
https://github.com/BoschSensortec/BME280_driver/blob/c47f06eb44fc96970f0abfcc941ec16425b2a9e6/bme280_defs.h#L272

Da sind also Wrapper nötig die diese Argumente nehmen und dann die 
entsprechende HAL Funktion aufrufen.

von Vorname N. (mcu32)


Lesenswert?

Johannes S. schrieb:
> Da sind also Wrapper nötig die diese Argumente nehmen und dann die
> entsprechende HAL Funktion aufrufen.

Ok, ergibt Sinn. Leider hab ich sowas noch nie gemacht :/ Wie wäre hier 
jetzt das vorgehen? Grundsätzlich verstehe ich glaube ich schon was das 
Problem ist, allerdings frage ich mich grade wie ich das umsetzen soll.

Auf jeden Fall schonmal vielen Dank :)

: Bearbeitet durch User
von Vorname N. (mcu32)


Lesenswert?

Ich glaube ich habs jetzt verstanden. Ich zeige quasi auf eine Funktion 
mit den entsprechenden ÜBergabeparametern wie in der oben genannten 
Definition und rufe dann IN dieser Funktion die eigentliche HAL Funktion 
mit den Parametern auf, richtig?

von Vorname N. (mcu32)


Lesenswert?

Ok, also irgendwie klappt das noch nicht so ganz.

Ich habe jetzt folgendes versucht:
1
bme280.read =  (bme280_read_fptr_t)user_i2c_read;
1
int8_t user_i2c_read(uint8_t reg_addr, uint16_t len)
2
{
3
  int8_t err;
4
  err = HAL_I2C_Master_Receive(&hi2c2, dev_addr, &reg_addr, len, 50);
5
  return err;
6
}

Allerdings KANN das nicht funktionieren. Irgendwie muss ich ja alle 
Parameter aus
1
typedef BME280_INTF_RET_TYPE (*bme280_read_fptr_t)(uint8_t reg_addr, uint8_t *reg_data, uint32_t len, void *intf_ptr);
and die HAL übergeben. Allerdings hab ich keine Ahnung wie ich das 
machen soll, denn die HAL nimmt nur
1
HAL_I2C_Master_Receive(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t Timeout)

Wie soll ich denn die ganzen Parameter da rein bringen ? Die DevAddress 
brauche ich ja weil sich sonst das Slave nicht angesprochen fühlt :/.

von Vorname N. (mcu32)


Lesenswert?

Falls irgend jemand hier nochmal drüber stolpern sollte und das selbe 
Problem hat: Ich hab es tatsächlich gelöst :)

Für eine direkte Registeradressierung innerhalb des Slave muss man die 
MemRead /Write Funktionen nehmen. Die Wrapper können dann z.B. so 
ausschauen:
1
int8_t user_i2c_read(uint8_t reg_addr, uint8_t *reg_data, uint32_t len)
2
{
3
4
    HAL_StatusTypeDef status = HAL_OK;
5
6
    //initial value is usually zero
7
  int32_t iError = BME280_INIT_VALUE;
8
9
  uint8_t tempbuff[I2C_BUFFER_LEN] = {BME280_INIT_VALUE};
10
  uint8_t index = BME280_INIT_VALUE;
11
  tempbuff[BME280_INIT_VALUE] = reg_addr;
12
13
  uint8_t trials = 3;
14
  uint8_t timeout = 20;
15
16
  //check if I2C is ready to use, use the generated i2c instance
17
  while (HAL_I2C_IsDeviceReady(&hi2c2, dev_addr, trials, timeout) != HAL_OK) {}
18
19
    status = HAL_I2C_Mem_Read(&hi2c2,            // i2c handle
20
                  (uint16_t)(dev_addr<<1),    // i2c address, left aligned
21
                (uint16_t)reg_addr,      // register address
22
                (uint16_t)I2C_MEMADD_SIZE_8BIT,      // bme280 uses 8bit register addresses
23
                tempbuff,      // write returned data to this variable
24
                (uint16_t)len,              // how many bytes to expect returned
25
                10);              // timeout
26
27
    if (status != HAL_OK)
28
    {
29
      // The BME280 API calls for 0 return value as a success, and -1 returned as failure
30
      iError = (-1);
31
    }
32
  for (index = BME280_INIT_VALUE; index < len; index++) {
33
    *(reg_data + index) = tempbuff[index];
34
  }
35
36
  return (int8_t)iError;
37
}
38
39
40
int8_t user_i2c_write(uint8_t reg_addr, uint8_t *reg_data, uint32_t len)
41
{
42
  HAL_StatusTypeDef status = HAL_OK;
43
44
  //initial value is usually zero
45
  int32_t iError = BME280_INIT_VALUE;
46
47
  uint8_t trials = 3;
48
  uint8_t timeout = 20;
49
50
51
  while (HAL_I2C_IsDeviceReady(&hi2c2, dev_addr, trials, timeout) != HAL_OK) {}
52
53
  status = HAL_I2C_Mem_Write(&hi2c2,            // i2c handle
54
                  (uint16_t)(dev_addr<<1),    // i2c address, left aligned
55
                (uint16_t)reg_addr,      // register address
56
                (uint16_t)I2C_MEMADD_SIZE_8BIT,      // bme280 uses 8bit register addresses
57
                reg_data,    // write returned data to reg_data
58
                (uint16_t)len,              // write how many bytes
59
                10);              // timeout
60
61
    if (status != HAL_OK)
62
      {
63
          // The BME280 API calls for 0 return value as a success, and -1 returned as failure
64
        iError = (-1);
65
      }
66
    return (int8_t)iError;
67
}
68
69
void user_delay(uint32_t us)
70
{
71
  HAL_Delay(us);
72
}

von Harry L. (mysth)


Lesenswert?

Warum nutzt du nicht HAL_I2C_Mem_Read_IT & Co?

Macht das Leben sehr viel einfacher...

von hard werker (Gast)


Lesenswert?

Harry L. schrieb:
> Macht das Leben sehr viel einfacher...

Mir erschliesst sich bis heute nicht warum man auf einem F103
ein wie auch immer gestaltetes RTOS verwendet um einen popeligen
Sensor zu steuern oder auszulesen. Auch nicht gezeigte Zusatz-
Funktionalität macht es noch nicht plausibel sich den Overhead
eines Betriebssystems auf einen mini kleinen Controller anzutun.

von Hans (Gast)


Lesenswert?

hard werker schrieb:
> Mir erschliesst sich bis heute nicht warum man auf einem F103
> ein wie auch immer gestaltetes RTOS verwendet um einen popeligen
> Sensor zu steuern oder auszulesen. Auch nicht gezeigte Zusatz-
> Funktionalität macht es noch nicht plausibel sich den Overhead
> eines Betriebssystems auf einen mini kleinen Controller anzutun.

Vielleicht um wie der TO schrieb

Vorname N. schrieb:
> derzeit ein wenig in die Materie von FreeRTOS einzutauchen

Muss ja nicht immer den "grossen" Sinn machen aber lernen kann man 
daraus und darum geht es anscheinend dem TO.

Ciao Hans

von Vorname N. (mcu32)


Lesenswert?

Hans schrieb:
> Muss ja nicht immer den "grossen" Sinn machen aber lernen kann man
> daraus und darum geht es anscheinend dem TO.

Genau so ist es. Das BluePill Board lag hier noch rum und das Thema RTOS 
interessierte mich schon länger :)

von Johnny B. (johnnyb)


Lesenswert?

hard werker schrieb:
> Mir erschliesst sich bis heute nicht warum man auf einem F103
> ein wie auch immer gestaltetes RTOS verwendet

Also im Vergleich zu vielen 8-Bit oder 16-Bit Mikrocontroller ist selbst 
ein "popeliger" und steinalter STM32F103 ein Monster und steckt wegen 
des grossen RAM's, Flash und hoher CPU Taktfrequenz den Overhead für 
FreeRTOS locker weg, auch wenn man nur ein paar LED's blinken lassen 
will.

von deutschleera (Gast)


Lesenswert?

Johnny B. schrieb:
> des grossen RAM's

Johnny B. schrieb:
> ein paar LED's

Johnny B. schrieb:
> steckt ......... den Overhead für FreeRTOS locker weg

Aber ich steck die Deppenapostrophen nich wech.

http://www.deppenapostroph.info

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.