The receiving part of the SPI in STM32F0 is not working - stm32

The MCU I used is STM32F042K6T6. I have configured the SPI in bidirectional mode in order to read the sensor MLX90316. Now the read function is blocked in waiting the the RXNE signal. If I have understood correctly, once I have put the SPI into rx mode, the SCLK is automatically generated and the receiving procedure is started. But the RXNE is never set and nothing in RxFIFO. It seems that the receiving part of the SPI is not working at all. Even I put the MCU in full duplex mode, when I initial the data transmitting by writing the SPI1->DR, the data is transmitted successfully but the problem still remains.
The same code is tested on a STM32F405RGT6 and it functions correctly(if I don't care about the data because the GPIO is not configured). I totally have no idea about this strange problem...
Thank you in advance.:)
The Initial function
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
/* SPI configuration -------------------------------------------------------*/
SPI_I2S_DeInit(SPI1);
SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx; // Initially Tx
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; // Clock steady high
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; // Data write on rising (second) edge
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_LSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI1, &SPI_InitStructure);
SPI_RxFIFOThresholdConfig(SPI1, SPI_RxFIFOThreshold_QF);
SPI_Cmd(SPI1, ENABLE);
The tramsmission function is:
void spi_send_8bit(uint8_t Data){
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); //wait buffer empty
SPI_SendData8(SPI1, Data);
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_BSY) == SET); //wait finish sending}
uint8_t spi_recv_8bit(void){
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET) ; // wait data receive
return SPI_ReceiveData8(SPI1);}
static void Read(void){
MLX90316_1_Set(); //Pull down the CS line
SPI_BiDirectionalLineConfig(SPI1, SPI_Direction_Tx);
spi_send_8bit(0xAA);
spi_send_8bit(0xFF);
while (SPI_GetReceptionFIFOStatus(SPI1) != SPI_ReceptionFIFOStatus_Empty)
SPI_ReceiveData8(SPI1);
SPI_Cmd(SPI1, DISABLE);
SPI_BiDirectionalLineConfig(SPI1, SPI_Direction_Rx);
SPI_Cmd(SPI1, ENABLE);
Data[0] = spi_recv_8bit();
Data[1] = spi_recv_8bit();
Data[2] = spi_recv_8bit();
Data[3] = spi_recv_8bit();
SPI_BiDirectionalLineConfig(SPI1, SPI_Direction_Tx);
spi_send_8bit(0xFF);
spi_send_8bit(0xFF);
spi_send_8bit(0xFF);
spi_send_8bit(0xFF);
MLX90316_1_Cl();}

I had the same problem, I solved it by setting up FRXTH bit in SPI1->CR2 register.

This function won't work:
uint8_t spi_recv_8bit(void){
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET) ; // wait datareceive
return SPI_ReceiveData8(SPI1);}
You have to write a dummy value to DR register to trigger the CLK pulses in SPI master mode. You could use your spi_send_8bit() function to do that.

While this may be an "old" post now I found it so it may be of use to others, I also have a problem with RXNE on both an STM32F030 and 042.
I found single stepping (IAR IDE) that the CPU hangs at the RXNE poll loop:
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
I tried:
while (SPI_GetReceptionFIFOStatus(SYS_SPI) == SPI_ReceptionFIFOStatus_Empty);
Same result, the receiver doesn't let us know it's completed.
I put a breakpoint on the line following these, which logically would not have been reached- it is!!! Is this a SPI peripheral or debug issue perhaps.
RXNE's descriotion leaves a lot to be desired, it almost reads as it it's "FIFO ALMOST FULL"

I have the same problem related, the clock out is never sent, even after the instruction SPI2->CR1 &= ~SPI_CR1_BIDIOE;
My solution: Before the above instruction, put:
SPI2->CR1 |= SPI_CR1_RXONLY;
SPI2->CR1 &= ~SPI_CR1_RXONLY;
This outputs the clock and sets the bit SPI_FLAG_RXNE.
I already use the internal 48MHz clock.

Related

STM32 Keil - Can not access target while debugging (AT Command UART)

I am trying to communicate with GSM module via UART communication. I could get message from the module as I expected. However when it comes to while loop (it is empty), debug session ends with "can not access target" error. Stepo by step, I am going to share my code:
Function 1 is AT_Send. (Note: Some of variables are declared globally.)
int AT_Send(UART_HandleTypeDef *huart, ATHandleTypedef *hat, unsigned char *sendBuffer, uint8_t ssize, unsigned char *responseBuffer, uint8_t rsize) {
if (HAL_UART_Transmit_IT(huart,sendBuffer,ssize) != HAL_OK) {
return -1;
}
while ((HAL_UART_GetState(huart) & HAL_UART_STATE_BUSY_TX) == HAL_UART_STATE_BUSY_TX) {
continue;
}
//;HAL_Delay(1000);
if (strstr((char*)receiveBuffer,(char*)responseBuffer) != NULL) {
rxIndex = 0;
memset(command, 0, sizeof(command));
return 0;
}
rxIndex = 0;
memset(command, 0, sizeof(command));
return 1;
}
Second function is AT_Init function. It sends AT to get OK response. At this point on, if I am not wrong, I am opening receive interrrupt and I am trying to get 1 byte.
int AT_Init(UART_HandleTypeDef *huart, ATHandleTypedef *hat)
{
HAL_UART_Receive_IT(huart,&rData,1);
tx = AT_Send(huart,hat,"AT\r",sizeof("AT\r\n"),"OK\r\n",sizeof("OK\r\n"));
return tx;
}
After these two functions, I am calling receive IT function in the call back while there are data on the bus.
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART1){
command[rxIndex] = rData;
rxIndex++;
if((rxIndex == 2) && (strstr((char*)command,"\r\n") != NULL)) {
rxIndex = 0;
} else if (strstr((char*)command,"\r\n") != NULL) {
memcpy(receiveBuffer, command, sizeof(command));
rxIndex = 0;
memset(command,0,sizeof(command));
}
HAL_UART_Receive_IT(&huart1,&rData,1);
}
}
Moreover, I am going to send a few HTTP commands simultaneously if I can get rid of this problem.
Can anyone share his/her knowledge?
Edit: Main function is shown below
tx = AT_Init(&huart1,&hat);
while (1)
{
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_3);
HAL_Delay(500);
}
Edit 2: I had replaced uart channel by USART2, and debugger worked. I suppose that it is related to hardware. Still, I am curious about possible reasons that cause to this problem.
The question doesn't mention on which µC the program is running, I only see the "stm32" tag. Similarly, we don't know which debug protocol is used (JTAG or SWD?).
Still, I dare to guess that the toggle command for GPIO port PB3 in the main loop is causing the observations: On many (most? all?) STM32 controllers, PB3 is used as JTDO pin, which is needed for JTAG debug connections.
Please make sure to configure the debug connection to SWD (without SWO, i.e., neither SWV is correct). It may also help to check the wiring of the debug cable, the fast toggle at the PB3/JTDO line may influence the signal levels on some neighbouring SWD lines if the wiring is low quality or a fast SWD speed has been chosen.
My hypothesis can be falsified by removing all actions to PB3. If the problem remains, I'm wrong.

Unable to use PC15 as GPIO input on stm32f030rc

I'm working on a project using an stm32f030rc. I need to use PC15 as a GPIO input but it appears I'm unable to.
I understand the couple PC14/PC15 is shared with the LFE oscillator, but of course I'm not using that function. Moreover, I am able to read the correct pin level on the PC14 GPIO. In the datasheed regarding my model the PC15 pin is marked as a I/O with OSC32_OUT as additional function: can it be used as input at all?
For reference, this is the C code I'm using to test the functionality; I'm using libopencm3 for initialization.
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
static void clock_setup(void)
{
rcc_clock_setup_in_hsi_out_48mhz();
/* Enable GPIOA, GPIOB, GPIOC clock. */
rcc_periph_clock_enable(RCC_GPIOA);
rcc_periph_clock_enable(RCC_GPIOB);
rcc_periph_clock_enable(RCC_GPIOC);
rcc_periph_clock_enable(RCC_DBGMCU);
/* Enable clocks for GPIO port B and C*/
gpio_mode_setup(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO5);
gpio_mode_setup(GPIOC, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO15);
gpio_mode_setup(GPIOC, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO14);
}
int main(void)
{
unsigned long long i = 0;
clock_setup();
/* Blink the LED (PA8) on the board with every transmitted byte. */
while (1)
{
gpio_toggle(GPIOA, GPIO5); /* LED on/off */
for (i = 0; i < 400000; i++) /* Wait a bit. */
__asm__("nop");
// This conditional is never entered
if (gpio_get(GPIOC, GPIO15) == 0) {
__asm__("nop");
__asm__("nop");
__asm__("nop");
}
// This one works
if (gpio_get(GPIOC, GPIO14) == 0) {
__asm__("nop");
__asm__("nop");
__asm__("nop");
}
}
return 0;
}
PC14 & PC15 have the same configuration properties. Of course, there are some limitations regarding using these pins as outputs (including PC13), but it should be okay to use them as inputs as long as you don't activate LSE functionality.
PC14 & PC15 are GPIO inputs after power-up and considering that LSE is disabled by default, you should be able to use them directly even without any configuration.
As you don't have any problems with PC14, I suspect 3 possible causes:
1) A bug in the GPIO code the library provides. Although it's very unlikely, it's easy to test. You can remove the configuration code for PC14 & PC15, as they are GPIO inputs after power-up by default. This eliminates the possibility of having a bug in gpio_mode_setup() function. To avoid using gpio_get() function, you can use the following code:
if (GPIOC->IDR & (1 << 15) == 0)
2) A bug in the clock config code the library provides. Again, this one is very unlikely, but you can test it by removing the rcc_clock_setup_in_hsi_out_48mhz() function. MCU uses HSI running at 8 MHz after power-up.
3) This can be a hardware problem. I suggest checking the voltage on PC15. Test it by physically connecting it to GND. Also measure PC14 for comparison. Among these 3 possible causes I can think of, this is the most probable one.

Detecting CAN bus errors under socketCAN linux driver

Our products are using a well known CANopen stack, which uses socketCAN, on an embedded Beaglebone Black based system running under Ubuntu 14.04 LTS. But for some reason, even though the stack we're using will detect when the CAN bus goes into a PASSIVE state or even a BUS OFF state, it never indicates when the CAN bus recovers from errors and goes out of a PASSIVE or warning state, and enters a non error state.
If I were to query the socketCAN driver directly (via ioctl calls), would I be able to detect when the CAN bus goes in and out of a warning state (which is less than 127 errors), in and out of a PASSIVE state (greater than 127 errors) or goes BUS OFF (greater than 255 errors)?
I'd like to know if I'd be wasting my time doing this or is there a better way to detect, accurately and in real-time, all conditions of a CAN bus?
I have only a partial solution to that problem.
As you are using socketCAN, the interface is seen as a standard network interface, on which we can query the status.
Based on How to check Ethernet in Linux? (replace "eth0" by "can0"), you can check the link status.
This is not real-time, but can be executed in a periodic thread to check the bus state.
So while this is an old question, I just happened to stumble upon it (while searching for something only mildly related).
SocketCAN provides all the means for detecting error frames OOB.
Assuming your code looks similar to this:
int readFromCan(int socketFd, unsigned char* data, unsigned int* rxId) {
int32_t bytesRead = -1;
struct can_frame canFrame = {0};
bytesRead = (int32_t)read(socketFd, &canFrame, sizeof(can_frame));
if (bytesRead >= 0) {
bytesRead = canFrame.can_dlc;
if (data) {
memcpy(data, canFrame.data, readBytes);
}
if (rxId) {
*rxId = canFrame.can_id; // This will come in handy
}
}
return bytesRead;
}
void doStuffWithMessage() {
int32_t mySocketFd = fooGetSocketFd();
int32_t receiveId = 0;
unsigned char myData[8] = {0};
int32_t dataLength = 0;
if ((dataLength = readFromCan(mySocketFd, myData, &receiveId) == -1) {
// Handle error
return;
}
if (receiveId & CAN_ERR_MASK != 0) {
// Handle error frame
return;
}
// Do stuff with your data
}

STM32F7 + FatFs = FR_NOT_READY

I am now using CubeMx 4.23.0 and FW package for STM32F7 1.8.0
MCU is STM32F746 on Core746i board.
Everything is generated by CubeMx automatically.
main.c:
SCB_EnableICache();
SCB_EnableDCache();
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_SDMMC1_SD_Init();
MX_USB_DEVICE_Init();
MX_FATFS_Init();
HAL_Delay(3000);
DebugString("start OK");
uint8_t res = 0;
FATFS SDFatFs;
FIL MyFile; /* File object */
char SD_Path[4];
res = f_mount(&SDFatFs, (TCHAR const*)SD_Path, 0);
sprintf(DebugStr, "f_mount = 0x%02X", res);
DebugString(DebugStr);
res = f_open(&MyFile, "test.txt", FA_READ);
sprintf(DebugStr, "f_open = 0x%02X", res);
DebugString(DebugStr);
sdmmc.c:
void MX_SDMMC1_SD_Init(void)
{
hsd1.Instance = SDMMC1;
hsd1.Init.ClockEdge = SDMMC_CLOCK_EDGE_RISING;
hsd1.Init.ClockBypass = SDMMC_CLOCK_BYPASS_DISABLE;
hsd1.Init.ClockPowerSave = SDMMC_CLOCK_POWER_SAVE_DISABLE;
hsd1.Init.BusWide = SDMMC_BUS_WIDE_1B;
hsd1.Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_DISABLE;
hsd1.Init.ClockDiv = 7;
//HAL_SD_Init(&hsd1);
// ^^^^^ I also tried this here
//HAL_SD_ConfigWideBusOperation(&hsd1, SDMMC_BUS_WIDE_4B)
//^^^^ and this
}
In case of f_mount(&SDFatFs, (TCHAR const*)SD_Path, 0) <- with 1 here (forced mount), output is:
f_mount = 0x03
f_open = 0x01
With 0 (do not mount now) output is:
f_mount = 0x00
f_open = 0x03
0x03 value is FR_NOT_READY, but official info is pretty vague about it
Things I've tried:
Adding HAL_SD_Init(&hsd1) to MX_SDMMC1_SD_Init() since i didnt find where is SD card GPIO init happening.
2 GB noname SD card, 1 GB Transcend card.
Different hsd1.Init.ClockDiv 3 to 255.
Resoldering everything completely.
Switching to 4-bit wide bus using HAL_SD_ConfigWideBusOperation(&hsd1, SDMMC_BUS_WIDE_4B);
Turn on and off pullups.
But card still does not mount. It's formatted in FAT, working on a PC, files i've tried to open are exist, but empty.
How to get it to mount?
Thanks in advance!
there was the problem with exact version of cubemx.
updating stm32cubemx helped.
You can try
`f_mount(0, "path", 0);
` after the f_open call . it may work.
If the function with forced mounting failed with FR_NOT_READY, it means that the filesystem object has been registered successfully but the
volume is currently not ready to work
. The volume mount process will be attempted on subsequent file/directroy function.
If implementation of the disk I/O layer lacks asynchronous media change detection, application program needs to perform f_mount function after each media change to force cleared the filesystem object.
Changing all SDIO pins except SDIO_CK to pull-up according to This Topic works for me
Try commenting MX_USB_DEVICE_Init(), see what happens.

stm32f4xx HAL lib & PCF8457AT - no response to write

I have stm32f4-discovery kit and I want to try i/o expander for hd44870 LCD . I have PCF8574AT link to io example like mine 8-bit expander where i2c address is 0x3f (checked by i2c scanner) on hi2c3 hardware. For c/c++ use HAL libraries on Eclipse environment. Ok take look at code.
First I initialize i2c3 - like Datasheet 100kHz on SCL:
static void MX_I2C3_Init(void)
{
hi2c3.Instance = I2C3;
hi2c3.Init.ClockSpeed = 100000;
hi2c3.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c3.Init.OwnAddress1 = 0;
hi2c3.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c3.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c3.Init.OwnAddress2 = 0;
hi2c3.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c3.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c3) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
Then try to send data to I/O expander. But before that I check that i/o is ready to use:
result = HAL_I2C_IsDeviceReady(&hi2c3,0x3f , 2, 2);
if (result == HAL_BUSY)
{
HD44780_Puts(6, 1, "busy");
}else{
HD44780_Puts(6, 1, "ready");
uint8_t data_io = 0xff;
HAL_I2C_Master_Transmit(&hi2c3, 0x3f, data_io, 1, 100);
}
On a same expander nothing changes. Any ideas what is wrong or maybe i/0 expander is broken ?
Im not sure about HAL driver, really never used HAL. But I have touched pcf8574 IO expander. As you said, you have checked it with scanner and if you get address, line and device is OK. As I am not expert on I2C and HAL libs,I'll show my I2C driver it relies on STM32 standard periphery drivers and it worked for PCF8574 and various I2C devices. There is an example,snippet(blocking mode, not irq based):
Checking if IO is not busy.
while(I2C_GetFlagStatus(&I2Cx, I2C_FLAG_BUSY) == SET){
if((timeout--) == 0) return -ETIMEDOUT;
}
Generate start condition and set write condition(with address for write mode).
I2C_TransferHandling(&I2Cx, dev_addr, 1, I2C_SoftEnd_Mode, I2C_Generate_Start_Write);
while(I2C_GetFlagStatus(&I2Cx, I2C_ISR_TXIS) == RESET){
if((timeout--) == 0) return -ENODEV;
}
Now you can send data byte( it is your IO states), This function writes directly to I2C TX(transceiver) register :
I2C_SendData(&I2Cx, reg_addr);
while(I2C_GetFlagStatus(&I2Cx, I2C_ISR_TC) == RESET){
if((timeout--) == 0) return -EIO;
}
Generate reading condition and than read from PCF8574, data should be same as it was just written(if nothing toggles IO expander). Basically you can read byte or more bytes (depends on device). In your case PCF8574(8bit) gives only 1 byte.
I2C_TransferHandling(dev->channel,dev_addr, len, I2C_AutoEnd_Mode,I2C_Generate_Start_Read);
size_t i;
for(i=0;i<len;i++){
timeout = I2C_TIMEOUT;
while(I2C_GetFlagStatus(dev->channel, I2C_ISR_RXNE) == RESET){
if((timeout--) == 0) return -EIO;
}
data[i] = I2C_ReceiveData(dev->channel);
}
You can continue RW operations, or just simply wait till device automatically stop transition on line:
while(I2C_GetFlagStatus(&I2Cx, I2C_FLAG_STOPF) == RESET){
if((timeout--) == 0) return -EIO;
}
I2C_ClearFlag(&I2Cx, I2C_ICR_STOPCF);
This steps will write and read data. Anyway this chip has some tricky logic there, it more simplistic than it looks like.Actually it works just as simple OUTPUT. Extern input just triggers up PCF8574 pin and nothing more, no special configuration for input mode. For input monitor for sure use PCF8574 INT pin, PCF8574 will trigger INT pin.
For example:
If you want input pins, than just simply set pins to logic zero. And monitor INT pin,if change happens on input, INT pin will be triggered and you should read data via I2C .
For OUTPUT low just write zero's.
And for OUTPUT high set logic true.
You are using HAL so you have to read what happens inside HAL_I2C_Master_Transmit function. Do not forget that address is 7bit and first byte with address also includes R/W condition.First byte bit0 is R/W bit. So you should handle it.
for example with defines:
#define PCF8574_WRITE_ADRESS (0x40) /*for writing to chip*/
#define PCF8574_READ_ADRESS ((0x40)|0x01) /*for reading chip*/
Here is some links:
i2c explanations
this may help
Really nice guide!
Hope this will help to understand your problem and solve it.:)
thanks , Bulkin
I found obvious mistake . HAL libs do not i2c_address << 1 . I/YOU must put that in code not same result !
HAL_I2C_Master_Transmit(&hi2c3, (0x3f<<1), data_io, 1, 100);
or
$define i2c_address_write (0x3f <<1 )
HAL_I2C_Master_Transmit(&hi2c3, i2c_address_write , data_io, 1, 100);
to read :
$define i2c_address_read ((0x3f <<1) | 0x01)
HAL_I2C_Master_Transmit(&hi2c3, i2c_address_read , data_io, 1, 100);