Skocz do zawartości

Kurs STM32 - #11 - I2C w praktyce, pamięć eeprom


Pomocna odpowiedź

Napisano
html_mig_img
Wcześniej poznaliśmy dwa interfejsy szeregowe: UART i SPI. Pierwszy wymagał tylko dwóch linii, ale był dość powolny. Drugi działał bardzo szybko wykorzystując więcej wyprowadzeń.Teraz zajmiemy się I2C, który kwalifikuje się gdzieś pomiędzy wcześniejszymi interfejsami.

UWAGA, to tylko wstęp! Dalsza część artykułu dostępna jest na blogu.

Przeczytaj całość »

Poniżej znajdują się komentarze powiązane z tym wpisem.

  • Lubię! 1
  • 2 tygodnie później...

Witam. Osobiście przerabiam ten kurs na stm32f0 discovery. Do tej pory większość zmian to była kosmetyka. Teraz utknąłem na:

i2c.I2C_ClockSpeed = 100000

SPL dla F0 nie przewiduje tej opcji. Jak to obejść?

mareq, witam na forum. Kurs dotyczy rodziny F1, więc jeśli masz problem z innym układem stwórz proszę osobny temat i opisz tam swój problem. Nie dyskutujmy o innych układach w kursie, aby nie robić początkującym zbędnego zamieszania.

  • 1 miesiąc później...

kubavit, jeśli oczekujesz pomocy na naszym forum, to miło by było, gdybyś opisał tutaj problem. Nie każdy ma konto na linkowanej przez Ciebie stronie 🙂

Przepraszam, zapomniałem że bez konta nie zobzczycie posta. A więc tak:

Przenoszę obsługę czujnika HIH6030 z AVR na STM32. Na Atmedze działał poprawnie, więc adresy i wysyłane sekwencje są w odpowiedniej kolejności. Problem polega na tym, że po ponownym wysłaniu bitu startu i próbie zapisu (i2c_write_byte( (HIH6030_ADDR<< 1) | 0x01)) program się zawiesza i czeka na odpowiednią flagę w pętli while. Załączam kod i schemat podłączonego układu.

PLIK I2C.C

#include "main.h"

/* I2C init */
void I2Cinit(void)
{
       RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB);
       /* Enable clock for alternative functions */
       RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
       /* Enable clock for I2C1 */
       RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);

       GPIO_InitTypeDef gpio;
       I2C_InitTypeDef i2c;

       /* init GPIO stricture for SDA & SCL */
       GPIO_StructInit(&gpio);
       gpio.GPIO_Pin = I2C1_SDA_PINx | I2C1_SCL_PINx;
       /* Outputs must be Open Drain - no pullup ! */
       gpio.GPIO_Mode = GPIO_Mode_AF_OD;
       gpio.GPIO_Speed = GPIO_Speed_50MHz;
       GPIO_Init(I2C1_REGx, &gpio);

       /* init I2C structure */
       I2C_StructInit(&i2c);
       i2c.I2C_Mode = I2C_Mode_I2C;
       /* 100 kHz speed - max 400 kHz but depends on wire's length */
       /* the longer wires are, the lowest speed shuold be used        */
       /* to avoid transmission errors                                                         */
       i2c.I2C_ClockSpeed = 100000;
       I2C_Init(I2C1, &i2c);
       I2C_Cmd(I2C1, ENABLE);
}

/* I2C start */
void i2c(void)
{
       I2C_GenerateSTOP(I2C1, ENABLE);
}

PLIK I2C.H

#ifndef I2C_H_
#define I2C_H_

#define I2C1_SDA_PINx   (GPIO_Pin_7)
#define I2C1_SCL_PINx   (GPIO_Pin_6)
#define I2C1_REGx               (GPIOB)

typedef enum I2C_Dir_Tag
{
       I2C_WriteData,
       I2C_ReadData
} I2C_Dir_T;

void I2Cinit(void);
void i2c(void);
uint8_t i2c_read_byte(void);
void i2c_write_byte(uint8_t data);
void i2c_write_addr(uint8_t addr, I2C_Dir_T mode);


#endif /* I2C_H_ */

HIH6030.C

#include "main.h"

uint8_t buffer[2];

static void _hih_request_measurement(void)
{
       i2c();
       _delay_ms(50);
}
void hih_read_temp(void)
{
       uint16_t HumidBuffer[2];
       uint16_t TemperatureBuffer[2];
       uint8_t lsbHumid, msbHumid, lsbTemp, msbTemp;
       long temperature, humidity;
       long conv_temp, conv_humid;

       /* request measurement */
       _hih_request_measurement();

       i2c();

       /* humidity conversion */
       msbHumid = (HumidBuffer[0] & 0x3F);
       lsbHumid = HumidBuffer[1];
       humidity = (msbHumid << 8) | lsbHumid;
       conv_humid =  (humidity * 100) >> 14;

       /* temperature conversion */
       msbTemp = TemperatureBuffer[0];
       lsbTemp = TemperatureBuffer[1];
       temperature = ( (msbTemp << 6) | (lsbTemp >> 2) );
       conv_temp = ( (temperature * 165) >> 14 ) - 40;
}

HIH6030.H

#ifndef BMP180_H_
#define BMP180_H_

#define HIH6030_ADDR                            (0x27)

void hih_read_temp(void);

#endif /* HIH6030_H_ */

Schemat realizowanego układu:

Z góry dziękuję za pomoc

[ Dodano: 10-02-2016, 17:20 ]

Na AVR powyższy układ działał bez problemu, więc błędy w schemacie i połączeniach odrzucam. Gdzieś w programie sie walnąłem i nie mogę znależć gdzie.

[ Dodano: 10-02-2016, 17:33 ]

Rzuciłem okiem na ERRATE i używam jednocześnie SPI1 i I2C1..

Conditions

• I2C1 and SPI1 are clocked.

• SPI1 is remapped.

• I/O port pin PB5 is configured as an alternate function output.

Description

Conflict between the SPI1 MOSI signal and the I2C1 SMBA signal (even if SMBA is not used).

Workaround

Do not use SPI1 remapped in master mode and I2C1 together.

When using SPI1 remapped, the I2C1 clock must be disabled.

[ Dodano: 10-02-2016, 17:35 ]

Ale możliwe, że nie tylko o to chodzi więc proszę o przejrzenie kodu, gdyż dopiero jutro wieczorem będe mógł to sprawdzić

Ciężko jest czytając sam program "zgadnąć" gdzie jest błąd. Tak na oko widzę jedną ciekawostkę - nigdzie nie ma wywołania funkcji i2c_write_addr. Wiem że adres wysyłasz jako dane, ale nie wiem jak na to maszyna stanowa i2c reaguje.

[ Dodano: 10-02-2016, 17:46 ]

Nie wiem jak dokładnie działa czujnik, który wykorzystujesz, ale wygląda że jest podobny do LSM303D - więc może wykorzystaj program z kolejnej części kursu: https://forbot.pl/blog/artykuly/programowanie/kurs-stm32-12-i2c-w-praktyce-akcelerometr-id10644

Po zmianie adresów powinno działać.

Wrzuce jutro i2c_write_addr, zamiast wysyłać w funkcji i2c_write oraz wyłącze na chwile SPI1 i inne peryferia. Zobacze po przedebugowaniu czy dalej sie zawiesza czy w końcu ruszy 🙂

[ Dodano: 10-02-2016, 17:49 ]

jak pisałem wcześniej na kilku AVR'ach czujnik działał. Zmienione zostały tylko funkcje niższego poziomu obsługujące I2C

Skoro wiesz najlepiej to po co pytasz?

W programie na oko widać sporo błędów - dlatego proponowałem zacząć od czegoś co jest sprawdzone. Może i ten program działał na AVR, ale lepiej zacząć od czegoś co działa na STM32.

Na pewno masz niepoprawne kończenie komunikacji po odebraniu danych. Flaga Stop i wyłączenie potwierdzenia powinno być ustawione przed odebraniem ostatniego bajtu.

Jest to błąd, ale możliwe że będzie z nim działał - jednak jeśli podłączysz analizator i2c zobaczysz, że transmisja nie jest w pełni poprawna.

  • 3 miesiące później...

Cześć

Próbuję skomunikować się z układem DS3231. Poniżej zamieszczam model komunikacji z noty układu.

Posiadam zestaw NUCLEO F103RB, pracuje na ac6 system workbench, najnowsza wersja, najnowsze firmware w zestawie.

Oto mój kod:

#include "stm32f10x.h"

int main(void)
{
GPIO_InitTypeDef gpio;
I2C_InitTypeDef i2c;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);

GPIO_StructInit(&gpio);
gpio.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; // SCL, SDA
gpio.GPIO_Mode = GPIO_Mode_AF_OD;
gpio.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &gpio);

I2C_StructInit(&i2c);
i2c.I2C_Mode = I2C_Mode_I2C;
i2c.I2C_Ack = I2C_Ack_Enable;
i2c.I2C_ClockSpeed = 100000;
I2C_Init(I2C1, &i2c);
I2C_Cmd(I2C1, ENABLE);

while (1) {
	I2C_GenerateSTART(I2C1, ENABLE);
	while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);

	I2C_AcknowledgeConfig(I2C1, ENABLE);
	I2C_Send7bitAddress(I2C1, 0xd0, I2C_Direction_Receiver);
	while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) != SUCCESS);

	while(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED) != SUCCESS);
	uint8_t sekundy = I2C_ReceiveData(I2C1);

	I2C_AcknowledgeConfig(I2C1, DISABLE);
	I2C_GenerateSTOP(I2C1, ENABLE);
}
}

Moim celem jest odczytanie pierwszego adresu 0x00 gdzie przechowywane są sekundy i zakończenie transmisji.

W trybie debug program zawiesza się w pętli

while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);

ale czasami odczyta poprawnie pierwszą wartość i dalej wisi na powyższej pętli, bywa też, że zawiesza się na kolejnych pętlach.

Debugger zatrzymuje się w tej funkcji wewnątrz stm32f10x_i2c.c

ErrorStatus I2C_CheckEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
{
 uint32_t lastevent = 0;
 uint32_t flag1 = 0, flag2 = 0;
 ErrorStatus status = ERROR;

 /* Check the parameters */
 assert_param(IS_I2C_ALL_PERIPH(I2Cx));
 assert_param(IS_I2C_EVENT(I2C_EVENT));

 /* Read the I2Cx status register */
 flag1 = I2Cx->SR1;
 flag2 = I2Cx->SR2;
 flag2 = flag2 << 16;

 /* Get the last event value from I2C status register */
 lastevent = (flag1 | flag2) & FLAG_Mask;

 /* Check whether the last event contains the I2C_EVENT */
 if ((lastevent & I2C_EVENT) == I2C_EVENT)
 {
   /* SUCCESS: last event is equal to I2C_EVENT */
   status = SUCCESS;
 }
 else
 {
   /* ERROR: last event is different from I2C_EVENT */
   status = ERROR;
 }
 /* Return status */
 return status;
}

Sprawdzałem to na 2 układach DS3231, one są na pewno sprawne, na ATMega działają prawidłowo. Ja coś robię źle, czy jest problem w bibliotekach od producenta?

natrov, w komentarzach do artykułów rozmawiamy wyłącznie o konfiguracji sprzętowej omawianej w kursie. Inaczej wprowadzi to duże zamieszanie 🙂 Jeśli masz problem z inną płytką, to załóż osobny temat w odpowiednim dziale.

  • 11 miesiące później...

Po przerobieniu lekcji z dołączoną pamięcią postanowiłem spróbować z AT34C02C. Odczyt działa dla pierwszych 128 pozycji. Dla adresów powyżej 0x80 dane które wyświetlane są w debuggerze nie zgadzają się z wartościami zapisanymi w pamięci. Czy adresowanie rekordów 128-255 powinno wyglądać inaczej?

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...