SPI read failing on STM32F4 device - arm

I'm using the STM32F4 trying to read over SPI. Debug environments is Visual Studio with the VisualGDB plugin.
The spi config is as follows. 8-bit frames, MSB first, using GPIOA/B for the SPI signals
void spiInit() {
RCC->AHB1ENR |= 3; //enable gpioA/B clock
RCC->APB2ENR |= 0x1000; //enable SPI1 clock (bit 12)
// configure A5 for SCLK
GPIOA->MODER &= ~0x00000C00; //clear pin mode for pin 5
GPIOA->MODER |= 0x00000800; //alternate function mode for pin 5
GPIOA->AFR[0] &= ~0x00F00000; //clear alternate mode
GPIOA->AFR[0] |= 0x00500000; //setup pins 5 for AF5
// configure B4 for MISO
GPIOB->MODER &= ~0x00000300; //clear pin mode for pin 4
GPIOB->MODER |= 0x00000200; //alternate function mode for pin 5
GPIOB->AFR[0] &= ~0x000F0000; //clear alternate mode
GPIOB->AFR[0] |= 0x00050000; //setup pins 4 for AF5
// configure B5 for MOSI
GPIOB->MODER &= ~0x00000C00; //clear pin mode for pin 5
GPIOB->MODER |= 0x00000800; //alternate function mode for pin 5
GPIOB->AFR[0] &= ~0x00F00000; //clear alternate mode
GPIOB->AFR[0] |= 0x00500000; //setup pins 5 for AF5
// configure A15 for output SS
GPIOA->MODER &= ~0xC0000000; //clear PA15 function bits
GPIOA->MODER |= 0x40000000; //set PA15 (SS) as output
SPI1->CR1 = 0x31E; //8-bit frames
SPI1->CR2 = 0; //mostly read/status/interrupt config
SPI1->CR1 |= 0x40; //enable SPI1
GPIOA->BSRR = 0x00008000; //deassert SS
}
Writes are fine, data is loaded correctly to slave and it responds as expected, reads from the slave however are not working. I can see that MISO looks good on the bus when i look at the waveforms, but it's not picked up by the Master.
After a few attempts at watching the SPI status-register flags, (getting the results described above) I'm using a utility function that i saw on another SO question (here). The data always reads 0 and i am confused, the code waits for the receive buffer to fill, but there seems to nothing there when it is read.
uint8_t SPI_ReadWrite8(SPI_TypeDef *spi, uint8_t data)
{
while(!(spi -> SR & SPI_SR_TXE)); //Wait for tx buffer to empty
*(volatile uint8_t *)&spi->DR = data; //Send data
while (!(spi -> SR & SPI_SR_RXNE)); //Wait for rx buffer to fill
return *(volatile uint8_t *)&spi->DR;
}
Any ideas what i'm missing?

Related

STM32f401xB/C - no PWM signal on PA15

I have a STM32F401xB/C board.
I am trying to create a PWM signal for my DC motors. I have followed this tutorial and seem to understand the code.
https://ruturajn.hashnode.dev/generating-pwm-pulses-on-the-stm32f407-for-servo-motor-control-using-bare-metal-programming
But after I change the pin I want the PWM output from I get no signal. The tutorial refrences the PA5 pin, which works, but PA15 does not work even though it is connected to the same timer TIM2 and channel.
Any idea?
This is my code:
//initialises the GPIO pins
void GPIO_Init(){
//give and clock to the GPIOB and GPIOA device
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
//THESE PINS ARE THE PWM DRIVERS
//PA15
//set alternative mode
GPIOA->MODER &= ~(GPIO_MODER_MODER15_1);
GPIOA->MODER |= GPIO_MODER_MODER15_1;
//low part of the alternate function register
GPIOA->AFR[1] |= GPIO_AFRH_AFSEL15_0;
}
//initialise the TIM2 timer device
void TIM2_Init(){
//give pwr and clk to TIM2 device
RCC->AHB1ENR |= RCC_APB1ENR_TIM2EN;
//set prescaler to 1Mhz = 1 microSeconds
TIM2->PSC = 16-1;
//total period of the timer = 20ms
TIM2->ARR = 20000;
//set counter to 0
TIM2->CNT = 0;
//set capture/compare mode register 1 to PWM Mode 1
TIM2->CCMR1 = 0x0060;
//set capture/compare enable register to output on chanel 1
TIM2->CCER |= 1;
//set >50% power
TIM2->CCR1 = 10000;
}
void setup(){
//set the timer to 16 mhz
RCC->CFGR |= 0 << 10;
GPIO_Init();
TIM2_Init();
//start TIM2 timer
TIM2->CR1 |= 1;
}
This only clears a single bit
//set alternative mode
GPIOA->MODER &= ~(GPIO_MODER_MODER15_1);
...
Should be
GPIOA->MODER &= ~(GPIO_MODER_MODE15_Msk);
...
PA15 is shared with JTDI and could have external interference.
By default it has pull-up enabled, that should be cleared if this pin is used as an output.
The problem was that I was not setting the clock and power properly for the clock. The register I should have checked is RCC->APB1ENR instead of RCC->AHB1ENR. The fact that I got power through PA5 was a coincidence.

STM32F302R8. TIM2 as output and TIM15 as input does not work for me

I am learning to use the STM32F302R8 board and I have this problem.
TIM2 is configured with toggle output CH1 on PA0. (This works fine).
TIM15 is configured as input capture CH1 on PA2.
I have a jumper between PA0 and PA2.
The aim is that when TIM2 reaches the value of CCR1 it triggers the PA0 pin, which happens, and having the jumper with the PA2 pin should trigger the TIM15 input but this does not happen.
The while that checks the CC1IF flag never ends, it doesn't detect anything.
What can it be?
while (1)
{
// wait until input edge is captured
while ( !(TIM15->SR & TIM_SR_CC1IF)) {}
timestamp = TIM15->CCR1; // read captured counter value
}
void mi_GPIO_Init ( void ) {
RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN;
GPIOA->MODER &= ~GPIO_MODER_MODER0; // clear PA0 mode
GPIOA->MODER |= GPIO_MODER_MODER0_1; // set pin to alt function
GPIOA->AFR[0] &= ~GPIO_AFRL_AFRL0; // clear pin AF bits
GPIOA->AFR[0] |= 0x0000001; // set pin to AF1 for TIM2_CH1
GPIOA->MODER &= ~GPIO_MODER_MODER2; // clear PA2 mode
GPIOA->MODER |= GPIO_MODER_MODER2_1; // set pin to alt function
GPIOA->AFR[0] &= ~GPIO_AFRL_AFRL2; // clear pin AF bits
GPIOA->AFR[0] |= 0x0000900; // set pin to AF9 for TIM15_CH1
// Configure TIM2 to wrap around at 1 Hz and toggle CH1 output when the counter value is 0
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; // enable TIM2 clock
TIM2->PSC = 800 - 1; // divided by 800
TIM2->ARR = 10000 -1; // divided by 10000
TIM2->CCMR1 = TIM_CCMR1_OC1M_0 | TIM_CCMR1_OC1M_1; // set output to toggle on match
// The rest of the bits are set to 0.
TIM2->CCR1 = 0; // set match value
TIM2->CCER |= TIM_CCER_CC1E; // enable CH1 compare mode
TIM2->CNT = 0; // clear timer counter
TIM2->CR1 |= TIM_CR1_CEN; // enable TIM2
// Configure TIM15 to do input capture
RCC->APB2ENR |= RCC_APB2ENR_TIM15EN; // enable TIM15 clock
TIM15->PSC = 8000 - 1; // divided by 8000
TIM15->ARR = 0xFFFF; // count until ARR
TIM15->CCMR1 &= ~TIM_CCMR1_CC1S; // set CH1 to capture at every edge
TIM15->CCMR1 |= TIM_CCMR1_CC1S_0; // CC1 as input, IC1 is mapped on TI1
TIM15->CCER |= TIM_CCER_CC1E; // enable CH1 capture rising edge
TIM15->CR1 |= TIM_CR1_CEN; // enable TIM15
}
I moved the clock trigger lines to the beginning of the code and instead of PA2 I use PB14 and now it works fine.

How do I take nRF24l01(slave) Register data using stm32f103 (master) through MISO pin

I'm trying to make a custom library for nRF24l01 for a stm32f103 target device, and I am writing code for a primary TX device.
And here I'm trying to read the register contents of nRF by sending the R_REGISTER command along with the address I am looking for, but I'm not able to figure out how to read the data after the R_REGISTER command is transmitted.
And i'm using the standard stm32f10x.h header file which comes along with startup files on Kiel uVision5.
Here are the configurations,
clock setup
RCC->CR |= RCC_CR_HSION; //HSI on
while( !(RCC_CR_HSIRDY & (RCC->CR)) ); //wait till its ready
//clocks for peripherals
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; //enable clock forport A
RCC->APB2ENR |= RCC_APB2ENR_AFIOEN; //enable clock for alternate functions
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN; //enable clock for SPI1
GPIO setup
these are my custom-defined functions, they just work fine
//GPIO pin setup as alternate function
pin_mode(IOPA, GPIOA, 7, op_50MHz, op_afpp); //MOSI pin as GPIO alternate_pin can run upto 50MHz
pin_mode(IOPA, GPIOA, 6, ip, ip_pupd); //MISO pin as GPIO alternate_pin can run upto 50MHz
pin_mode(IOPA, GPIOA, 5, op_50MHz, op_afpp); //SCK pin as GPIO alternate_pin can run upto 50MHz
pin_mode(IOPA, GPIOA, 4, op_50MHz, op_gppp); //CS pin as GPIO general_puspose_pin can run upto 50MHz
SPI setup
SPI1->CR1 |= SPI_CR1_MSTR; //master mode
SPI1->CR1 |= SPI_CR1_BR_0 | SPI_CR1_BR_1 | SPI_CR1_BR_2; //at 571Kbps, max 31Mbps
SPI1->CR1 |= SPI_CR1_SSI; //Software slave management enabled
SPI1->CR2 |= SPI_CR2_SSOE; //SS o/p enable
SPI1->CR1 |= SPI_CR1_SPE; //turn on the SPI
Im stuck here
uint8_t SPI_read_uint8_t(uint8_t addr){
uint8_t reg_val;
//sending the read command first along with address where we are reading from
delay_us(50);
digital_writepin(GPIOA, 4, LOW);
SPI1->DR = (R_REGISTER | addr); //sending the R_REGISTER command along with address
while( (SPI1->SR) & (SPI_SR_BSY) );
//please help here, how do I read the Register data from MISO pin
uint8_t spi_read_write(uint8_t data)
{
while(SPI1 -> SR & SPI_SR_RXNE) (void)*(volatile uint8_t *)&SPI1 -> DR; //clean the FIFO
*(volatile uint8_t *)&SPI1 -> DR = data;
while(!(SPI1 -> SR & SPI_SR_RXNE));
return *(volatile uint8_t *)&SPI1 -> DR;
}
uint8_t youroperation(uint8_t command, uint16_t *data)
{
uint8_t status;
setCSLine();
status = spi_read_write(command);
*data = spi_read_write(0);
*data |= ((uint16_t)spi_read_write(0)) << 8;
clearCSLine();
return status
}
youroperation will return Sn status register
command parameter means Cn command bits
data will contain the Dx data bits
To read data from SPI slave you need to send dummy bytes because master provides the clock for the slave.

STM32L011K4 with DMA RX using I2C data not appearing in buffer

I am trying to query a temperature sensor (TMP007) over an I2C bus using the L011K4's DMA controller. I've managed to get TMP007 to respond with good data, but that data isn't appearing in the buffer array that I pass my I2C DMA receive function.
What I expect to happen is that the I2C hardware is set up by the software in the Init and Read function, then the start condition is generated, the slave's address and register address are sent, then a second start/a restart is sent out followed by the slave's address again, and finally ending with a transmission of two bytes from TMP007 and a stop condition.
The data acquired from the temperature sensor should be appearing in a buffer that is passed into Start_Read (as MemoryBaseAddress), but that data does not appear when viewing the processor in debug mode after the read function has been called.
The Channel 3 transfer complete interrupt is being called, so I know the DMA has activated and managed the RX transfer, but the data doesn't appear in the desired buffer.
Here's an image of the communication being observed by a logic analyzer: https://i.imgur.com/SzySsXl.png
DIO2 is connected to the onboard LED, which tells us that the DMA transfer has completed (it's the only ISR with the LED toggle).
Any help would be greatly appreciated!
STM32L011K4 Datasheet: https://www.st.com/content/ccc/resource/technical/document/datasheet/42/c0/ab/e5/71/7a/47/0b/DM00206508.pdf/files/DM00206508.pdf/jcr:content/translations/en.DM00206508.pdf
STM32L011K4 Reference Manual: https://www.st.com/resource/en/reference_manual/dm00108282-ultralowpower-stm32l0x1-advanced-armbased-32bit-mcus-stmicroelectronics.pdf
void I2C1_DMA_Start_Read(int SlaveAddress, int RegisterAddress, int* MemoryBaseAddress, int BufferSize) {
I2C1->CR1 &= ~I2C_CR1_PE; // Disable I2C1
DMA1_Channel3->CCR &= ~DMA_CCR_EN; // Disable Channel 3 DMA
DMA1_Channel3->CCR &= ~(0x00007FFF); // Channel 3 DMA mask
// Configure DMA Channel 3 for 16 bit memory and peripheral, and other aliased settings (reference manual page 249, 10.4.3)
DMA1_Channel3->CCR |= (0b11 << 12)|(0b01 << 10)|(0b01 << 8)|DMA_CCR_MINC|DMA_CCR_TEIE|DMA_CCR_TCIE;
DMA1_Channel3->CPAR = (uint32_t) &(I2C1->RXDR);
DMA1_Channel3->CMAR = (uint32_t) MemoryBaseAddress;
DMA1_Channel3->CNDTR = (uint16_t) BufferSize;
DMA1_Channel3->CCR |= DMA_CCR_EN; // Activate DMA channel 3
I2C1->CR1 |= I2C_CR1_NACKIE; // Enable no acknowledge interrupt for I2C1
I2C1->CR2 |= ((uint8_t) (SlaveAddress << 1)); // Set up the slave address
I2C1->CR2 &= ~I2C_CR2_RD_WRN; // Master requests a write transfer in order to send the register
I2C1->CR1 |= I2C_CR1_PE|I2C_CR1_RXDMAEN; // Enable I2C1 and RX DMA for I2C1
I2C1->CR2 &= ~(0xFF << I2C_CR2_NBYTES_Pos); // Mask byte count
I2C1->CR2 |= (0x01 << I2C_CR2_NBYTES_Pos); // Transfer one byte (the slave's register address)
while(!(I2C1->ISR & I2C_ISR_TXE)); // Wait until TX data register is empty
I2C1->TXDR = RegisterAddress; // Set the transmit data to the desired register address
I2C1->CR2 |= I2C_CR2_START; // Generate start condition
while(I2C1->CR2 & I2C_CR2_START); // Wait until hardware clears the start bit
while(!(I2C1->ISR & I2C_ISR_TC)); // Wait until hardware clears the transmit complete bit
I2C1->CR2 |= I2C_CR2_RD_WRN; // Master requests a read transfer after sending over the register address
I2C1->CR2 &= ~(0xFF << I2C_CR2_NBYTES_Pos); // Mask byte count
I2C1->CR2 |= (0x02 << I2C_CR2_NBYTES_Pos); // Master looks for two bytes from the slave
I2C1->CR2 |= I2C_CR2_START; // Generate start condition
while(I2C1->CR2 & I2C_CR2_START); // Wait until hardware clears the start bit
return;
}
void DMA1_Channel2_3_IRQHandler() {
if(DMA1->ISR & DMA_ISR_TCIF3) {
GPIOB->ODR ^= 1 << 3; // Toggle the onboard LED as an indicator
I2C1->CR2 |= I2C_CR2_STOP; // Generate stop condition
while(I2C1->CR2 & I2C_CR2_STOP); // Wait until hardware clears the stop bit
DMA1->IFCR |= DMA_IFCR_CTCIF3; // Clear transfer complete interrupt bit
I2C1->CR1 &= ~I2C_CR1_PE; // Disable I2C1
DMA1_Channel3->CCR &= ~DMA_CCR_EN; // Disable Channel 3 DMA
} else if(DMA1->ISR & DMA_ISR_TEIF3) {
I2C1->CR2 |= I2C_CR2_STOP; // Generate stop condition (if I2C comm is already stopped on error, delete this and following line)
while(I2C1->CR2 & I2C_CR2_STOP); // Wait until hardware clears the stop bit
DMA1->IFCR |= DMA_IFCR_CTEIF3; // Clear transfer error interrupt bit
I2C1->CR1 &= ~I2C_CR1_PE; // Disable I2C1
DMA1_Channel3->CCR &= ~DMA_CCR_EN; // Disable Channel 3 DMA
}
return;
}
void I2C1_IRQHandler() {
if(I2C1->ISR & I2C_ISR_NACKF) {
//GPIOB->ODR ^= 1 << 3; // Toggle the onboard LED as an indicator
I2C1->ICR |= I2C_ICR_NACKCF;
}
return;
}

How to get a PWM signal on port D6 (PE_9) on a STM board?

I'm programming a board and want a PWM signal to appear on a pin to drive a LED. I'm using a STM32 NUCLEO-F207ZG board, and only low-level register programming. It is not working.
I've looked into the manual, datasheet and application note. Also some Google searches have been done.
//Enable timer 1 clock:
RCC->APB2ENR |= BIT0;
//Output mode on PWM
TIM1->CCMR1 |= BIT5 | BIT6;
//Period:
TIM1->ARR = 0x0000FFFF;
//Duty cycle:
TIM1->CCR1 = 0x00007FFF;
//Enable preload
TIM1->CCMR1 |= BIT3;
TIM1->CR1 |= BIT7;
//Enable CC1 output
TIM1->CCER |= BIT0;
//Enable timer
TIM1->CR1 |= BIT0;
//Enable GPIOE clock
RCC->AHB1ENR |= BIT4;
//Alternate function mode voor pin PE_9
GPIOE->MODER |= BIT19;
GPIOE->AFR[1] |= BIT4;
I expect a PWM signal on pin D6 (PE_9), to drive a LED. But the LED doesn't seem to do anything.
I didn't check your code bit by bit, but it seems OK in general. But I suspect a possible cause of the problem: Normally you should wait a few clock cycles before accessing any peripheral just after enabling its clock. I may be wrong, but it's possible that these 2 lines of code are ignored by the peripherals, as they are executed just after enabling the clocks:
TIM1->CCMR1 |= BIT5 | BIT6;
GPIOE->MODER |= BIT19;
I suggest using a debugger to check if all the peripheral registers are loaded with the correct values.
I also suggest trying other PWM channels. There may be some conflicts with the pin you're using because of the board hardware configuration.

Resources