[STM32F103] Remapowany SPI1 zdaje się nie odbiera danych

Tu możesz pisać o swoich problemach z pisaniem programów w języku C/C++ dla STM.
Awatar użytkownika
ZbeeGin
User
User
Posty: 492
Rejestracja: sobota 08 lip 2017, 17:16
Lokalizacja: Śląsko-Zagłębiowska Metropolia
Kontaktowanie:

[STM32F103] Remapowany SPI1 zdaje się nie odbiera danych

Postautor: ZbeeGin » środa 06 gru 2017, 19:30

Witam.

Mam problem z podstawowym dogadaniem się z pamięcią szeregową M25P16 po SPI z STM32F103VB. Pamięć ta jest podłączona do pinów SPI1, ale nie tych, które są domyślnie dla niego przeznaczone w porcie A tylko na porcie B. Stąd potrzeba ich remapowania.

Myślę, że w kodzie odpowiedzialnym za obsługę tej pamięci zrobiłem wszystko co potrzeba:
- włączone zegary,
- skonfigurowane odpowiednie piny jako AF,
- włączone AFIO,
- włączone remapowanie SPI1,
- wyłączony JTAG.

Niestety odczyty bajtu statusu czy ciągu ID zawsze zwracają zera. I teraz nie wiem, czy coś jest ze sprzętem, czy w kodzie jest coś nie tak albo jeszcze czegoś zapomniałem dopisać w kwestii remapowania (najbardziej parawdopodobne). Jeśli ktoś jeszcze mógłby rzucić fachowym okiem...

ps1. Nieistotne fragmenty wycięte.
ps2. Sorry za ang. komentarze - ale są krótsze :)


ext_mem_hw.png


Kod: Zaznacz cały

/**
  ******************************************************************************
  * @file    main.c
  * @version V1.0
(...)
*/

// includes

#include "stm32f10x.h"

#include "status_leds.h"
#include "ext_memory.h"


// global variables

uint32_t dupsko;


// start-up function - prepare MCU

int board_startup(void)
{
(...)
   leds_init();
   ext_mem_init();

   return(0);
}


// main function

int main(void)
{
   uint8_t   memstat;

   board_startup();            // start hardware
   LEDPWR_ON;               // PWR LED on - we are alive

   ext_mem_clear_buffer();         // erase memory buffer
   ext_mem_get_id();            // read memory id (FUTURE: raise error if missmatch)
   memstat = ext_mem_get_status();   // read memory status byte

   LEDMODE_1;               // indicate mode 1 - no protection valves
   
   for(;;){
(...)
   }
}
// end of main



Kod: Zaznacz cały

/*
 * ext_memory.h
(...)
 */

#include "stm32f10x.h"

#ifndef EXT_MEMORY_H_
#define EXT_MEMORY_H_

// Define pins and ports
#define SPI_PORT      GPIOB         // All SPI HW pins at Port B
#define SPI_MISO      GPIO_Pin_4      // PB4 - SPI1 MISO Remap
#define SPI_MOSI      GPIO_Pin_5      // PB5 - SPI1 MOSI Remap
#define SPI_SCK      GPIO_Pin_3      // PB3 - SPI1 SCK Remap

#define SPI_CS         GPIO_Pin_15      // PA15 - Regular GPIO
#define SPI_CS_PORT      GPIOA
#define SPI_WP         GPIO_Pin_6      // PB6 - Regular GPIO Low Level
#define SPI_WP_PORT   GPIOB


// Define APB clock bits to enable
#define   RCC_SPI         RCC_APB2Periph_SPI1
#define   RCC_GPIO         RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB


// Declare Macros
#define   MEM_SELECT      GPIO_ResetBits(SPI_CS_PORT, SPI_CS)
#define    MEM_DESELECT      GPIO_SetBits(SPI_CS_PORT, SPI_CS)

#define   MEM_PROTECT      GPIO_ResetBits(SPI_WP_PORT, SPI_WP)
#define    MEM_UNPROTECT   GPIO_SetBits(SPI_WP_PORT, SPI_WP)

// ------------------------------------------------------------------

// Memory sectors

#define SECTOR_SIZE      0x00010000
#define PAGE_SIZE      0x00000100
#define MAX_PAGE      (PAGE_SIZE - 1)
(...)
// Memory commands
#define CMD_WRITE_STATUS_REG   0x01
#define CMD_PAGE_PROGRAM      0x02
#define CMD_READ_DATA_BYTES   0x03
#define CMD_WRITE_DISABLE      0x04
#define CMD_READ_STATUS_REG   0x05
#define CMD_WRITE_ENABLE      0x06
#define CMD_READ_DATA_FAST      0x0b
#define CMD_READ_ID1         0x9f
#define CMD_READ_ID2         0x9e
#define CMD_LEAVE_PD         0xab
#define CMD_ENTER_PD         0xb9
#define CMD_BULK_ERASE         0xc7
#define CMD_SECTOR_ERASE      0xd8

// ##################################################################

uint8_t page_buffer[PAGE_SIZE];            // read/write buffer
uint8_t mem_id[20];                     // 3 byte ID and max 17 bytes of signature

// Declare input output functions

void ext_mem_init(void);
uint8_t ext_mem_exchange_byte(uint8_t byte);
uint8_t ext_mem_get_status(void);
void ext_mem_get_id(void);

void ext_mem_clear_buffer(void);
(...)
#endif /* EXT_MEMORY_H_ */



Kod: Zaznacz cały

/*
 * ext_memory.c
 *
(...)
 */

#include "ext_memory.h"
#include "stm32f10x.h"


void ext_mem_init(void)
{
   GPIO_InitTypeDef gpio;
   SPI_InitTypeDef spi;

   // Enable some peripherials
   RCC_APB2PeriphClockCmd(RCC_GPIO , ENABLE);
   RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO , ENABLE);
   RCC_APB2PeriphClockCmd(RCC_SPI , ENABLE);

   // Remap HW SPI1 to alternative location and disable JTAG
   GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable , ENABLE);
   GPIO_PinRemapConfig(GPIO_Remap_SPI1, ENABLE);

   // Software part
   GPIO_StructInit(&gpio);
   gpio.GPIO_Pin = SPI_CS;
   gpio.GPIO_Mode = GPIO_Mode_Out_PP;
   gpio.GPIO_Speed = GPIO_Speed_50MHz;
   GPIO_Init(SPI_CS_PORT, &gpio);

   gpio.GPIO_Pin = SPI_WP;
   GPIO_Init(SPI_WP_PORT, &gpio);

   MEM_DESELECT;
   MEM_UNPROTECT;

   // Hardware Part - SPI
   GPIO_StructInit(&gpio);
   gpio.GPIO_Pin = SPI_SCK;
   gpio.GPIO_Mode = GPIO_Mode_AF_PP;
   gpio.GPIO_Speed = GPIO_Speed_50MHz;
   GPIO_Init(SPI_PORT, &gpio);

   gpio.GPIO_Pin = SPI_MISO;
   gpio.GPIO_Mode = GPIO_Mode_IN_FLOATING;
   gpio.GPIO_Speed = GPIO_Speed_50MHz;
   GPIO_Init(SPI_PORT, &gpio);

   gpio.GPIO_Pin = SPI_MOSI;
   gpio.GPIO_Mode = GPIO_Mode_AF_PP;
   gpio.GPIO_Speed = GPIO_Speed_50MHz;
   GPIO_Init(SPI_PORT, &gpio);

   SPI_StructInit(&spi);
   spi.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
   spi.SPI_DataSize = SPI_DataSize_8b;
   spi.SPI_CPHA = SPI_CPHA_1Edge;
   spi.SPI_CPOL = SPI_CPOL_Low;
   spi.SPI_FirstBit = SPI_FirstBit_MSB;
   spi.SPI_Mode = SPI_Mode_Master;
   spi.SPI_NSS = SPI_NSS_Soft;
   spi.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32;
   SPI_Init(SPI1, &spi);

   // Start SPI
   SPI_Cmd(SPI1, ENABLE);
}


// SPI communication
uint8_t ext_mem_exchange_byte(uint8_t byte)
{
   while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
   SPI_I2S_SendData(SPI1, byte);
   while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);

   return SPI_I2S_ReceiveData(SPI1);
}


// read memory status
uint8_t ext_mem_get_status(void)
{
   MEM_SELECT;
   ext_mem_exchange_byte(CMD_READ_STATUS_REG);

   return(ext_mem_exchange_byte(0xff));
   MEM_DESELECT;
}


// read memory status
void ext_mem_get_id(void)
{
   uint32_t i;

   MEM_SELECT;

   ext_mem_exchange_byte(CMD_READ_ID1);

   for(i = 0; i < 19; i++)
      mem_id[i] = ext_mem_exchange_byte(0xff);   // dummy write

   MEM_DESELECT;
}


// clear buffer with
void ext_mem_clear_buffer(void)
{
   uint32_t i;

   for(i = 0; i < MAX_PAGE; i++)
      page_buffer[i] = 0xff;            // as unprogrammed
}
(...)
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.
Ostatnio zmieniony czwartek 07 gru 2017, 10:48 przez ZbeeGin, łącznie zmieniany 1 raz.

Awatar użytkownika
ZbeeGin
User
User
Posty: 492
Rejestracja: sobota 08 lip 2017, 17:16
Lokalizacja: Śląsko-Zagłębiowska Metropolia
Kontaktowanie:

Re: [STM32F103] Remapowany SPI1 zdaje się nie odbiera danych

Postautor: ZbeeGin » czwartek 07 gru 2017, 10:41

Dzisiaj wziąłem i zebrałem przebiegi tego SPI. Sorry, że jednokomórkowcem, ale żaden pen nie chciał współpracować.
Zapętliłem tą część by nie łapać w trybie SINGLE.

Kod: Zaznacz cały

   ext_mem_get_id();            // read memory id (FUTURE: raise error if missmatch)
   memstat = ext_mem_get_status();   // read memory status byte


Oto kardiogram:

20171207_095645.jpg

20171207_095949.jpg

20171207_100110.jpg

20171207_100140.jpg

20171207_100219.jpg


Jak widać SCK, MOSI pracują, a CS spada. Choć widać, że CS nie wraca na chwilę pomiędzy czytaniem statusu a czytaniem bajtów ID. Być może wynik optymalizacji kodu przez kompilator.
Musze jeszcze dogłębniej przeanalizować na bitach co jest wysyłane, bo coś mi tu się nie podoba...

---

Jeszcze zależność od pojawienia się napięcia VCC. Czas POR pamięci może wynieść 20ms, tu jakiekolwiek działanie z magistralą pojawia się po 200ms.

20171207_122217.jpg

20171207_122247.jpg

20171207_122329.jpg
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.
Ostatnio zmieniony czwartek 07 gru 2017, 12:36 przez ZbeeGin, łącznie zmieniany 1 raz.

Awatar użytkownika
Nefarious19
Newb
Newb
Posty: 80
Rejestracja: sobota 02 sty 2016, 20:45

Re: [STM32F103] Remapowany SPI1 zdaje się nie odbiera danych

Postautor: Nefarious19 » czwartek 07 gru 2017, 12:31

Zanim ustawisz CS na high, sprawdzaj flagę BSY w rejestrze SPI1->SR.

Coś takiego:
while(SPI1->SR & SPI_SR_BSY);
CS_HIGH;
Plany na przyszłość: C, C++, C#

Awatar użytkownika
ZbeeGin
User
User
Posty: 492
Rejestracja: sobota 08 lip 2017, 17:16
Lokalizacja: Śląsko-Zagłębiowska Metropolia
Kontaktowanie:

Re: [STM32F103] Remapowany SPI1 zdaje się nie odbiera danych

Postautor: ZbeeGin » czwartek 07 gru 2017, 17:44

Wydaje mi się - tak to zrozumiałem z treści noty - że sprawę odczekiwania rozwiązuje już sama procedura wymiany bajtu `ext_mem_exchange_byte()`, gdzie sprawdzana jest inna flaga SPI_I2S_IT_RXNE, która pojawi się jak rejestr odbiorczy zostanie zapełniony. Dodatkowo flaga zaniknie jak rejestr ten zostanie odczytany co dzieje się przy powrocie z funkcji.

Co więcej. Na oscylogramach nie mogę w żaden sposób wyłapać momentów gdzie CS tak jak w kodzie wraca w stan H. Widać, że utrzymuje się ono cały czas w stanie niskim w trakcie wymiany danych. Zatem "przypuszczalnie" nie zanika w trakcie odbioru.

Awatar użytkownika
ZbeeGin
User
User
Posty: 492
Rejestracja: sobota 08 lip 2017, 17:16
Lokalizacja: Śląsko-Zagłębiowska Metropolia
Kontaktowanie:

Re: [STM32F103] Remapowany SPI1 zdaje się nie odbiera danych

Postautor: ZbeeGin » piątek 08 gru 2017, 19:48

Informuję wszem i wobec, że pamięć zaczęła "rozmawiać" z procesorem. Studium przypadku jest następujące.

Na dobry trop naprowadził mnie kol. Antystatyczny podczas czatu. Okazuje się, że cytat ze skeczu kabaretowego, które umieściłem na jednym ze zdjęć był jak najbardziej prawdziwy w tym wypadku. I cały problem był już bardzo dobrze widoczny na tych zdjęciach z oscyloskopu. Ale po kolei.

Pierwszy problem z tym kodem był taki, że w pierwotnej jego wersji zrobione zostało remapowanie SPI1 na alternatywne piny w porcie PB. Zostały one skonfigurowane, uruchomiono remapowanie poprzez AFIO, ale nie został wyłączony interfejs JATG, który przejmuje kontrolę nad częścią wykorzystanych pinów. Dlatego sygnały SCK oraz CS pamięci nie były sterowane, a nawet gdyby jakimś cudem na linii MISO pojawiłyby się jakieś dane nie zostałyby one w ogóle odebrane. Dlatego też przy takim remapowaniu należy również wyłączyć interfejs JTAG lub jeśli możemy sobie na to pozwolić JTAG razem z SWD. Oczywiście w tym ostatnim przypadku tracimy wtedy możliwość debugowania w układzie ST-LINKiem. W moim przypadku skończyło się tylko na wyłączeniu JTAG-a funkcją GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable , ENABLE);.

Kolejny problem z tym kodem jest dwojaki. Pierwsza rzecz, to jego optymalizacja przez kompilator. Jak widać na oscylogramach linia CS spada do stanu L przy wysłaniu polecenia odczytania bajtu statusu, po nim następuje polecenie odczytania bajtów identyfikacji i wraca w stan H dopiero po zakończeniu czytania 20 bajtów (Okazuje się, że pamięć Micron M25P16 wysyła w sumie 20 bajtów, a zastosowany układ ST M25P16 wysyła ich tylko trzy!). Z punktu widzenia pamięci wysłaliśmy jej polecenie odczytania statusu i pamięć ten bajt wysłała! Nie widać tego, bo pamięć zwraca 0x00. Polecenie wysłania ID pamięci zostanie zignorowane, gdyż pamięć myśli, że wysyłamy jej jakieś dane, które przy tym poleceniu nie są jej w ogóle potrzebne, dlatego je zignorowała. Tak stan rzeczy jest spowodowany tym, że każde polecenie oraz dane nadane lub odebrane musi zakończyć się powrotem linii CS w stan H. Wtedy pamięć uzna, że bieżące odpytanie jest zakończone. Następne polecenie należy wysłać ponownie aktywując pamięć niskim stanem linii CS. Tylko tak pamięć zinterpretuje pierwszy nadany bajt jako polecenie, a kolejne jako dane.

Ktoś może zapytać: Przecież w kodzie występują makra MEM_ENABLE i MEM_DISABLE, zatem linia CS powinna być tak sterowana, więc o co chodzi? Niestety powyższy kod ma poważny błąd i to Mój błąd. :shock: Jeśli się dokładnie przyjrzymy to w funkcji odczytu zerujemy pin CS wpisując zero na odpowiedniej pozycji do rejestru wyjściowego portu. Wysyłamy komendę, odczytujemy dane i przekazujemy funkcji return(), następnie wpisujemy w rejestr portu jedynkę by ustawić pin CS. A jak to widzi kompilator:

0800092c <ext_mem_get_status>:
800092c: b580 push {r7, lr}
800092e: af00 add r7, sp, #0
8000930: f44f 4100 mov.w r1, #32768 ; 0x8000
8000934: 4805 ldr r0, [pc, #20] ; (800094c <ext_mem_get_status+0x20>)
8000936: f7ff fd3a bl 80003ae <GPIO_ResetBits>
800093a: 2005 movs r0, #5
800093c: f7ff ffd0 bl 80008e0 <ext_mem_exchange_byte>
8000940: 20ff movs r0, #255 ; 0xff
8000942: f7ff ffcd bl 80008e0 <ext_mem_exchange_byte>
8000946: 4603 mov r3, r0
8000948: 4618 mov r0, r3
800094a: bd80 pop {r7, pc}
800094c: 40010800 andmi r0, r1, r0, lsl #16


Jak widać nie ma odwołania do GPIO_SetBits(). Dlaczego? Bo znajduje się to już po funkcji return(). Dlatego ta część została całkowicie wycięta! :oops:
Po poprawkach w funkcji ext_mem_get_status() odczyt bajtu statusowego jest już prawidłowa.

0800092c <ext_mem_get_status>:
800092c: b580 push {r7, lr}
800092e: b082 sub sp, #8
8000930: af00 add r7, sp, #0
8000932: f44f 4100 mov.w r1, #32768 ; 0x8000
8000936: 480f ldr r0, [pc, #60] ; (8000974 <ext_mem_get_status+0x48>)
8000938: f7ff fd39 bl 80003ae <GPIO_ResetBits>
800093c: 2005 movs r0, #5
800093e: f7ff ffcf bl 80008e0 <ext_mem_exchange_byte>
8000942: 4603 mov r3, r0
8000944: 71fb strb r3, [r7, #7]
8000946: 20ff movs r0, #255 ; 0xff
8000948: f7ff ffca bl 80008e0 <ext_mem_exchange_byte>
800094c: 4603 mov r3, r0
800094e: 71fb strb r3, [r7, #7]
8000950: bf00 nop
8000952: 4b09 ldr r3, [pc, #36] ; (8000978 <ext_mem_get_status+0x4c>)
8000954: 891b ldrh r3, [r3, #8]
8000956: b29b uxth r3, r3
8000958: f003 0380 and.w r3, r3, #128 ; 0x80
800095c: 2b00 cmp r3, #0
800095e: d1f8 bne.n 8000952 <ext_mem_get_status+0x26>
8000960: f44f 4100 mov.w r1, #32768 ; 0x8000
8000964: 4803 ldr r0, [pc, #12] ; (8000974 <ext_mem_get_status+0x48>)
8000966: f7ff fd14 bl 8000392 <GPIO_SetBits>
800096a: 79fb ldrb r3, [r7, #7]
800096c: 4618 mov r0, r3
800096e: 3708 adds r7, #8
8000970: 46bd mov sp, r7
8000972: bd80 pop {r7, pc}
8000974: 40010800 andmi r0, r1, r0, lsl #16
8000978: 40013000 andmi r3, r1, r0


Sytuacja może się powtórzyć, ale już nie z naszej winy, gdy w grę wejdzie optymalizacja kodu, która również może nam to wyciąć. Wtedy wypada wykorzystać mechanizmy ochronne by nawet w razie spotkania się jedna za drugą funkcji GPIO_SetBits() oraz GPIO_ResetBits() (np. w pętli odpytującej bajt statusu) nie nastąpiło wycięcie żadnych poleceń zmiany stanu CS zwłaszcza w sekwencji L→H i H→L.

Na koniec mała dygresja. Problem by nie został zauważony, gdybym nie ubzdurał sobie, że najpierw przeczytam sobie bajt statusu a zaraz potem jeszcze ID pamięci. Gdybym rozpoczął od tylko przeczytania ID to pewnie szybko bym odkrył, że po wyłączeniu interfejsu JTAG, sprzętowy SPI1 zremapowany na alternatywne piny jednak działa, i wszystko pozornie jest w porządku.

"Chciałby nad poziomy człek, a tu ciągle nic! Nie uniesie pusty łeb, ciężkiej dupy wzwyż..." (Władek Sikora, Kabaret "POTEM").


Wróć do „Programowanie STM w C/C++”

Kto jest online

Użytkownicy przeglądający to forum: Obecnie na forum nie ma żadnego zarejestrowanego użytkownika i 9 gości