AnsweredAssumed Answered

[STM32F103VE] SPI1 strange behaviour

Question asked by yooz on Feb 24, 2012
Latest reply on Feb 24, 2012 by yooz
First of all: hello to all users! I am new here, and my adventure with STM32 has just began.

I have been fighting with strange problem on my STM32F103VE.


The description: when I initialize the pins responsible for SPI1 (PA4-PA7) as normal GPIO (PA4, PA5, PA7 as Output Push-Pull max 50 MHz; PA6 as Floating Input) I communicate with slave device without any problem. PA4 is my Chip Select line (without any pull-ups). I use electronic osciloscope for measuring the waveforms, and they are ok, timing and levels (0V low, 3.3V high). BUT! When I use the built in SPI1 peripheral (no remap), strange things happen - one of the lines (SCK or MOSI) in low level appears to be 1.3V, high level is ok - 3.3V. There is a 3.3-5V level converter between STM32 and LCD slave, but it works ok, because if I play with pins manually following the SPI protocol - all works ok (as previously stated). If I disconnect the LCD slave from interface behind level converter - waveforms go back to normal (low - 0V, high - 3.3V). So I'm rather out of ideas what is wrong. Seems to me that either SPI1 peripheral is broken (GPIO works good at same pins), or some stupid mistakes in code.


My development environment

- Eclipse 20100617-1415 Helios
- arm-none-eabi C/C++ compiler
- OpenOCD 0.5.0
- JTAG Andtech (FT2232)

STM32 PCB is MMstm32F103Vx from Propox.


For easier understanding
of code: when I change the GPIO pins manually, I use spi1_test_init() function and then uncomment part of code in spi_test_sending().
When I want to use built in peripheral SPI1 I use spi_init_master() and uncomment other part of code in spi_test_sending().

Here are the most important parts of my code:


 
int main(void){
    system_init();
    pll_start(CRYSTAL, FREQUENCY);
    spi_init_master();// I use SPI1
    // spi1_test_init();  // manual pin change, prot. SPI
 
    while(1){
        spi_test_sending();
        //some delay inserted here
    }
}
 
staticvoid system_init(void)
{
     RCC->CFGR |= RCC_CFGR_PPRE2_0 | RCC_CFGR_PPRE2_1 | RCC_CFGR_PPRE2_2;// set APB2 divider to 16
     // enable GPIOs on port A,B and E
     RCC->APB2ENR = RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN |   RCC_APB2ENR_IOPDEN | RCC_APB2ENR_IOPEEN |
     RCC_APB2ENR_AFIOEN;//| RCC_APB2ENR_IOPCEN |
     //RCC_APB2ENR_IOPDEN | RCC_APB2ENR_IOPEEN;     // enable all GPIOs
 
     GPIOE->CRH =0x44411444;// configure port E bits 8-10 and 13-15 as floating inputs, 11-12 as GP push-pull outputs (for leds)
 
     D7_CR =((D7_CR &0x0F0F0FFF)|0x10101000);// configure port D bits 11,13,15 as GP push-pull outputs (for 7 segment disp.)
 
}
 
void spi1_test_init(){
     GPIOA->CRL =((GPIOA->CRL &0x00000FFF)|0x38333000);
     GPIOA->ODR &=0xFFFFFF0F;
     spi_ss(1);
} 

SPI initialization from another file:

void spi_init_master (void){
/* Initialize and enable the SSP Interface module. */
 
if(spi_state == SPI_STATE_NOT_INITIALIZED){
             spi_state = SPI_STATE_INIT_SET_REGS;
}
 
if(spi_state == SPI_STATE_INIT_SET_REGS){
 
// Enable clock for GPIOA, AFIO and SPI1
           RCC->APB2ENR |= RCC_APB2ENR_AFIOEN | RCC_APB2ENR_IOPAEN | RCC_APB2ENR_SPI1EN;
 
/* No SPI1 remap (use PA5..PA7). */
        AFIO->MAPR   &=0xFFFFFFFE;
 
/* SPI1_NSS is GPIO, output set to high. */
/* SPI1_SCK, SPI1_MISO, SPI1_MOSI are SPI pins. */
        GPIOA->CRL =((GPIOA->CRL &0x00000FFF)|0xB8B33000);
        GPIOA->BSRR =0x00000018;// set the SPI1_SS to high level
 
           SPIx->CR1 = SPI_CR1_CPHA | SPI_CR1_MSTR | SPI_CR1_LSBFIRST | SPI_CR1_BR_1 | SPI_CR1_BR_2;
        SPIx->CR2  =0x0004;
        SPIx->CR1 |= SPI_CR1_SPE;
        spi_state = SPI_STATE_INIT_CLEAR_RX;
}
 
if(spi_state == SPI_STATE_INIT_CLEAR_RX){
          spi_check_regs();
if(!(SPIx->SR & TXE)||(SPIx->SR & RXNE)||(SPIx->SR & BSY)){
uint8_t data =0;
               data = SPIx->DR;
               spi_check_regs();
}else{
               spi_state = SPI_STATE_INITIALIZED;
               newByteReceived = FALSE;
               newByteToSend = FALSE;
}
}
}
spi_check_regs() function reads the SPI SR CR1 and CR2 registers. And finally spi_test_sending from main.c:
void spi_test_sending(){
uint8_t data_to_send[]={17,3,27,68,73,188};
uint16_t  data_read =0, j =0;//, bcnt = 0;
uint16_t i =0;
 
/*   
// manual pin change code begin  
    uint16_t bcnt = 0;
     for(bcnt = 0; bcnt < 6; bcnt++){
 
          spi_ss(0);
          // transmit every byte of data
          for(i = 0; i < 8; i++){
               // set the SCK line high
               GPIOA->ODR = GPIOA->ODR | 0x00000020;
               if((data_to_send[bcnt] >> i) & 0x01){
                    GPIOA->ODR |= 0x00000080;
               } else {
                    GPIOA->ODR &= ~(0x00000080);
               }
               for(j = 0; j < 50; j++){
                    j = j;
               }
               // set the SCK line low
               GPIOA->ODR &= 0xFFFFFFDF;
               for(j = 0; j < 50; j++){
                    j = j;
               }
          }
          spi_ss(1);
          // make a short pause
          for(i = 0; i < 200; i++)
          {
               i = i;
          }
     }
 
     GPIOA->ODR &= ~(0x00000080);          // reset MOSI pin
 
     spi_ss(0);                                   // slave select
 
     // send dummy byte to clock in the response
     for(i = 0; i < 8; i++){
          // set the SCK line high
          GPIOA->ODR = GPIOA->ODR | 0x00000020;
          //               }
          for(j = 0; j < 50; j++){
               j = j;
          }
          // set the SCK line low
          GPIOA->ODR &= 0xFFFFFFDF;
          for(j = 0; j < 50; j++){
               j = j;
          }
          //read MISO pin and shift in to data_read
          if(GPIOA->ODR & 0x00000040){
               data_read = data_read | 0x80;
          }
          data_read = data_read >> 1;
 
     }
     spi_ss(1);               // slave unselect
     // make a short pause
     for(i = 0; i < 200; i++)
     {
          i = i;
     }
 
     if(data_read == 0x06){
          LED_D3 = LED_ON;
     }
 
     GPIOA->ODR &= 0xFFFFFF0F;
/**/

 
// manual pin change code end
// SPI1 built-in peripheral code begin
 
 
if(SPIx->SR & SPI_SR_RXNE){
          data_read = SPIx->DR;
}
 
 
for(i=0;i<(data_to_send[1]+3);i++){
          spi_ss(0);
while(((SPIx->SR)& SPI_SR_BSY)){
 
}
if(!spi_simple_send(data_to_send[i])){
break;
}
if(SPIx->SR & SPI_SR_RXNE)
{
               data_read = SPIx->DR;
}
while(((SPIx->SR)& SPI_SR_BSY)){
 
}
for(j=0;j<100;j++)
{
               spi_ss(0);
}
          spi_ss(1);
for(j=0;j<(u16)(FREQUENCY/150000ul);j++)
{
               spi_ss(1);
}
}
     spi_ss(0);
     spi_simple_send(0);
while(((SPIx->SR)& SPI_SR_BSY)){
 
}
     spi_ss(1);
 
 
 
/* */
}
 
 
Slave device demands that between each byte  there is 80us pause, and during this time CS line should be high. There are also two functions used: spi_ss(level) for setting CS line level, and spi_simple_send(data) which is used only by peripheral SPI1 implementation.
bool spi_simple_send (u8 sendB){
while(!(SPIx->SR & SPI_SR_TXE)){
          spi_check_regs();
}
if(SPIx->SR & SPI_SR_TXE){
          SPIx->DR = sendB;
return TRUE;
}else{
return FALSE;
}
}
 
void spi_ss (u32 ss){
 
// for SPI1 pin PA3
/* Enable/Disable SPI Chip Select (drive it high or low). */
     GPIOA->BSRR = ss ?0x00000008:0x00080000;
 
 
}
 
So if any of you guys would go through the code and see what can be possibly wrong, or why such strange SPI behaviour could happen, please let me know.
Cheers
yooz
 

Outcomes