I have a STM32F103RE MCU that is connected to a fxos8700cq sensor through I2C2 port.
I've set up the peripherals using stm32cubemx and I'm trying to simply test the communication with the sensor. But as I debug the code, it seems that data transfer is not happening correctly.
These are the i2c settings:
hi2c2.Instance = I2C2;
hi2c2.Init.ClockSpeed = 100000;
hi2c2.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c2.Init.OwnAddress1 = 0;
hi2c2.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c2.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c2.Init.OwnAddress2 = 0;
hi2c2.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c2.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
and here is the main code:
uint8_t send_buffer[1];
uint8_t receive_buffer[1];
uint8_t FXOS_ADDR = (FXOS8700_DEVICE_ADDR_SA_11 << 1);
send_buffer[0] = FXOS8700_WHO_AM_I;
receive_buffer[0] = 0;
// chip select for fxos
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, GPIO_PIN_SET);
HAL_Delay(500);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, GPIO_PIN_RESET);
HAL_Delay(500);
ret = HAL_I2C_Master_Transmit(&hi2c2, FXOS_ADDR, send_buffer, 1, 100);
//ret = HAL_I2C_Master_Transmit_IT(&hi2c2, FXOS_ADDR, send_buffer, 1);
HAL_Delay(2);
ret = HAL_I2C_Master_Receive(&hi2c2, FXOS_ADDR, receive_buffer, 1, 100);
//ret = HAL_I2C_Master_Receive_IT(&hi2c2, FXOS_ADDR, receive_buffer, 1);
if (receive_buffer[0] == FXOS8700_WHO_AM_I_PROD_VALUE)
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);
else
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_10, GPIO_PIN_SET);
but after I debug the code, and watch the i2c registers after executing the HAL_I2C_Master_Transmit(&hi2c2, FXOS_ADDR, send_buffer, 1, 100); function I get these values. That means the TXE bit in SR1 register is not set.
I also tried using the interrupt mode but that's not working either. the values of i2c registers after the ret = HAL_I2C_Master_Transmit_IT(&hi2c2, FXOS_ADDR, send_buffer, 1); line are like this.
In all cases the functions return HAL_OK and there is no error.
I've tested the sensor and the board communication using mbed and it works fine. So the hardware has no problem.
You should not need to toggle a chip select when using the device's I2C interface.
The device's chip select signal is only for the SPI interface.
You should have the SA0 and SA1 pins pulled high or low to use the I2C interface and select the device's I2C address. See table 11 and section 10.2.3 in the datasheet.
HAL_I2C_Master_Transmit() and HAL_I2C_Master_Receive() are not the best functions for reading a register value from the device. These functions transmit a start condition, the device address, the data, and finally a stop condition. But this is not what the FXOS8700CQ datasheet recommends in section 10.1.2, "Single-byte read". It says transmit start condition, device address with W bit, register address, repeat-start condition (notice no stop before repeat start), device address with R bit, then the data, and finally a stop condition.
The HAL_I2C_Mem_Read() function can be used to perform the single-byte read as described in the datasheet. (i.e., write register address, repeat-start, read data.) Try something like this.
uint8_t data_byte = 0;
HAL_StatusTypeDef hal_status = HAL_I2C_Mem_Read(&hi2c2, FXOS_ADDR, FXOS8700_WHO_AM_I, I2C_MEMADD_SIZE_8BIT, &data_byte, 1, 100);
If that doesn't help, then check the return value from the HAL library calls.
If that doesn't help, then examine the I2C clock and data signals with a logic analyzer or oscilloscope.
Related
I am interfacing the BMA253 Accelerometer with STM32 Discovery Board using 4 wire SPI. I am using the CUBEMX HAL Library. As a first step, I am trying to read the CHIP ID from the sensor register 0x00. The chip which should be read is 0xFA. The following code is added in the while loop just for the purpose of verification. As SPI is a active low protocol, I have configured PIN_2 to be high by default.
uint8_t result = 0;
uint8_t address = 0x00;
while (1)
{
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
HAL_SPI_Transmit(&hspi1, &address, 1, 100);
HAL_Delay (100);
HAL_SPI_Receive(&hspi1, &result, 1, 100);
HAL_Delay (100);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
}
Since it is in the infinite while loop, for the first two loop cycles I am getting 0xFF in the result. Then in the next two loop cycles, I am getting the correct Chip Id which is 0xFA. Then I am getting 0xFF in the next two loop cycles and this pattern is alternating infinitely.
I am not able to understand why I am receiving 0xFF first and then 0xFA.
Could there be a problem with the delay mismatch? I feel that 100ms should be fine.
I also feel that the SPI Receive is being incorrectly implemented. I am worrying because since SPI Read is an important function in retrieving the acceleration data, this function is the key.
I request anyone to please suggest me on what to do to get it working perfectly. Any help would be highly appreciated.
Thanks in advance.
EDIT: WORKING NOW
1. Followed theSealion's suggestion to set the first bit high.
2. Additionally, I had to configure the CPOL and CPHA in the SPI Configuration to either Mode 0 or Mode 3 as per the sensor requirement.
Please try the follwoing to ready the Chip ID
#define READ_REGISTER 0x80
uint8_t result = 0;
uint8_t address = 0x00 + READ_REGISTER;
while (1)
{
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_RESET);
HAL_SPI_Transmit(&hspi1, &address, 1, 100);
HAL_SPI_Receive(&hspi1, &result, 1, 100);
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_2, GPIO_PIN_SET);
HAL_Delay (500);
}
According to the datasheet, if you want to read a register, the MSB of the command must be 1. And you do not need any within the communication.
Dear stack overflow users,
I have built a device with a master device, and a network of 10 slaves. All of them communicate via 4 wire SPI. Right now I am writing the program for both boards, and they don't seem to be working, I do not get expected responses.
I have a master board, and 10 of identical slave boards. The protocol is simple - as with SPI any transaction is initiated by the master device, and a command is sent. The selected slave then receives aforemetioned command, sets a busy flag pin high, and checks if it's valid. After parsing the command the busy bin is released, and if the command is valid, same byte as received is sent to the master, otherwise an error marker is sent. After that, any necessary data exchanges are executed. I've tried configuring the IO's as regular portf, and their Alternative Functions, also I tried resetting the SPI periph after each transaction and nothing seems to be working.
This is what I get:
https://imgur.com/a/MICEx2f
The channels are from the top, respectively:
MOSI,MISO,CLK, and busy flag. I get no response from the slave, no matter what. The command is interpreted correctly (debug data from UART), however nothing is sent back.
This is the SPI part of code for the SLAVE device:
uint8_t spi_sendrecv(uint8_t byte)
{
// poczekaj az bufor nadawczy bedzie wolny
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
SPI_I2S_SendData(SPI1, byte);
// poczekaj na dane w buforze odbiorczym
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
return SPI_I2S_ReceiveData(SPI1);
}
uint8_t SPI_get_cmd_ack(void)
{
uint8_t cmd;
uint8_t valid_flag;
//In cas if the BF pin was left high
BF_OUT_low();
//Let's wait for some data
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
cmd = SPI_I2S_ReceiveData(SPI1);
//cmd = SPI_get_command();
//Check the cmd
BF_OUT_high();
valid_flag = SPI_check_for_valid_cmd(cmd);
//SPI_reset_flush();
BF_OUT_low();
if(valid_flag == CMD_RET_STATUS_VALID)
{
spi_sendrecv(cmd);
return cmd;
}
else
{
spi_sendrecv(CMD_ERROR);
return CMD_ERROR;
}
}
And this is the MASTER part:
//Sends a command to a slave device
//Param1: slave device no, from 0 to 9
//Param2: command to send
//Retval: command send success or failure:
//DATA_TRANSFER_OK or DATA_TRANSFER_ERR
uint8_t SPI_send_command(uint8_t slave_no, uint8_t cmd)
{
uint8_t cnt = 0;
uint8_t rx_cmd;
//SPI_reset();
//Select the correct slave
SPI_select_slave(0);
delay_ms(0);
SPI_select_slave(slave_no);
delay_ms(0);
//Transmit the cmd
SPI_sendrecv(cmd);
//SPI_reset();
//Wait for the busy flag indication
while(SPI_get_busy_flag(slave_no) == Bit_RESET)
{
if(cnt < SPI_RETRY_COUNT)
{
++cnt;
delay_ms(1);
}
else
{
SPI_select_slave(0);
return DATA_TRANSFER_ERR;
}
}
//Same for the busy flag on:
while (SPI_get_busy_flag(slave_no) == Bit_SET)
{
if(cnt < SPI_RETRY_COUNT)
{
++cnt;
delay_ms(1);
}
else
{
SPI_select_slave(0);
return DATA_TRANSFER_ERR;
}
}
rx_cmd = SPI_sendrecv(0);
//SPI_reset();
if(rx_cmd == cmd) return DATA_TRANSFER_OK;
else return DATA_TRANSFER_ERR;
}
And here are the initialization parts of the code, slave and master respectively:
void SPI_init(void)
{
GPIO_InitTypeDef SPI_GPIO;
SPI_InitTypeDef SPI;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA | RCC_AHBPeriph_GPIOB | RCC_AHBPeriph_GPIOC, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
//GPIOA5 SCK
//GPIOA6 MISO
//GPIOA7 MOSI
SPI_GPIO.GPIO_Mode = GPIO_Mode_AF;
SPI_GPIO.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
SPI_GPIO.GPIO_PuPd = GPIO_PuPd_DOWN;
SPI_GPIO.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOA, &SPI_GPIO);
SPI_GPIO.GPIO_Pin = GPIO_Pin_15;
SPI_GPIO.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &SPI_GPIO);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource15, GPIO_AF_SPI1);
//Busy flag
SPI_GPIO.GPIO_Mode = GPIO_Mode_OUT;
SPI_GPIO.GPIO_OType = GPIO_OType_PP;
SPI_GPIO.GPIO_Pin = GPIO_Pin_5;
GPIO_Init(GPIOC, &SPI_GPIO);
/*SPI_GPIO.GPIO_Mode = GPIO_Mode_IN;
SPI_GPIO.GPIO_PuPd = GPIO_PuPd_UP;
SPI_GPIO.GPIO_Pin = GPIO_Pin_15;
GPIO_Init(GPIOA, &SPI_GPIO);*/
SPI.SPI_CPHA = SPI_CPHA_1Edge;
SPI.SPI_CPOL = SPI_CPOL_Low;
SPI.SPI_DataSize = SPI_DataSize_8b;
SPI.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI.SPI_FirstBit = SPI_FirstBit_MSB;
SPI.SPI_Mode = SPI_Mode_Slave;
SPI.SPI_NSS = SPI_NSS_Hard;
SPI_Init(SPI1, &SPI);
SPI_Cmd(SPI1, ENABLE);
SPI_aux_tim_conf();
}
static void SPI_IO_conf(void)
{
//Struct
GPIO_InitTypeDef SPI_IO;
//CLK
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOE, ENABLE);
//Conf
SPI_IO.GPIO_Mode = GPIO_Mode_AF;
//5 - SCK, 6 - MISO, 7- MOSI
SPI_IO.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7 | GPIO_Pin_6;
SPI_IO.GPIO_PuPd = GPIO_PuPd_DOWN;
SPI_IO.GPIO_OType = GPIO_OType_PP;
SPI_IO.GPIO_Speed = GPIO_Speed_25MHz;
//Init
GPIO_Init(GPIOA, &SPI_IO);
//Connect to SPI periph
GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1);
//For busy flag checking
SPI_IO.GPIO_Mode = GPIO_Mode_IN;
SPI_IO.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 |GPIO_Pin_12 |GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
SPI_IO.GPIO_PuPd = GPIO_PuPd_DOWN;
SPI_IO.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOE, &SPI_IO);
SPI_IO.GPIO_Pin = GPIO_Pin_10;
GPIO_Init(GPIOB, &SPI_IO);
}
static void SPI_periph_conf(void)
{
//Struct
SPI_InitTypeDef SPI_conf;
//CLK
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
//Conf
//SysClk = 84000000
//84/64 = 1,3125MHz
SPI_conf.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;
SPI_conf.SPI_CPHA = SPI_CPHA_1Edge;
SPI_conf.SPI_CPOL = SPI_CPOL_Low;
//SPI_conf.SPI_CRCPolynomial =
SPI_conf.SPI_DataSize = SPI_DataSize_8b;
SPI_conf.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_conf.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_conf.SPI_Mode = SPI_Mode_Master;
SPI_conf.SPI_NSS = SPI_NSS_Soft;
//Conf, enable
SPI_Init(SPI1, &SPI_conf);
SPI_Cmd(SPI1, ENABLE);
//SPI_Cmd(SPI1, DISABLE);
}
As You can see on the oscillogram, there is no response from the Slave, The expected response is the same command that was sent in the previous cycle by the master. Eg, I send a 0x01 presence command, and the slave should respond with the same byte, after that, any other exchanges should occur, which are not implemented yet.
Best regards, Marek
From your images it seems that CLK is kept low after sending data. In SPI the Master is the sole governor of the clock.
From STM32F411xC/E reference manual, p 578:
BUSY flag
This BSY flag is set and cleared by hardware (writing to this flag has no effect). The BSY flag indicates the state of the communication layer of the SPI.
When BSY is set, it indicates that the SPI is busy communicating. There is one exception in master mode / bidirectional receive mode (MSTR=1 and BDM=1 and BDOE=0) where the BSY flag is kept low during reception.
The BSY flag is useful to detect the end of a transfer if the software wants to disable the SPI and enter Halt mode (or disable the peripheral clock). This avoids corrupting the last transfer. For this, the procedure described below must be strictly respected.
The BSY flag is also useful to avoid write collisions in a multimaster system.
The BSY flag is set when a transfer starts, with the exception of master mode / bidirectional receive mode (MSTR=1 and BDM=1 and BDOE=0).
It is cleared:
when a transfer is finished (except in master mode if the communication is continuous)
when the SPI is disabledwhen a master mode fault occurs (MODF=1)
When communication is not continuous, the BSY flag is low between each communication.
When communication is continuous:
in master mode, the BSY flag is kept high during all the transfers
in slave mode, the BSY flag goes low for one SPI clock cycle between each transfer
Note:Do not use the BSY flag to handle each data transmission or reception. It is better to use the TXE and RXNE flags instead
So I think your waiting for busy flag in master after sending data can lock indefinitely. Try this (the code uses plain CMSIS, but it should be understandable):
GPIOB->BSRR |= GPIO_BSRR_BR6; //slave select
while(! (SPI1->SR & SPI_SR_TXE)); //wait for Tx buffer empty
SPI1->DR = 0x01; //send 0x01
while(! (SPI1->SR & SPI_SR_RXNE)); //wait for Rx buffer not empty (receive 0x0 sent by the slave during our sending 0x01 since it's 4-wire SPI)
uint8_t tmp = SPI1->DR; //we don't need that value, but need to read DR in order to reset RXNE flag
SPI1->DR = 0x0; //we need to trigger send in order to receive
while(! (SPI1->SR & SPI_SR_RXNE)); //wait for Rx buffer not empty (our response)
response = SPI1->DR;
while(SPI1->SR & SPI_SR_BSY); //now we can wait for SPI to end communications
GPIOB->BSRR |= GPIO_BSRR_BS6; //slave deselect
Thank You for the help. After long hours I managed to get it working, by resetting the SPI peripheral in th Slave device after each transaction:
void SPI_reset_flush(void)
{
//Reset the periph and registers
RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1, ENABLE);
SPI_aux_tim_wait();
RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1, DISABLE);
SPI_aux_tim_wait();
SPI_Cmd(SPI1, ENABLE);
}
12.04.2019
Actually, I think the mentioned solution is not the best. The problem was, I was not waiting for the SPI buffers to empty, this resulted in random data being send, and I lost synchronisation between devices. I since the rewrote the code, and stuck to the TX/RX procedures in a Reference Manuals.
Best Regards, Marek
As i was beginning with SPI FLASH from winbond W25Q32FV with STM32F103RCT6 CORTEX M3. I am facing a real issue of understanding how things should work.
I am using CUBEMX. First i have selected RCC as crystal/ceramic resonator and configured my clock to 72MHz. Then i configured SPI1 as FULL DUPLEX MASTER. There i got only 3 Pin (PA5 - SCK, PA6- MISO, PA7 - MOSI) so i configured CS pin as GPIO OUTPUT on PA2.
Now to write to flash? What is the first thing I need to do? What are the steps i need to follow?
As long as i refereed to the datasheet first i need to Enable Write(0x06). Then i need to send Page Program(0x02) and then i need to send 24 bit address. Then i need to send at least 1 byte of data. All these procedure will happen when CS is low and then after sending all this CS will be high.
Then i am disabling Write enable, i.e write Disable(0x04).
After then i am trying to read data from that address, So, I send Read Data(0x03) and 24bit address. Then Recieve data in buffer.
Here is the sample code:
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_RESET);
HAL_Delay(10);
SPI_TX_BUFF[0] = 0x06;
SPI_TX_BUFF[1] = 0x02;
SPI_TX_BUFF[2] = 0x00;
SPI_TX_BUFF[3] = 0x00;
SPI_TX_BUFF[4] = 0x01;
SPI_TX_BUFF[5] = 0x11;
HAL_SPI_Transmit(&hspi1, SPI_TX_BUFF, 6, 50);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_RESET);
HAL_Delay(100);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_RESET);
HAL_Delay(10);
SPI_TX_BUFF[0] = 0x04;
HAL_SPI_Transmit(&hspi1, SPI_TX_BUFF, 1, 50);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_RESET);
HAL_Delay(100);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_RESET);
HAL_Delay(10);
SPI_TX_BUFF[0] = 0x03;
SPI_TX_BUFF[1] = 0x00;
SPI_TX_BUFF[2] = 0x00;
SPI_TX_BUFF[3] = 0x01;
HAL_SPI_Transmit(&hspi1, SPI_TX_BUFF, 4, 50);
HAL_SPI_Receive(&hspi1, SPI_RX_BUFF, 1, 50);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_RESET);
HAL_Delay(100);
This code is not working. More over after flashing the code i can't even enter the debug mode. It says NO TARGET CONNECTED. I know i am doing something massively wrong and need a little guidance. I just need to know what are the steps involved to successfully initiate, write and read from spi flash.
Like I am confused with few stuffs
Here i am directly sending the Write enable as my first command. Here should i need to send the id first? I mean how to initiate and let know the MCU that he is having a flash connected to the spi pins.
2.How to send 24 bit address? What is the starting address i begin with to write data in flash?
3.When is flash a simple blinky. The MCU works fine but when i flash this code why the alert NO TARGET CONNECTED. Then i have to press reset and erase everything.
Any help will be appreciated.
Thank you in advance.
Edit:
The solution was to turn down the I2C clock in the initialization block. Although the STM could handle it, the data sheet for the LCD stated it could handle only up to 10kHz.
For the DMA there is an IRQ that must be enabled and setup in the CubeMX software that will enable DMA TX/RX lines.
I'm using an STM32 - Nucleo-F401RE board with freeRTOS. I've used freeRTOS a bit recently, but I have never really used I2C.
Trying to setup a simple LCD display using the I2C drivers that CubeMX generates.
So far it only sends about half the data I request to send.
I've tested it with the simple "show firmware" command and this works. So I can verify that the I2C instance is setup correctly.
/* I2C1 init function */
static void MX_I2C1_Init(void)
{
hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed = 100000;
hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c1) != HAL_OK)
{
Error_Handler();
}
}
It has international pull-up resistors set as well.
/* vdebugTask function */
void vdebugTask(void const * argument)
{
/* USER CODE BEGIN vdebugTask */
/* Infinite loop */
for(;;)
{
char hi[6];
hi[0] = 'h';
hi[1] = 'e';
hi[2] = 'y';
hi[3] = 'a';
hi[4] = 'h';
hi[5] = '!';
HAL_I2C_Master_Transmit_DMA(&hi2c1, (0x50), hi, 6);
vTaskDelay(10);
}
/* USER CODE END vdebugTask */
}
This is the code I am trying to run, I have not changed the HAL function at all. I don't think that it could be more simple than this, however this is all that happens.
I followed the timing constraints in the data sheet for the LCD, and the CubeMX software didn't warn or state anywhere that their I2C drivers had any special requirements. Am I doing something wrong with the program?
I have also tried using the non-DMA blocking mode polling transfer function that was also created by CubeMX
HAL_I2C_Master_Transmit(&hi2c1, (0x50), hi, 6, 1000);
This is even worse and just continuously spams the screen with unintelligible text.
The solution was to turn down the I2C clock in the initialization block. Although the STM could handle it, the data sheet for the LCD stated it could handle only up to 10kHz.
For the DMA there is an IRQ that must be enabled and setup in the CubeMX software that will enable DMA TX/RX lines.
Note that the clock still must adhere to the hardware limitations. I assume that roughly 20% less than maximum stated in the data sheet will suffice. For my LCD this means that I am going to be using 80kHz.
First go to configuration :
And then click on DMA to setup a DMA request. I've only selected TX as I don't care about an RX DMA from the LCD.
I'am using STM32F407vg and i'am trying to write data in SPI data register, the following code shows the configuration function
void init_SPI1(void){
GPIO_InitTypeDef GPIO_InitStruct;
SPI_InitTypeDef SPI_InitStruct;
// enable clock for used IO pins
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
/* configure pins used by SPI1
* PA4 = NSS
* PA5 = SCK
* PA6 = MISO
* PA7 = MOSI
*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_6 | GPIO_Pin_5|GPIO_Pin_4;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStruct);
// connect SPI1 pins to SPI alternate function
GPIO_PinAFConfig(GPIOA, GPIO_PinSource4, GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1);
//Set chip select high
GPIOA->BSRRL |= GPIO_Pin_4; // set PE4 high
// enable peripheral clock
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
/* configure SPI1 in Mode 0
* CPOL = 0 --> clock is low when idle
* CPHA = 0 --> data is sampled at the first edge
*/
SPI_StructInit(&SPI_InitStruct); // set default config
SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex; // set to full duplex mode, seperate MOSI and MISO lines
SPI_InitStruct.SPI_Mode = SPI_Mode_Master; // transmit in master mode, NSS pin has to be always high
SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b; // one packet of data is 8 bits wide
SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low; // clock is low when idle
SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge; // data sampled at first edge
SPI_InitStruct.SPI_NSS = SPI_NSS_Soft ; // set the NSS management to internal and pull internal NSS high
SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; // SPI frequency is APB2 frequency / 4
SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;// data is transmitted MSB first
SPI_Init(SPI1, &SPI_InitStruct);
SPI_Cmd(SPI1, ENABLE); // enable SPI1
}
and this is the function that send data
uint8_t SPI1_send(uint8_t data){
SPI1->DR = data; // write data to be transmitted to the SPI data register
while( !(SPI1->SR & SPI_I2S_FLAG_TXE) ); // wait until transmit complete
while( !(SPI1->SR & SPI_I2S_FLAG_RXNE) ); // wait until receive complete
while( SPI1->SR & SPI_I2S_FLAG_BSY ); // wait until SPI is not busy anymore
return SPI1->DR; // return received data from SPI data register
}
I think that i have set the good configuration (the choice of pins is not wrong in my opinion and also the configuration of the bus used ) the following pictures are extracted from the STM32F407 DataSheet
The problem is that the data in the DR registry is different than the data passed as argument .I don't know why this happen .Could anyone guide me to the right point
The SPI DR register is not a normal memory location where writing and reading access the same storage.
Instead, writing loads the output shift register, while reading reads the received input. Depending on device details, reading may also "claim" the input, clearing it from the register until another word is received.
For these reasons, trying to watch the SPI DR with a debugger is not only not going to give you the information you seek, it may even be damaging to the data you would otherwise receive.
After reading the SPI section from this book my problem is solved, i just connect SPI MOSI to SPI MISO pin (PA6 and PA7) then used the following function :
int spiReadWrite(SPI_TypeDef* SPIx, uint8_t *rbuf,
const uint8_t *tbuf, int cnt, enum spiSpeed speed)
{
int i;
SPIx->CR1 = (SPIx->CR1 & ~SPI_BaudRatePrescaler_256) |
speeds[speed];
for (i = 0; i < cnt; i++){
if (tbuf) {
SPI_I2S_SendData(SPIx, *tbuf++);
} else {
SPI_I2S_SendData(SPIx, 0xff);
}
while (SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_RXNE) == RESET);
if (rbuf) {
*rbuf++ = SPI_I2S_ReceiveData(SPIx);
} else {
SPI_I2S_ReceiveData(SPIx);
}
}
return i;
}
by using a debugger i can see that the data sent (from tbuf) are succesfully received (in rbuf)
note that SCLK_freq = APB2_freq / (BaudRatePrescaler + 1)