STM32F100 se při odeslání znaku po SPI zasekne

Ahoj, mám takový dost záludný problém. Pokud odešlu znak po SPIčku tak se mi program zasekne. Podle pozorování dojde k obsluze přerušení a shození příznaku vyprázdnění TX bufru…pak už program nic nevykonává. Pokud nezapnu přerušení tak program každou sekundu bez problémů odesílá.
Nemáte někdo podoné zkušenosti? přikládám zdroják:

extern uint32_t m_global_cas;
volatile uint16_t mcp_cmd;

void init_spi(void){
  SPI1->CR1 = 0
  | SPI_CR1_MSTR                                    // Master Selection 
  | SPI_CR1_CPOL                                     // Clock Polarity
  | SPI_CR1_CPHA                                     // Clock Phase
  | SPI_CR1_DFF                                       // Data Frame Format
  | SPI_CR1_BR_1                                    // Baud Rate Control        
  //| SPI_CR1_LSBFIRST                           // Frame Format        
  | SPI_CR1_SPE;                                     // SPI Enable 
  
  SPI1->CR2 = 0
  | SPI_CR2_TXEIE;                                  // Tx buffer Empty Interrupt Enable    
  
  NVIC_EnableIRQ(SPI1_IRQn);              // Enable interrupt from SPI1 
  }
void handle_spi(void){
  char *pchPtr = (char*)&mcp_cmd; //pchPtr[0] - lo bajt, pchPtr[1] - hi bajt
  if((m_global_cas & (1<<C_1S_SPI)) != 0){ m_global_cas &= ~(1<<C_1S_SPI);
    pchPtr[1] = 0x40;
    pchPtr[0] = 0x01;
    SPI1->DR = mcp_cmd;
    }
  }
//=============================================================================
// SPI1 Interrupt Handler
//=============================================================================
void SPI1_IRQHandler(void){     // pri odeslani znaku
  if(SPI1->SR & SPI_SR_TXE){    // if TXE flag is set   
    SPI1->SR &= ~SPI_SR_TXE;    // clear TXE flag
    //change_state_led(LEDG_MODUL, LED_ON, 0);   //test led
    }
  }

Nemáš náhodou tabulku vektorů přerušení v assembleru, jo? Protože jsem narazil na to, že assembler nenastavoval bit 0 adresy (flag) a kvůli tomu přerušení havarovalo, musela se k adresám přičítat jednička. (v listingu musí být adresa v tabulce vektorů liché číslo)

No todle mě také napadlo, ale došel jsem k závěru, že toto je nadefinováné dobře. Udělal jsem ale jinej pokus. Podle datasheetu se ten příznak shazuje pokaždé, když se naplní opět registr pro odeslání. Pokud v tom přerušení tedy registr opět naplním daty, tak je vše bez problémů. Zdroják jsem teda upravil tak, že před naplněním registru povolím přerušení, jakmile se přerušení vykoná tak ho zakážu. Je to sice taková ochcávačka, ale funguje to stejně efektivně.

Si myslím, že problém je v tom, že po povolení INT je SPI_SR_TXE rovné 1, lebo nemá čo odoslať. To spôsobí interrupt. Keďže príznak sa nuluje zápisom do dátového registra a inak je read-only, tak sa neznuluje a po ukončení prerušenia hneď nastane ďalšie.

Nevím sice který je to MCU, neviděl jsem datasheet - ale mám dojem že ta obsluha bude špatně. Jestli jsem to správně pochopil z náznaků na netu, SPI_SR_TXE je jenom readonly flag že je možné odeslat znak (že vysílací buffer je prázdný), takže by ani neměl co dělat v obsluze přerušení a určitě by se neměl nulovat. Buď by měla obsluha z hlavní smyčky ten flag testovat a pokud je buffer volný, tak odeslat další bajt, nebo by to mohla řešit přerušovací rutina a sama odesílat další bajty je-li třeba.

Tohle jsem řešil na STM32F103 nedávno. Věc se má tak že SPI_SR_TXE je 1 vždy kromě doby kdy není možné vložit data do vysílacího registru. Takže pokud je povoleno přerušení od tohodle flagu, vyvová se hned jak se povolí globální IRQ a není nutno ani nic odesílat přes SPI. Takže doporučuju se buď pověsit na SPI_SR_RXNE (nepatrně to brzdí přenos) a nebo zjišťovat jestli je znak vkládaný do SPI->DR poslední a zakázat irq od tx_empty.

Díky za rady, všechny jsem si je vzal k srdci a myslím, že to vedlo ke zdárnému konci. Zarazili mě ale dvě věci. Jedna je ta, že při hardwarovém řízení SS se sepne asi tak 2/3 času před tím než jsou vyslána data. Očekával bych, že bude aktivován těsně před tím než se data budou vysílat. Druhá věc je ta, že když se data odešlou tak se SS přepne do jedničky, ale náběh rozhodně není hranatý a trvá cca 4us.