Skocz do zawartości
Komentator

Kurs STM32 F1 HAL - #9 - SPI w praktyce, ekspander IO

Pomocna odpowiedź

html_mig_img
Poznaliśmy już jeden interfejs szeregowy, który był asynchroniczny. Oczywiście chodzi o UART. Teraz dla odmiany pora na bardzo popularny, interfejs synchroniczny, którym jest  SPI.W tej części kursu STM32 wykorzystamy go do podłączenia ekspandera portów.

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.

Udostępnij ten post


Link to post
Share on other sites

Podobieństwo do UART bywa mylące.

MISO powinno zostać skonfigurowane na pinie PA6, natomiast pin PA3 jest podpięty pod RX z UART.

Ten sam błąd pojawia się w następnej części części kursu.

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites

ArczerMX, gratuluję czujności i dziękuję za zgłoszenie. Właśnie poprawiłem oba teksty 🙂

Udostępnij ten post


Link to post
Share on other sites

Ja mam pytanie z kolei o "Identyfikator urządzenia" po co go w ogóle wysyłać do ekspandera? Czy to jest po prostu ułatwienie dla czytelności kodu? Nigdzie nie zauważyłem, aby było to wyjaśnione, a sprawa ciekawa 🙂 W dokumentacji scalaka też cisza jeśli chodzi o wartości 0x40 i 0x41.

Udostępnij ten post


Link to post
Share on other sites

@beszt, witam na forum 😉 Widzę, że to Twoje pierwsze kroki na Forbocie, oto najważniejsze informacje na start:

  • Chcesz przywitać się z innymi członkami naszej społeczności? Skorzystaj z tematu powitania użytkowników.
  • Opis najciekawszych funkcji, które ułatwiają korzystanie z forum znajdziesz w temacie instrukcja korzystania z forum - co warto wiedzieć?
  • Poszczególne posty możesz oceniać (pozytywnie i negatywnie) za pomocą reakcji - ikona serca w prawym dolnym rogu każdej wiadomości.

16 godzin temu, beszt napisał:

Ja mam pytanie z kolei o "Identyfikator urządzenia" po co go w ogóle wysyłać do ekspandera? Czy to jest po prostu ułatwienie dla czytelności kodu? Nigdzie nie zauważyłem, aby było to wyjaśnione, a sprawa ciekawa 🙂 W dokumentacji scalaka też cisza jeśli chodzi o wartości 0x40 i 0x41.

Adres/identyfikator urządzenia ustawia się za pomocą wejść opisany jako A0 oraz A1. Adres zależy również od tego czy zapisujemy, czy odczytujemy dane - jest to opisane w dokumentacji:

ac58b-2019-01-18_13-00-31-5545.png

Jest tam podana informacja na temat sposobu '"obliczenia" identyfikatora:

  • dla odczytu mamy binarnie 01000001, co daje 0x41
  • dla zapisu mamy binarnie 010000000, co daje 0,40

Gdzie pogrubione 00 to wartości ustawione za pomocą pinów A0 oraz A1 🙂

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites

@Treker Serdecznie dziękuję za odpowiedź! Teraz wszystko jasne - po za CS, musi iść bajt kontrolny do samego układu. Dziękuję za wyjaśnienie i pokazanie w instrukcji - jak to często u mnie bywa, zbyt szybko ją przewertowałem 🙂

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites

Cześć!

Zrobiłem zadanie 9.3 Oto mój kod (działa na 4 diodach, bo ciasno mi było montować wszystkie... ;p):

/**
  ******************************************************************************
  * @file    main.c
  * @author  Ac6
  * @version V1.0
  * @date    01-December-2013
  * @brief   Default main function.
  ******************************************************************************
*/


#include "stm32f1xx.h"

#define MCP_IODIR		0x00
#define MCP_IPOL		0x01
#define MCP_GPINTEN		0x02
#define MCP_DEFVAL		0x03
#define MCP_INTCON		0x04
#define MCP_IOCON		0x05
#define MCP_GPPU		0x06
#define MCP_INTF		0x07
#define MCP_INTCAP		0x08
#define MCP_GPIO		0x09
#define MCP_OLAT		0x0a

SPI_HandleTypeDef spi;

void mcp_write_reg(uint8_t addr, uint8_t value)
{
	uint8_t tx_buf[] = {0x40, addr, value};

	HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_RESET);
	HAL_SPI_Transmit(&spi, tx_buf, 3, HAL_MAX_DELAY);
	HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_SET);
}

uint8_t mcp_read_reg(uint8_t addr)
{
	uint8_t tx_buf[] = {0x41, addr, 0xff};
	uint8_t rx_buf[3];

	HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_RESET);
	HAL_SPI_TransmitReceive(&spi, tx_buf, rx_buf, 3, HAL_MAX_DELAY);
	HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_SET);

	return rx_buf[2];
}

int main(void)
{
	SystemCoreClock = 8000000;

	HAL_Init();

	__HAL_RCC_GPIOA_CLK_ENABLE();
	__HAL_RCC_GPIOC_CLK_ENABLE();
	__HAL_RCC_SPI1_CLK_ENABLE();

	/* SPI I/O config */

	/* SCK & MOSI */
	GPIO_InitTypeDef gpio;
	gpio.Mode = GPIO_MODE_AF_PP;
	gpio.Pin = GPIO_PIN_5 | GPIO_PIN_7;
	gpio.Pull = GPIO_NOPULL;
	gpio.Speed = GPIO_SPEED_FREQ_HIGH;
	HAL_GPIO_Init(GPIOA, &gpio);

	/* MISO */

	gpio.Mode = GPIO_MODE_AF_INPUT;
	gpio.Pin = GPIO_PIN_6;
	HAL_GPIO_Init(GPIOA, &gpio);

	/* CS */

	gpio.Mode = GPIO_MODE_OUTPUT_PP;
	gpio.Pin = GPIO_PIN_0;
	HAL_GPIO_Init(GPIOC, &gpio);

	HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_SET);	/* idle state of the conection with slave */

	spi.Instance = SPI1;
	spi.Init.Mode = SPI_MODE_MASTER;
	spi.Init.NSS = SPI_NSS_SOFT;
	spi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8; /* 1MHz */
	spi.Init.Direction = SPI_DIRECTION_2LINES;
	spi.Init.CLKPhase = SPI_PHASE_1EDGE;
	spi.Init.CLKPolarity = SPI_POLARITY_LOW;
	spi.Init.DataSize = SPI_DATASIZE_8BIT;
	spi.Init.FirstBit = SPI_FIRSTBIT_MSB;
	spi.Init.TIMode = SPI_TIMODE_DISABLE;
	spi.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
	spi.Init.CRCPolynomial = 7;
	HAL_SPI_Init(&spi);

	__HAL_SPI_ENABLE(&spi);

	mcp_write_reg(MCP_IODIR, ~0x0F);

	mcp_write_reg(MCP_GPPU, 0x10);

	while(1)
	{
		if ((mcp_read_reg(MCP_GPIO) & 0x10) == 0)
		{
			for (int i = 0x00; i<=0x0F; ++i)
			{
				mcp_write_reg(MCP_OLAT, i);
				HAL_Delay(1000);
			}
		}
		else
		{
			for (int i = 0x0F; i>=0x00; --i)
			{
				mcp_write_reg(MCP_OLAT, i);
				HAL_Delay(1000);
			}
		}
	}
}

Niby działa, ale nie jest to najwspanialsze rozwiązanie, gdyż odczyt stanu pinu ekspandera odbywa się po skończeniu pętli. Chciałbym użyć przerwań, aby wykrywać zmianę stanu tego pinu, lecz nie wiem jak się za to zabrać. :<

Czy moglibyście mi pomóc? Dać jakieś wskazówki?

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites
Dnia 1.02.2019 o 15:52, MaciejZyskowski napisał:

Chciałbym użyć przerwań, aby wykrywać zmianę stanu tego pinu, lecz nie wiem jak się za to zabrać. :<

Czy dobrze rozumiem, że chciałbyś wyzwalać przerwanie w mikrokontrolerze po zmianie stanu na pisanie ekspandera "bez" ciągłego odpytywania ekspandera o stan jego pinów?

Udostępnij ten post


Link to post
Share on other sites
Dnia 2.02.2019 o 17:29, Treker napisał:

Czy dobrze rozumiem, że chciałbyś wyzwalać przerwanie w mikrokontrolerze po zmianie stanu na pisanie ekspandera "bez" ciągłego odpytywania ekspandera o stan jego pinów?

Dokładnie to mam na myśli. Obecnie zmiana "kierunku" pracy licznika może obyć się dopiero po zakończeniu pętli. Chciałbym, żeby mogła nastąpić natychmiast po zmianie stanu pinu wejściowego.

Udostępnij ten post


Link to post
Share on other sites

@MaciejZyskowski przy takim standardowym podłączeniu nie będzie raczej takiej opcji, bo mikrokontroler nie ma pojęcia co dzieje się na pinach ekspandera (jeśli ich ciągle nie sprawdza). W Twoim przypadku rozwiązaniem może być chyba wykorzystaniu pinu INT, który należałoby osobno podłączyć do STM32. Fragment z noty katalogowej:

26105-2019-02-04_10-15-26-5710.png

Jeśli będziesz chciał zabrać się za ten temat to załóż proszę osobny temat na forum, aby nie "mieszać" tutaj w komentarzach do kursu 🙂

Udostępnij ten post


Link to post
Share on other sites
 if ((mcp_read_reg(MCP_GPIO) & 0x02) == 0)

Po co tutaj koniunkcja i jak ona dziala? chyba nie mozna w taki sposob odczytac wartosci z rejestru

Udostępnij ten post


Link to post
Share on other sites

@danielll witam na forum 🙂 Dlaczego uważasz, że taki zapis nie jest poprawny? Porównujemy zwyczajnie dwie wartości za pomocą operatora logicznego i sprawdzamy wynik. Co dokładnie wydaje Ci się tutaj nieodpowiednie? 

Udostępnij ten post


Link to post
Share on other sites
(edytowany)

Nie za bardzo rozumiem o co w tym chodzi

Gdy przewod laczacy PIN1 z masa jest zwarty to funkcja mcp_read_reg zwraca stan wysoki na tym pinie czyli wartosc 0x02 (00000010). Po co nastepnie ta koniunkcja. Sluzy moze do tego zeby wyzerowac najmlodszy bit gdy dioda jest zaswiecona  (bo wtedy tez jest na niej stan wysoki wiec zostanie odczytana wartosc 0x03) ? 

Edytowano przez danielll

Udostępnij ten post


Link to post
Share on other sites
1 godzinę temu, danielll napisał:

Gdy przewod laczacy PIN1 z masa jest zwarty to funkcja mcp_read_reg zwraca stan wysoki na tym pinie czyli wartosc 0x02 (00000010). Po co nastepnie ta koniunkcja

W wyniku działania funkcji otrzymujemy cały bajt, a nas interesuje tylko wartość z konkretnego bitu (drugi od prawej). Taki zapis pozwala uzyskać właśnie taki efekt. Załóżmy, że funkcja zwraca bajt 01011011, porównujemy go więc z bajtem 00000010. W wyniku działania tego operatora otrzymamy liczbę, w której jedynki będą jedynie na tych pozycjach, na których jedynka wystąpiła w obu porównywanych liczbach. W wyniku operacji: "010110X1 & 00000010" otrzymujemy więc: 000000X0, czyli wyciągamy z całego bajta tylko jeden, interesujący nas bit. Pozostały bity nie wpłyną na wynik i o to nam właśnie chodzi, bo w programie chcemy sprawdzić stan jednego I/O. Pozostałe nas w tym przypadku nieinteresującą. Czy teraz jest to trochę jaśniejsze?

To nie jest bezpośrednio związane z STMami, ani nawet tym przykładem, to ogólne zagadnienie z logiki. Warto potrenować korzystanie z różnych operatorów logicznych, ponieważ przy mikrokontekstach bardzo często się z tego korzysta 😉

  • Pomogłeś! 1

Udostępnij ten post


Link to post
Share on other sites
(edytowany)

Poza tym warto odróżnić koniunkcję (czyli operację na wartościach logicznych) od operacji na bitach.

Ot, taka niewielka różnica między && i &

Edytowano przez ethanak
  • Lubię! 1

Udostępnij ten post


Link to post
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!

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