Ten wpis będzie krótki - właściwie nie planowałem zajmować się SPI, to w końcu taki prosty interfejs. Z drugiej strony okazało się, że jego obsługa konieczna jest do uruchomienia LCD. Więc tym razem krótko i prosto - komunikacja przez SPI.
Na początek małe sprzątanie kodu. Do przetestowania SPI wygodnie będzie wysłać otrzymane dane przez UART, więc utworzyłem odpowiedni moduł. Plik nagłówkowy uart.h:
Oraz sam kod uart.c:
Do komunikacji przez SPI oczywiście przydałby się jakiś moduł. Na płytce discovery znajdziemy trzyosiowy żyroskop L3GD20.
Jak widać jest podłączony do interfejsu SPI5 - tego samego co wyświetlacz.
Obsługa SPI jest bardzo prosta. Jak zwykle musimy najpierw uruchomić zegar modułu i skonfigurować odpowiednie piny - podobnie jak poprzednio użyję funkcji pin_config, chociaż przyznam się do lenistwa i jej skopiowania. Kiedyś trzeba będzie to poprawić:
RCC->APB2ENR |= RCC_APB2ENR_SPI5EN;
config_pin(GPIOF, 7, 5);
config_pin(GPIOF, 8, 5);
config_pin(GPIOF, 9, 5);
Linią wyboru układu CS będziemy sterować programowo, więc jest to zwykły pin GPIO:
GPIOC->MODER |= GPIO_MODER_MODE1_0;
GPIOC->BSRR = GPIO_BSRR_BS1;
Na koniec sama konfiguracja i uruchomienie modułu SPI:
SPI5->CR1 = SPI_CR1_BR_2 | SPI_CR1_BR_0 | SPI_CR1_MSTR |
SPI_CR1_SSM | SPI_CR1_SSI;
SPI5->CR1 |= SPI_CR1_SPE;
BR2 i BR0 ustawiają dzielnik zegara dla SPI, pozostałe piny uruchamiają interfejs w trybie master. Plik nagłówkowy spi.h:
Implementacja spi.c:
Teraz wystarczy napisać program główny i przetestować komunikację. Do testów najłatwiej będzie odczytać rejestr WHO_AM_I układu L3GD20:
Jak widać poprawna wartość to 0xd4. Przy okazji małe ułatwienie - dodałem przekierowanie printf() na uart. To trochę rozrzutność jak chodzi o kod, ale stm32f429 ma ogromną jak na mikrokontroler pamięć Flash, więc warto chociaż zapamiętać że taka opcja istnieje. Kod main.c:
Po uruchomieniu program wypisuje oczekiwaną wartość, czyli 0xd4 a analizator logiczny pokazuję piękny przebieg. Skoro SPI już działa, czas zająć się sterownikiem wyświetlacza.
Na początek małe sprzątanie kodu. Do przetestowania SPI wygodnie będzie wysłać otrzymane dane przez UART, więc utworzyłem odpowiedni moduł. Plik nagłówkowy uart.h:
#ifndef __UART__ #define __UART__ #include <stdint.h> void uart_init(void); void usart_putc(char c); void usart_print_hex(uint8_t value); void usart_send(const char *s); #endif // __UART__
Oraz sam kod uart.c:
#include "uart.h" #include "pll.h" #include "stm32f429xx.h" void uart_init(void) { GPIOA->MODER |= GPIO_MODER_MODE9_1|GPIO_MODER_MODE10_1; GPIOA->AFR[1] |= GPIO_AFRH_AFSEL9_0 | GPIO_AFRH_AFSEL9_1 | GPIO_AFRH_AFSEL9_2; GPIOA->AFR[1] |= GPIO_AFRH_AFSEL10_0 | GPIO_AFRH_AFSEL10_1 | GPIO_AFRH_AFSEL10_2; RCC->APB2ENR |= RCC_APB2ENR_USART1EN; USART1->BRR = APB2_FREQ / 115200; USART1->CR3 = 0; USART1->CR2 = 0; USART1->CR1 = USART_CR1_TE | USART_CR1_RE; for (volatile uint32_t dly = 0; dly < 1000; dly++); USART1->CR1 |= USART_CR1_UE; } void usart_putc(char c) { while ((USART1->SR & USART_SR_TXE) == 0); USART1->DR = (int)c; } void usart_print_hex(uint8_t value) { const char hex2char[] = "0123456789abcdef"; usart_putc(hex2char[value >> 4]); usart_putc(hex2char[value & 0x0f]); } void usart_send(const char *s) { while (*s) usart_putc(*s++); }
Do komunikacji przez SPI oczywiście przydałby się jakiś moduł. Na płytce discovery znajdziemy trzyosiowy żyroskop L3GD20.
Jak widać jest podłączony do interfejsu SPI5 - tego samego co wyświetlacz.
Obsługa SPI jest bardzo prosta. Jak zwykle musimy najpierw uruchomić zegar modułu i skonfigurować odpowiednie piny - podobnie jak poprzednio użyję funkcji pin_config, chociaż przyznam się do lenistwa i jej skopiowania. Kiedyś trzeba będzie to poprawić:
RCC->APB2ENR |= RCC_APB2ENR_SPI5EN;
config_pin(GPIOF, 7, 5);
config_pin(GPIOF, 8, 5);
config_pin(GPIOF, 9, 5);
Linią wyboru układu CS będziemy sterować programowo, więc jest to zwykły pin GPIO:
GPIOC->MODER |= GPIO_MODER_MODE1_0;
GPIOC->BSRR = GPIO_BSRR_BS1;
Na koniec sama konfiguracja i uruchomienie modułu SPI:
SPI5->CR1 = SPI_CR1_BR_2 | SPI_CR1_BR_0 | SPI_CR1_MSTR |
SPI_CR1_SSM | SPI_CR1_SSI;
SPI5->CR1 |= SPI_CR1_SPE;
BR2 i BR0 ustawiają dzielnik zegara dla SPI, pozostałe piny uruchamiają interfejs w trybie master. Plik nagłówkowy spi.h:
#ifndef __SPI__ #define __SPI__ #include <stdint.h> void spi_init(void); void spi_start(void); void spi_stop(void); uint8_t spi_sendrecv(uint8_t tx); #endif // __SPI__
Implementacja spi.c:
#include "spi.h" #include "stm32f429xx.h" static void config_pin(GPIO_TypeDef* port, uint8_t pin, uint32_t func) { if (pin < 8) { port->AFR[0] |= func << (pin * 4); port->AFR[0] |= func << (pin * 4); } else { port->AFR[1] |= func << ((pin - 8) * 4); port->AFR[1] |= func << ((pin - 8) * 4); } port->MODER |= 2 << (pin * 2); port->OSPEEDR |= 2 << (pin * 2); } void spi_init(void) { RCC->APB2ENR |= RCC_APB2ENR_SPI5EN; config_pin(GPIOF, 7, 5); config_pin(GPIOF, 8, 5); config_pin(GPIOF, 9, 5); GPIOC->MODER |= GPIO_MODER_MODE1_0; GPIOC->BSRR = GPIO_BSRR_BS1; SPI5->CR1 = SPI_CR1_BR_2 | SPI_CR1_BR_0 | SPI_CR1_MSTR | SPI_CR1_SSM | SPI_CR1_SSI; SPI5->CR1 |= SPI_CR1_SPE; } uint8_t spi_sendrecv(uint8_t tx) { while ((SPI5->SR & SPI_SR_TXE) == 0); SPI5->DR = tx; while ((SPI5->SR & SPI_SR_RXNE) == 0); return SPI5->DR; } void spi_start(void) { GPIOC->BSRR = GPIO_BSRR_BR1; } void spi_stop(void) { while ((SPI5->SR & SPI_SR_TXE) == 0); while (SPI5->SR & SPI_SR_BSY); GPIOC->BSRR = GPIO_BSRR_BS1; }
Teraz wystarczy napisać program główny i przetestować komunikację. Do testów najłatwiej będzie odczytać rejestr WHO_AM_I układu L3GD20:
Jak widać poprawna wartość to 0xd4. Przy okazji małe ułatwienie - dodałem przekierowanie printf() na uart. To trochę rozrzutność jak chodzi o kod, ale stm32f429 ma ogromną jak na mikrokontroler pamięć Flash, więc warto chociaż zapamiętać że taka opcja istnieje. Kod main.c:
#include <stdio.h> #include <stdint.h> #include <stdbool.h> #include "stm32f429xx.h" #include "pll.h" #include "uart.h" #include "spi.h" int _write(int fd, char *str, int len) { int i; for (i = 0; i < len; i++) usart_putc(str[i]); return len; } int main(int argc, char *argv[]) { RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOCEN | RCC_AHB1ENR_GPIOFEN | RCC_AHB1ENR_GPIOGEN; pll_init(); uart_init(); spi_init(); printf("Hello world!\r\n"); spi_init(); while (1) { printf("Starting SPI communication...\r\n"); spi_start(); uint8_t r = spi_sendrecv(0x8f); printf("Received: %02x\r\n", r); r = spi_sendrecv(0x00); printf("Received: %02x\r\n", r); spi_stop(); printf("done.\r\n"); for (volatile uint32_t dly = 0; dly < 10000000; dly++); } return 0; }
Po uruchomieniu program wypisuje oczekiwaną wartość, czyli 0xd4 a analizator logiczny pokazuję piękny przebieg. Skoro SPI już działa, czas zająć się sterownikiem wyświetlacza.
Brak komentarzy:
Prześlij komentarz