Wstęp
Ekspander portów wejścia/wyjścia pozwala na zwiększenie liczby dostępnych pinów. Komunikacja przebiega poprzez interfejs SPI. W przypadku zakupy układu MCP23008 zamiast interfejsu SPI będzie dostępny I2C.
Rys. 1. Ekspander [microchip.com]
Główne parametry układu to:
- Zasilanie: 1,8 - 5,5 V
- Prędkość transmisji do 10kbps
- Ilość kanałów 8
- Taktowanie do 10MHz
- Komunikacja SPI
- Niski pobór prądu w trybie oczekiwania 1uA
Schemat wyprowadzeń poszczególnych pinów z ekspandera. Został on zaczerpnięty z dokumentacji układu:
Rys. 2. Wyprowadzenia układu
Poniżej przedstawiam opis poszczególnych wyprowadzeń:
- SCK - Zegar taktujący
- SI - Wejście danych
- SO - Wyjście danych
- A1 - Sprzętowe wejście danych adresowych, musi być podciągnięty do zasilania
- A0 - Sprzętowe wejście danych, musi być podciągnięty do zasilania
- RESET - Reset, musi być podciągnięty do zasilania
- CS - Wybranie układu
- INT - Wyjście przerwań. Można skonfigurować jako aktywny wysoki, aktywny niski oraz wyjście typu otwarty dren
- VSS - Masa
- VDD - Zasilanie
- GP0 - GP7 - Wyprowadzenia sygnałów z ekspandera
Kolejnym ważnym elementem po sprawdzeniu wyprowadzeń jest zapoznanie się z rejestrami ekspandera. Można je znaleźć w dokumentacji układu:
Rys. 3. Rejestry układu
Zestawy rejestrów są takie same zarówno dla układu z SPI jak i dla wersji z I2C.
Poszczególne rejestry pełnią następujące funkcje:
- IODIR - pozwala na wybranie kierunku działania linii
- IPOL - ustawienie biegunowości
- GPINTEN - konfiguracja przerwania dla poszczególnych pinów
- DEFVAL - porównywanie wartości rejestrów z przerwania
- INTCON - konfiguracja przerwania
- IOCON - konfiguracja urządzenia
- GPPU - daje możliwość podłączenia dodatkowych rezystorów podciągających
- INTF - odbicie parametrów przerwań ustawionych za pomocą rejestry GPINTEN
- INTCAP - pozwala na przechwycenie wartości z rejestru kiedy nastąpi przerwanie
- GPIO - pozwala na odczytanie stanu jaki znajduje się na linii
- OLAT - ustawienie stanu linii
Kolejnym elementem są parametry rejestrów, czyli w jaki sposób należy je skonfigurować. Schemat wygląda następująco:
Rys. 4. Konfiguracja rejestrów
Mikrokontroler zastosowany na płytce posiada następujące wyprowadzenia interfejsu SPI:
- SPI1:
- NSS(CS) - PA4
- MISO - PA6
- MOSI - PA7
- SCK - PA5
- SPI2:
- NSS(CS) - PB12
- MISO - PB14
- MOSI - PB15
- SCK - PB13
Programowanie
Inicjalizacja
Pierwszym elementem jaki należy wykonać jest odpowiednie ustawienie pinów wejścia, oraz wyjścia.
Następnie należy ustawić odpowiednio interfejs SPI oraz włączyć odpowiednie zegary. Nie zaszkodzi także zdefiniować sobie podstawowych wartości rejestrów oraz pinów za pomocą #define.
void GPIOInit(void) { GPIO_InitTypeDef GPIOInit; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); GPIO_StructInit(&GPIOInit); //piny SCK, MOSI GPIOInit.GPIO_Pin = SCK_PIN|MOSI_PIN; // SCK, MOSI GPIOInit.GPIO_Mode = GPIO_Mode_AF_PP; GPIOInit.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(SCK_LINE, &GPIOInit); //Pin Miso GPIOInit.GPIO_Pin = MISO_PIN; // MISO GPIOInit.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIOInit.GPIO_Speed = GPIO_Speed_10MHz; GPIO_Init(MISO_LINE, &GPIOInit); //Pin CS GPIOInit.GPIO_Pin = CS_PIN; GPIOInit.GPIO_Mode = GPIO_Mode_Out_PP; GPIOInit.GPIO_Speed = GPIO_Speed_10MHz; GPIO_Init(CS_LINE, &GPIOInit); //Ustawienie pinu CS na niski, następuje wybranie urządzenia GPIO_SetBits(GPIOC, GPIO_Pin_0); } //Inicjalizacja spi void SPIInit(void) { SPI_InitTypeDef SPIInit; RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); SPI_StructInit(&SPIInit); //Transmisja z wykorzystaniem dwóch linii SPIInit.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //Tryb pracy mikrokontrolera SPIInit.SPI_Mode = SPI_Mode_Master; //Stan sygnału taktującego przy braku transmisji SPIInit.SPI_CPOL = SPI_CPOL_Low; //Aktywne zbocze sygnału taktującego SPIInit.SPI_CPHA = SPI_CPHA_1Edge; //Wylaczenie sprzetowej obslugi linii CS SPIInit.SPI_NSS = SPI_NSS_Soft; //Szybkośc transmisji 64MHz/16 = 4MHz SPIInit.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16; //Inicjalizacja SPI SPI_Init(SPI1, &SPIInit); //Włączenie SPI SPI_Cmd(SPI1, ENABLE); }
Wysyłanie danych
SPI jest interfejsem szeregowym, dwukierunkowym. Oznacza to, że nie można tylko odbierać z niego danych. Należy także jakieś dane do niego wysyłać.
Rozpoczęcie komunikacji rozpoczyna się poprzez podanie stanu niskiego na pin CS. Następnie należy podać komendę pozwalającą na wprowadzanie danych. Adresowanie wygląda następująco:
Rys. 5. Rejestr adresów
Dla bitu R/W wartość 0 oznacza wpisywanie danych do urządzenia (Write), natomiast 1 odczytuje dane z urządzenia (Read).
Aby rozpocząć procedurę komunikacji należy przesłać identyfikator urządzenia, który wygląda następująco: 1000000 lub w postaci szesnastkowej 0x40.
Następnym elementem jest przesłanie informacji do jakiego rejestru dane będą wprowadzane, po czym następuje przesłanie jego ustawień.
Część programu odpowiedzialna za przesyłanie danych wygląda następująco:
//Wysłanie danych uint8_t SPI_SEND(uint8_t byte) { //Sprawdzanie zajętości bufora nadawczego, jak będzie wolny nastąpi wysłanie danych while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); SPI_I2S_SendData(SPI1, byte); //Sprawdzanie, czy dane są w buforze odbiorczym, jeśli tak to zostaną zwrócone while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET); return SPI_I2S_ReceiveData(SPI1); } //Wpisanie danych do rejestrów void MCP_WRITE(uint8_t rejestr, uint8_t wartosc) { //Ustawienie pinu CS na stan niski, wybranie urządzenia GPIO_ResetBits(CS_LINE, CS_PIN); //wpisanie danych do CS. SPI_SEND(0x40); //wyslanie informacji do którego rejestru dane zostaną przesłane SPI_SEND(rejestr); //wysłanie ustawienia wartości SPI_SEND(wartosc); //Zakończenie transmisji, zmiana stanu linii CS na wysoki GPIO_SetBits(CS_LINE, CS_PIN); }
Program 1
W tym programie zaprezentuję obsługę pięciu portów GPIO ekspandera. Do jego wyjść podłączyłem diody świecące, które będą się zapalały oraz gasły w zależności od stanów na portach.
#include "stm32f10x.h" //Deklaracje rejestrów #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 //Deklaracja pinów #define CS_PIN GPIO_Pin_0 #define CS_LINE GPIOC #define SCK_PIN GPIO_Pin_5 #define SCK_LINE GPIOA #define MISO_PIN GPIO_Pin_6 #define MISO_LINE GPIOA #define MOSI_PIN GPIO_Pin_7 #define MOSI_LINE GPIOA #define GP0 0x01 #define GP1 0x02 #define GP2 0x04 #define GP3 0x08 #define GP4 0x10 #define GP5 0x20 #define GP6 0x40 #define GP7 0x80 #define Low 0x00 void SPIInit(void); void GPIOInit(void); uint8_t SPI_SEND(uint8_t byte); void MCP_WRITE(uint8_t, uint8_t); void Delay(int); void GP_ACTIVE(int); void GP_ACTIVE_GROUP(int PinEnd); int main(void) { GPIOInit(); SPIInit(); GP_ACTIVE_GROUP(4); while (1) { //Ustawienie rejestru dla diody podłączonej do GP0 //Zapalenie diody MCP_WRITE(MCP_OLAT, GP0); Delay(500); //Zgaszeniediody MCP_WRITE(MCP_OLAT, Low); Delay(500); //Ustawienie rejestru dla diody podłączonej do GP1 MCP_WRITE(MCP_OLAT, GP1); Delay(500); MCP_WRITE(MCP_OLAT, Low); Delay(500); //Ustawienie rejestru dla diody podłączonej do GP2 MCP_WRITE(MCP_OLAT, GP2); Delay(500); MCP_WRITE(MCP_OLAT, Low); Delay(500); //Ustawienie rejestru dla diody podłączonej do GP3 MCP_WRITE(MCP_OLAT, GP3); Delay(500); MCP_WRITE(MCP_OLAT, Low); Delay(500); //Ustawienie rejestru dla diody podłączonej do GP4 MCP_WRITE(MCP_OLAT, GP4); Delay(500); MCP_WRITE(MCP_OLAT, Low); Delay(500); } } void GPIOInit(void) { GPIO_InitTypeDef GPIOInit; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); GPIO_StructInit(&GPIOInit); //piny SCK, MOSI GPIOInit.GPIO_Pin = SCK_PIN|MOSI_PIN; // SCK, MOSI GPIOInit.GPIO_Mode = GPIO_Mode_AF_PP; GPIOInit.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(SCK_LINE, &GPIOInit); //Pin Miso GPIOInit.GPIO_Pin = MISO_PIN; // MISO GPIOInit.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIOInit.GPIO_Speed = GPIO_Speed_10MHz; GPIO_Init(MISO_LINE, &GPIOInit); //Pin CS GPIOInit.GPIO_Pin = CS_PIN; GPIOInit.GPIO_Mode = GPIO_Mode_Out_PP; GPIOInit.GPIO_Speed = GPIO_Speed_10MHz; GPIO_Init(CS_LINE, &GPIOInit); //Ustawienie pinu CS na niski, następuje wybranie urządzenia GPIO_SetBits(GPIOC, GPIO_Pin_0); } //Inicjalizacja spi void SPIInit(void) { SPI_InitTypeDef SPIInit; RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); SPI_StructInit(&SPIInit); //Transmisja z wykorzystaniem dwóch linii SPIInit.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //Tryb pracy mikrokontrolera SPIInit.SPI_Mode = SPI_Mode_Master; //Stan sygnału taktującego przy braku transmisji SPIInit.SPI_CPOL = SPI_CPOL_Low; //Aktywne zbocze sygnału taktującego SPIInit.SPI_CPHA = SPI_CPHA_1Edge; //Wylaczenie sprzetowej obslugi linii CS SPIInit.SPI_NSS = SPI_NSS_Soft; //Szybkośc transmisji 64MHz/16 = 4MHz SPIInit.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16; //Inicjalizacja SPI SPI_Init(SPI1, &SPIInit); //Włączenie SPI SPI_Cmd(SPI1, ENABLE); } //Wysłanie danych uint8_t SPI_SEND(uint8_t byte) { //Sprawdzanie zajętości bufora nadawczego, jak będzie wolny nastąpi wysłanie danych while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); SPI_I2S_SendData(SPI1, byte); //Sprawdzanie, czy dane są w buforze odbiorczym, jeśli tak to zostaną zwrócone while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET); return SPI_I2S_ReceiveData(SPI1); } //Wpisanie danych do rejestrów void MCP_WRITE(uint8_t rejestr, uint8_t wartosc) { //Ustawienie pinu CS na stan niski, wybranie urządzenia GPIO_ResetBits(CS_LINE, CS_PIN); //wpisanie danych do CS. SPI_SEND(0x40); //wyslanie informacji do którego rejestru dane zostaną przesłane SPI_SEND(rejestr); //wysłanie ustawienia wartości SPI_SEND(wartosc); //Zakończenie transmisji, zmiana stanu linii CS na wysoki GPIO_SetBits(CS_LINE, CS_PIN); } //Funckcja do ustawiania wybranego portu void GP_ACTIVE(int Pin) { //Ustawienie pinu jako wejście przebiega poprzez wyzerowanie //odpowiedniego zestawu bitow switch(Pin) { case 0: MCP_WRITE(MCP_IODIR, ~0x01); break; //GP0 bit0 Dec: 1 case 1: MCP_WRITE(MCP_IODIR, ~0x02); break; //GP1 bit1 Dec: 2 case 2: MCP_WRITE(MCP_IODIR, ~0x04); break; //GP2 bit2 Dec: 4 case 3: MCP_WRITE(MCP_IODIR, ~0x08); break; //GP3 bit3 Dec: 8 case 4: MCP_WRITE(MCP_IODIR, ~0x10); break; //GP4 bit4 Dec: 16 case 5: MCP_WRITE(MCP_IODIR, ~0x20); break; //GP5 bit5 Dec: 32 case 6: MCP_WRITE(MCP_IODIR, ~0x40); break; //GP6 bit6 Dec: 64 case 7: MCP_WRITE(MCP_IODIR, ~0x80); break; //GP7 bit7 Dec: 128 default: MCP_WRITE(MCP_IODIR, ~0x01); break; } } void GP_ACTIVE_GROUP(int PinEnd) { //Ta funkcja ustawia odpowiednie wartości dla pinu początkowego oraz końcowego //Ustawienie pinu jako wejście przebiega poprzez wyzerowanie //odpowiedniego zestawu bitow switch(PinEnd) { case 1: MCP_WRITE(MCP_IODIR, ~0x03); break; //GP0 bit0 Dec: 3 case 2: MCP_WRITE(MCP_IODIR, ~0x07); break; //GP1 bit1 Dec: 7 case 3: MCP_WRITE(MCP_IODIR, ~0x0F); break; //GP2 bit2 Dec: 15 case 4: MCP_WRITE(MCP_IODIR, ~0x1F); break; //GP3 bit3 Dec: 31 case 5: MCP_WRITE(MCP_IODIR, ~0x3F); break; //GP4 bit4 Dec: 63 case 6: MCP_WRITE(MCP_IODIR, ~0x7F); break; //GP5 bit5 Dec: 127 case 7: MCP_WRITE(MCP_IODIR, ~0xFF); break; //GP6 bit6 Dec: 255 default: MCP_WRITE(MCP_IODIR, ~0xFF); break; } } //Pętla opóźniająca void Delay(int czas) { int i; for (i = 0; i < czas * 5000; i++) {} }
Program 2
Program poniżej działa prawie tak samo jak poprzedni, jedyną różnicą jest sterowanie portami za pomocą interfejsu UART. Wpisanie odpowiedniej komendy spowoduje zapalenie, bądź zgaszenie wybranej diody. Wszystkie informacje odnoście działania programu umieściłem bezpośrednio w nim.
#include "stm32f10x.h" //Deklaracje rejestrów #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 //Deklaracja pinów #define CS_PIN GPIO_Pin_0 #define CS_LINE GPIOC #define SCK_PIN GPIO_Pin_5 #define SCK_LINE GPIOA #define MISO_PIN GPIO_Pin_6 #define MISO_LINE GPIOA #define MOSI_PIN GPIO_Pin_7 #define MOSI_LINE GPIOA #define GP0 0x01 #define GP1 0x02 #define GP2 0x04 #define GP3 0x08 #define GP4 0x10 #define GP5 0x20 #define GP6 0x40 #define GP7 0x80 #define Low 0x00 void SPIInit(void); void GPIOInit(void); uint8_t SPI_SEND(uint8_t byte); void MCP_WRITE(uint8_t, uint8_t); void Delay(int); void GP_ACTIVE(int); void GP_ACTIVE_GROUP(int PinEnd); void GPIOInit_USART(void); void USARTInit(void); void SendCharc(volatile char); void USARTSend(volatile char *c); uint8_t USART_Getc(USART_TypeDef* USARTx); int8_t USART_Buffer[8][32]; uint16_t usart_buf_in[8] = {0, 0, 0, 0, 0, 0, 0, 0}; uint16_t usart_buf_out[8] = {0, 0, 0, 0, 0, 0, 0, 0}; uint16_t usart_buf_num[8] = {0, 0, 0, 0, 0, 0, 0, 0}; int main(void) { uint16_t c; GPIOInit(); GPIOInit_USART(); USARTInit(); SPIInit(); GP_ACTIVE_GROUP(4); USARTSend("Aby zapalic bądz zgasic wybrane diody nalezy:\n"); USARTSend("GP0 - Zapal 1, Zgas 2\n"); USARTSend("GP1 - Zapal 3, Zgas 4\n"); USARTSend("GP2 - Zapal 5, Zgas 6\n"); USARTSend("GP3 - Zapal 7, Zgas 8\n"); USARTSend("GP4 - Zapal 9, Zgas 0\n"); while (1) { c = USART_Getc(USART2); if(c) { switch (c) { case '1': USARTSend("GP0 - Odebrano 1. Dioda zapalona!\n"); MCP_WRITE(MCP_OLAT, GP0); break; case '2': USARTSend("GP0 - Odebrano 2. Dioda zgaszona!\n"); MCP_WRITE(MCP_OLAT, (1<<0)); break; case '3': USARTSend("GP1 - Odebrano 3. Dioda zapalona!\n"); MCP_WRITE(MCP_OLAT, GP1); break; case '4': USARTSend("GP1 - Odebrano 4. Dioda zgaszona!\n"); MCP_WRITE(MCP_OLAT, Low); break; case '5': USARTSend("GP2 - Odebrano 5. Dioda zapalona!\n"); MCP_WRITE(MCP_OLAT, GP2); break; case '6': USARTSend("GP2 - Odebrano 6. Dioda zgaszona!\n"); MCP_WRITE(MCP_OLAT, Low); break; case '7': USARTSend("GP3 - Odebrano 7. Dioda zapalona!\n"); MCP_WRITE(MCP_OLAT, GP3); break; case '8': USARTSend("GP3 - Odebrano 8. Dioda zgaszona!\n"); MCP_WRITE(MCP_OLAT, Low); break; case '9': USARTSend("GP4 - Odebrano 9. Dioda zapalona!\n"); MCP_WRITE(MCP_OLAT, GP4); break; case '0': USARTSend("GP4 - Odebrano 0. Dioda zgaszona!\n"); MCP_WRITE(MCP_OLAT, Low); break; case 'C': USARTSend("Gasze wszystkie diody\n"); MCP_WRITE(MCP_OLAT, Low); break; default: USARTSend("Wybierz poprawna wartosc \n"); SendCharc(c); break; } } } } //Ustawienie pinów SPI void GPIOInit(void) { GPIO_InitTypeDef GPIOInit; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); GPIO_StructInit(&GPIOInit); //piny SCK, MOSI GPIOInit.GPIO_Pin = SCK_PIN|MOSI_PIN; // SCK, MOSI GPIOInit.GPIO_Mode = GPIO_Mode_AF_PP; GPIOInit.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(SCK_LINE, &GPIOInit); //Pin Miso GPIOInit.GPIO_Pin = MISO_PIN; // MISO GPIOInit.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIOInit.GPIO_Speed = GPIO_Speed_10MHz; GPIO_Init(MISO_LINE, &GPIOInit); //Pin CS GPIOInit.GPIO_Pin = CS_PIN; GPIOInit.GPIO_Mode = GPIO_Mode_Out_PP; GPIOInit.GPIO_Speed = GPIO_Speed_10MHz; GPIO_Init(CS_LINE, &GPIOInit); //Ustawienie pinu CS na niski, następuje wybranie urządzenia GPIO_SetBits(GPIOC, GPIO_Pin_0); } //Ustawienie portów dla USARTU void GPIOInit_USART(void) { GPIO_InitTypeDef GPIOInit; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //PA2 Tx GPIO_StructInit(&GPIOInit); GPIOInit.GPIO_Pin = GPIO_Pin_2; GPIOInit.GPIO_Speed = GPIO_Speed_50MHz; GPIOInit.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOA, &GPIOInit); //PA3 Rx GPIOInit.GPIO_Pin = GPIO_Pin_3; GPIOInit.GPIO_Speed = GPIO_Speed_50MHz; GPIOInit.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIOInit); } //Inicjalizacja spi void SPIInit(void) { SPI_InitTypeDef SPIInit; RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); SPI_StructInit(&SPIInit); //Transmisja z wykorzystaniem dwóch linii SPIInit.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //Tryb pracy mikrokontrolera SPIInit.SPI_Mode = SPI_Mode_Master; //Stan sygnału taktującego przy braku transmisji SPIInit.SPI_CPOL = SPI_CPOL_Low; //Aktywne zbocze sygnału taktującego SPIInit.SPI_CPHA = SPI_CPHA_1Edge; //Wylaczenie sprzetowej obslugi linii CS SPIInit.SPI_NSS = SPI_NSS_Soft; //Szybkośc transmisji 64MHz/16 = 4MHz SPIInit.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16; //Inicjalizacja SPI SPI_Init(SPI1, &SPIInit); //Włączenie SPI SPI_Cmd(SPI1, ENABLE); } //Wysłanie danych uint8_t SPI_SEND(uint8_t byte) { //Sprawdzanie zajętości bufora nadawczego, jak będzie wolny nastąpi wysłanie danych while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); SPI_I2S_SendData(SPI1, byte); //Sprawdzanie, czy dane są w buforze odbiorczym, jeśli tak to zostaną zwrócone while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET); return SPI_I2S_ReceiveData(SPI1); } //Wpisanie danych do rejestrów void MCP_WRITE(uint8_t rejestr, uint8_t wartosc) { //Ustawienie pinu CS na stan niski, wybranie urządzenia GPIO_ResetBits(CS_LINE, CS_PIN); //wpisanie danych do CS. SPI_SEND(0x40); //wyslanie informacji do którego rejestru dane zostaną przesłane SPI_SEND(rejestr); //wysłanie ustawienia wartości SPI_SEND(wartosc); //Zakończenie transmisji, zmiana stanu linii CS na wysoki GPIO_SetBits(CS_LINE, CS_PIN); } //Funckcja do ustawiania wybranego portu void GP_ACTIVE(int Pin) { //Ustawienie pinu jako wejście przebiega poprzez wyzerowanie //odpowiedniego zestawu bitow switch(Pin) { case 0: MCP_WRITE(MCP_IODIR, ~0x01); break; //GP0 bit0 Dec: 1 case 1: MCP_WRITE(MCP_IODIR, ~0x02); break; //GP1 bit1 Dec: 2 case 2: MCP_WRITE(MCP_IODIR, ~0x04); break; //GP2 bit2 Dec: 4 case 3: MCP_WRITE(MCP_IODIR, ~0x08); break; //GP3 bit3 Dec: 8 case 4: MCP_WRITE(MCP_IODIR, ~0x10); break; //GP4 bit4 Dec: 16 case 5: MCP_WRITE(MCP_IODIR, ~0x20); break; //GP5 bit5 Dec: 32 case 6: MCP_WRITE(MCP_IODIR, ~0x40); break; //GP6 bit6 Dec: 64 case 7: MCP_WRITE(MCP_IODIR, ~0x80); break; //GP7 bit7 Dec: 128 default: MCP_WRITE(MCP_IODIR, ~0x01); break; } } //Grupowa aktywacja pinów od GP0 do wybranego void GP_ACTIVE_GROUP(int PinEnd) { //Ta funkcja ustawia odpowiednie wartości dla pinu początkowego oraz końcowego //Ustawienie pinu jako wejście przebiega poprzez wyzerowanie //odpowiedniego zestawu bitow switch(PinEnd) { case 1: MCP_WRITE(MCP_IODIR, ~0x03); break; //GP0 bit0 Dec: 3 case 2: MCP_WRITE(MCP_IODIR, ~0x07); break; //GP1 bit1 Dec: 7 case 3: MCP_WRITE(MCP_IODIR, ~0x0F); break; //GP2 bit2 Dec: 15 case 4: MCP_WRITE(MCP_IODIR, ~0x1F); break; //GP3 bit3 Dec: 31 case 5: MCP_WRITE(MCP_IODIR, ~0x3F); break; //GP4 bit4 Dec: 63 case 6: MCP_WRITE(MCP_IODIR, ~0x7F); break; //GP5 bit5 Dec: 127 case 7: MCP_WRITE(MCP_IODIR, ~0xFF); break; //GP6 bit6 Dec: 255 default: MCP_WRITE(MCP_IODIR, ~0xFF); break; } } //Pętla opóźniająca void Delay(int czas) { int i; for (i = 0; i < czas * 5000; i++) {} } //Inicjalizacja USARTA void USARTInit(void) { USART_InitTypeDef USARTInit; NVIC_InitTypeDef NVIC_InitStruct; NVIC_InitStruct.NVIC_IRQChannel = USART2_IRQn; RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); //Ustawienie prędkości transmisji 9600bps USARTInit.USART_BaudRate = 9600; //Ustawienie dlugosci slowa USARTInit.USART_WordLength = 8; //Ustawienie bitu stopu USARTInit.USART_StopBits = USART_StopBits_1; //Brak kontroli parzystosci USARTInit.USART_Parity = USART_Parity_No; //Kontrola przepływu danych USARTInit.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //Tryb pracy TX i RX USARTInit.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART2, &USARTInit); USART_Cmd(USART2, ENABLE); USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); //Wlaczenie przerwania dla RX //Włącznie obslugi IRQ NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; //Ustawienie priorytetu grupowego dla USART1 przypisane jest 0 NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0x00; //Podpriorytet NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1; NVIC_Init(&NVIC_InitStruct); } //Wysłanie danej void SendCharc(volatile char c) { while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET); USART_SendData(USART2, c); } //Wysłanie danych void USARTSend(volatile char *c) { //Pętla działa do puki będzie jakiś znak do wysłania while(*c) { //Sprawdza czy rejestr danych został opróżniony while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET); //Prześlij dane, USART_SendData(USART2, *c); *c++; } } //Wprowadzenie danych do bufora void USART_InsertToBuffer(uint8_t usart_num, uint8_t c) { usart_num = 0; //Sprawdza dostepne miejsce w buforze if (usart_buf_num[usart_num] < 32) { if (usart_buf_in[usart_num] > (32 - 1)) { usart_buf_in[usart_num] = 0; } USART_Buffer[usart_num][usart_buf_in[usart_num]] = c; usart_buf_in[usart_num]++; usart_buf_num[usart_num]++; } } //Pobranie danej uint8_t USART_Getc(USART_TypeDef* USARTx) { uint8_t usart_num = 0; uint8_t c = 0; //Sprawdza czy sa dane w buforze if (usart_buf_num[usart_num] > 0) { if (usart_buf_out[usart_num] > (32 - 1)) { usart_buf_out[usart_num] = 0; } c = USART_Buffer[usart_num][usart_buf_out[usart_num]]; USART_Buffer[usart_num][usart_buf_out[usart_num]] = 0; usart_buf_out[usart_num]++; usart_buf_num[usart_num]--; } return c; } //Obsługa przerwania od USARTU #ifdef USART2 void USART2_IRQHandler(void) { //Sprawdzenie czy wystapilo przerwanie z powodu odebrania danych if (USART_GetITStatus(USART2, USART_IT_RXNE)) { #ifdef USART2_USE_CUSTOM_IRQ //Wywołaj funckje przerwania USART2_ReceiveHandler(USART2->DR); #else //Podaj dane do Buffera USART_InsertToBuffer(0, USART2->DR); #endif } } #endif