2016-02-17 03:57 AM
Hi to all,
I tried to build my own acquisition board using a STM32F030K6 microcontroller and I am experiencing problems with SPI interface. At present only microcontroller is mounted, no other devices on board. As I am using PA5, PA6 and PA7 as analog input I use PB3, PB4, PB5 for SPI interface. I send thorugh SPI interface the value 0x80, but if MISO and MOSI pins are unconnected I receive 0xFF (and this is ok), but when I shortcut them together I receive 0x00 instead of 0x80. Hereafter there is my code: ===================================== &sharpdefine SPISCK 3 // GPIOB &sharpdefine SPIMISO 4 // GPIOB &sharpdefine SPIMOSI 5 // GPIOB &sharpdefine A0 6 // GPIOB - SS0 &sharpdefine A1 7 // GPIOB - SS1 &sharpdefine A2 1 // GPIOB - SS2 MYSPI::MYSPI() { /* SPI PIN */ if(!(RCC->AHBENR & RCC_AHBENR_GPIOBEN)) RCC->AHBENR |= RCC_AHBENR_GPIOBEN; // Enable GPIOB clock GPIOB->MODER &= ~(0x03<<(2*SPISCK) | 0x03<<(2*SPIMISO) | 0x03<<(2*SPIMOSI)); //Alternate function GPIOB->MODER |= 0x02<<(2*SPISCK) | 0x02<<(2*SPIMISO) | 0x02<<(2*SPIMOSI); GPIOB->OSPEEDR &= ~(0x03<<(2*SPISCK) | 0x03<<(2*SPIMISO) | 0x03<<(2*SPIMOSI)); //Pin speed GPIOB->OSPEEDR |= 0x03<<(2*SPISCK) | 0x03<<(2*SPIMISO) | 0x03<<(2*SPIMOSI); GPIOB->PUPDR &= ~(0x03<<(2*SPISCK) | 0x03<<(2*SPIMISO) | 0x03<<(2*SPIMOSI)); //Pull up GPIOB->PUPDR |= 0x01<<(2*SPISCK) | 0x01<<(2*SPIMISO) | 0x01<<(2*SPIMOSI); GPIOB->OTYPER &= ~(0x01<<SPISCK | 0x01<<SPIMISO | 0x01<<SPIMOSI); //PushPull GPIOB->OTYPER |= 0x00<<SPISCK | 0x00<<SPIMISO | 0x00<<SPIMOSI; GPIOB->AFR[0] &= 0xFF000FFF; //Alternate function 0 (SPI1) // GPIOB->AFR[0] &= (uint32_t) ~(0xF<<(4*SPISCK) | 0xF<<(4*SPIMISO) | 0xF<<(4*SPIMOSI)); //Alternate function 0 (SPI1) (This line doesn't work) GPIOB->AFR[0] |= 0x00<<(4*SPISCK) | 0x00<<(4*SPIMISO) | 0x00<<(4*SPIMOSI); RCC->APB2ENR |= RCC_APB2ENR_SPI1EN; // Enable SPI1 clock SPI1->CR1 = 0x0000; // Disable and reset SPI SPI1->CR1 |= SPI_CR1_SSM | SPI_CR1_SSI | 0x02<<3 | SPI_CR1_MSTR | SPI_CR1_CPHA; // Bidirectional | Fclk/8 | Master | CPOL = 0 | CPHA = 1 SPI1->CR2 = 0x0700; // Reset CR2 SPI1->CR2 |= SPI_CR2_FRXTH | 0x07<<8; // RX full = 1/4 (8bit) | 8 bit SPI1->I2SCFGR &= (uint16_t)(~SPI_I2SCFGR_I2SMOD); // not found in DM00091010-ReferenceManual SPI1->CR1 |= SPI_CR1_SPE; // Enable SPI /* SLAVE SELECT PIN */ if(!(RCC->AHBENR & RCC_AHBENR_GPIOBEN)) RCC->AHBENR |= RCC_AHBENR_GPIOBEN; // Enable GPIOB clock (redundant) GPIOB->MODER &= ~(0x03<<(2*A0) | 0x03<<(2*A1) | 0x03<<(2*A2)); //Alternate function = output GPIOB->MODER |= 0x01<<(2*A0) | 0x01<<(2*A1) | 0x01<<(2*A2); GPIOB->OTYPER &= ~(0x01<<A0 | 0x01<<A1 | 0x01<<A2); //Output push-pull GPIOB->OTYPER |= 0x00<<A0 | 0x00<<A1 | 0x00<<A2; GPIOB->OSPEEDR &= ~(0x03<<(2*A0) | 0x03<<(2*A1) | 0x03<<(2*A2)); //Pin speed = low speed GPIOB->OSPEEDR |= 0x00<<(2*A0) | 0x00<<(2*A1) | 0x00<<(2*A2); GPIOB->PUPDR &= ~(0x03<<(2*A0) | 0x03<<(2*A1) | 0x03<<(2*A2)); //Pull up GPIOB->PUPDR |= 0x01<<(2*A0) | 0x01<<(2*A1) | 0x01<<(2*A2); } uint8_t MYSPI::Transfer8(uint8_t slave, uint8_t c, uint8_t cpol, uint8_t cpha) { uint8_t t; SPI1->CR1 &= ~(SPI_CR1_CPOL | SPI_CR1_CPHA); SPI1->CR1 |= cpol<<1 | cpha; // Set the correct CPOL and CPHA SPI1->CR2 &= ~(SPI_CR2_FRXTH | 0x0F00); // Reset CR2 SPI1->CR2 |= SPI_CR2_FRXTH | 0x07<<8; // RX full = 1/4 (8bit) | 8 bit SlaveLow(slave); while(SPI1->SR & SPI_SR_BSY); // Wait for SPI transmission finished SPI1->DR = c; while(SPI1->SR & SPI_SR_BSY); // Wait for SPI transmission finished t = SPI1->DR; SlaveHigh(slave); myusart1.PrintString((uint8_t *) ''SPI '', ' ', 0); myusart1.PrintHex(t); myusart1.PrintChar('\n'); return t; } ================================= This code on port A and discovery board works, so what's wrong with port B? Any hint? Thank you Freya #portb #spi2016-02-17 06:09 AM
Avoid changing CPOL/CPHA without disabling/enabling the SPI.
Did you look at the pins with an oscilloscope? Are you aware of the FIFO/data packing, and that assigning to/from SPI1->DR implies a 16-bit access (unless you use a nonstandard definition of SPI1)? JW2016-02-18 12:06 AM
Hi Jan,
I looked at pins with a scope (see annex) and I found 19 clock pulses for a 8-bit transfer. I wrote in CR2 8-bit, so why so may clock pulses? ________________ Attachments : NewFile1.bmp : https://st--c.eu10.content.force.com/sfc/dist/version/download/?oid=00Db0000000YtG6&ids=0680X000006I0xt&d=%2Fa%2F0X0000000bgM%2FY0mjCMs1_7nGEQJc7BzFE5sUorloKNAfLRvjiv25O10&asPdf=false2016-02-18 01:53 AM
See data packing in SPI chapter of RM.
You need to use an 8-bit write/read instruction, in C this is usually accomplished through the pointer-casting form of type punning.This has been discussed on this forum already, search for 'F0xx and SPI (use google, the native search is currently broken in this forum). And I repeat, avoid changing CPOL, CPHA without disabling/enabling SPI. JW2016-02-22 12:27 AM
Hi,
I added SPI disable and SPI enable instructions, but SPI continues to send 16 clock instead of 8. Hereafter my routine: =============================================== uint8_t MYSPI::Transfer8(uint8_t slave, uint8_t c, uint8_t cpol, uint8_t cpha) { uint8_t t; SPI1->CR1 &= ~SPI_CR1_SPE; // Disnable SPI SPI1->CR1 &= ~(SPI_CR1_CPOL | SPI_CR1_CPHA); // Clear CPOL and CPHA SPI1->CR1 |= cpol<<1 | cpha; // Set the correct CPOL and CPHA SPI1->CR2 = 0x1700; // In this way I am sure about value in CR2 (RX full = 1/4 (8bit) | 8 bit) SPI1->CR1 |= SPI_CR1_SPE; // Enable SPI while(SPI1->SR & 0x001) t = (uint8_t) SPI1->DR; // Wait for RX FIFO empty SlaveLow(slave); while(SPI1->SR & SPI_SR_BSY); // Wait for SPI transmission finished SPI1->DR = (uint8_t) c; // Transmit byte while(SPI1->SR & SPI_SR_BSY); // Wait for SPI transmission finished SlaveHigh(slave); /* DEBUG */ myusart1.PrintString((uint8_t *) ''SPI8 '', ' ', 0); myusart1.PrintHex(SPI1->CR1); // Let's check CR1 myusart1.PrintChar(' '); myusart1.PrintHex(SPI1->CR2); // Let's check CR2 myusart1.PrintChar(' '); myusart1.PrintHex(SPI1->SR); // Let's check SR myusart1.PrintChar('\t'); while(SPI1->SR & 0x0001) // Loop until RX fIFO empty { t = (uint8_t) SPI1->DR; myusart1.PrintHex(t); myusart1.PrintChar(' '); } myusart1.PrintHex(SPI1->SR); // Check once again SR myusart1.PrintChar('\n'); return (uint8_t) t; } =================================== and here is the result (PrintHex formats everything in 8 digits): SPI8 00000355 00001700 00000403 000000FF 000000FF 00000002 That is RX FIFO is empty after 2 read cycles (16 bits), but CR2 is programmed for 8 bits. May be instruction ''SPI1->DR = (uint8_t) c;''? But in this case how can I instruct TX FIFO for only 1 byte? Thank you Freya2016-02-23 11:39 PM
Hi to all,
problem solved! It was the ''data packing'', that is, if you send 8 bit or less and you access to DR register with 16 bits, then it is interpreted as 2 data to transmit (that explains the 16 clock pulses). As SPI1->DR is intrpretes ALWAYS as 16 bits and no cast can win on it, the solution I found is to define a 8 bit pointer and use that for writing data. Hereafter my working routine: uint8_t MYSPI::Transfer8(uint8_t slave, uint8_t c, uint8_t cpol, uint8_t cpha) { uint8_t t, *d; d = (uint8_t *) &(SPI1->DR); SPI1->CR1 &= ~SPI_CR1_SPE; // Disnable SPI SPI1->CR1 &= ~(SPI_CR1_CPOL | SPI_CR1_CPHA); SPI1->CR1 |= cpol<<1 | cpha; // Set the correct CPOL and CPHA SPI1->CR2 &= ~(SPI_CR2_FRXTH | 0x0F00); // Reset CR2 SPI1->CR2 |= SPI_CR2_FRXTH | 0x07<<8; // RX full = 1/4 (8bit) | 8 bit SPI1->CR1 |= SPI_CR1_SPE; // Enable SPI SlaveLow(slave); while(SPI1->SR & SPI_SR_BSY); // Wait for SPI transmission finished *d = c; // Write data to transmit while(SPI1->SR & SPI_SR_BSY); // Wait for SPI transmission finished SlaveHigh(slave); t = SPI1->DR; return (uint8_t) t; } Bye Freya