Skocz do zawartości

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


Komentator

Pomocna odpowiedź

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
Link do komentarza
Share on other sites

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ść?

Link do komentarza
Share on other sites

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.

Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

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 🙂

Link do komentarza
Share on other sites

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ć

Link do komentarza
Share on other sites

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ć.

Link do komentarza
Share on other sites

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

Link do komentarza
Share on other sites

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.

Link do komentarza
Share on other sites

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?

Link do komentarza
Share on other sites

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.

Link do komentarza
Share on other sites

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?

Link do komentarza
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.