STM32F469 with AS4C4M32S SDRAM only able to write first byte - stm32

I have made a custom board that features a STM32F469NI MCU coupled with AS4C4M32S SDRAM.
Following are the SDRAM timing and register set information:
hsdram1.Instance = FMC_SDRAM_DEVICE;
/* hsdram1.Init */
hsdram1.Init.SDBank = FMC_SDRAM_BANK1;
hsdram1.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_8;
hsdram1.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_12;
hsdram1.Init.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_32;
hsdram1.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4;
hsdram1.Init.CASLatency = FMC_SDRAM_CAS_LATENCY_3;
hsdram1.Init.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE;
hsdram1.Init.SDClockPeriod = FMC_SDRAM_CLOCK_PERIOD_2;
hsdram1.Init.ReadBurst = FMC_SDRAM_RBURST_DISABLE;
hsdram1.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_0;
/* SdramTiming */
SdramTiming.LoadToActiveDelay = 2;
SdramTiming.ExitSelfRefreshDelay = 7;
SdramTiming.SelfRefreshTime = 4;
SdramTiming.RowCycleDelay = 6;
SdramTiming.WriteRecoveryTime = 2;
SdramTiming.RPDelay = 2;
SdramTiming.RCDDelay = 2;
if (HAL_SDRAM_Init(&hsdram1, &SdramTiming) != HAL_OK)
{
Error_Handler( );
}
/* USER CODE BEGIN FMC_Init 2 */
FMC_SDRAM_CommandTypeDef cmd = { 0 };
cmd.CommandMode = FMC_SDRAM_CMD_CLK_ENABLE;
cmd.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
cmd.AutoRefreshNumber = 1;
cmd.ModeRegisterDefinition = 0;
HAL_SDRAM_SendCommand(&hsdram1, &cmd, 0xFFFF);
HAL_Delay(100);
cmd.CommandMode = FMC_SDRAM_CMD_PALL;
cmd.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
cmd.AutoRefreshNumber = 1;
cmd.ModeRegisterDefinition = 0;
HAL_SDRAM_SendCommand(&hsdram1, &cmd, 0xFFFF);
cmd.CommandMode = FMC_SDRAM_CMD_AUTOREFRESH_MODE;
cmd.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
cmd.AutoRefreshNumber = 8;
cmd.ModeRegisterDefinition = 0;
HAL_SDRAM_SendCommand(&hsdram1, &cmd, 0xFFFF);
cmd.CommandMode = FMC_SDRAM_CMD_LOAD_MODE;
cmd.CommandTarget = FMC_SDRAM_CMD_TARGET_BANK1;
cmd.AutoRefreshNumber = 1;
cmd.ModeRegisterDefinition = (uint32_t) 0x230; //also tried 0x231
HAL_SDRAM_SendCommand(&hsdram1, &cmd, 0xFFFF);
//Setup SDRAM Refresh Timer Register with the delay between refresh cycles of the SDRAM, this is SDRAM specific.
HAL_SDRAM_ProgramRefreshRate(&hsdram1, 1404); //Tried 1386
This same code works when running on a STM32F469-I DISCOVERY Board but fails when It is run on my custom carrier board.
On my board, whenever I write to any SDRAM address, only the first byte is READ/WRITTEN to, for example when I write 0x00 to address 0xC0000000, the data at that address when monitored under debugging contains 0x48129100 i.e first 3 BYTES are random garbage and only the last byte is written correctly(0x00), and suppose If i write 0xAA to the same address the data there when monitored contains the value 0x481291AA.
This is true to every address that I read/write to/from the external SDRAM.
Only the last byte is written/read.
The custom board schematic and PCB layout is identical to the STM32F469-I DISCO board.
Only difference is the RAM chip which instead of being MICRON one is Alliance Memory's AS4C4M32S .
What can be the issue of such a problem?

Reading SDRAM always uses at the full bit width, but writing requires to set the four signals BL0, BL1, BL2 and BL3 to say which bytes to write.
It sounds like only BL0 is currently working. Perhaps BL1-3 are not physically connected (hardware) or perhaps you just forgot to configure them (software).
(If it turns out to be a hardware problem then someone with the appropriate permissions will want to move this question and answer to the electronics stack exchange)

Related

Using STM32 FMC HAL driver with parallel DAC

Im trying to generate sinusoidal signal with STM32f767 and DAC8412. DAC have 12 bit data bus and 2 bit address to select one of the four analog outputs. I've configuried FMC in CubeIDE for a SRAM memory with 16 bit data and 2 bit addres. I was able to create buffer with 4096 integer values of sin(). Then i've tried to write them to addres 0x60000000, but it only writes 4 values. After that, program goes to HardFault_Handler().
#define SRAM_BANK_ADDR ((uint32_t)0x60000000)
#define RESOLUTION_T ((uint32_t)0x1000)
#define RESOLUTION_Y ((uint32_t)0x1000)
uint32_t aTxBuffer[RESOLUTION_T];
uint32_t address = SRAM_BANK_ADDR;
Thats how i try to send data to DAC:
for (uint32_t i = 0; i<RESOLUTION_T; i++ )
{
*(__IO uint32_t*) address = aTxBuffer[i];
}
Thats how i fill buffer:
static void Fill_Buffer(uint32_t *pBuffer, uint32_t res_T, uint32_t res_Y)
{
uint32_t tmpIndex = 0;
double sinVal;
/* Put in global buffer different values */
for (tmpIndex = 0; tmpIndex < res_T; tmpIndex++ )
{
sinVal = round((sin(M_TWOPI*tmpIndex/res_T)+1)*res_Y/2);
pBuffer[tmpIndex] = sinVal;
}
}

How to interact with the Linux PCA953X.c driver? How does one utilize this driver?

I have a PCA9535 GPIO Expander board connected through I²C to my Raspberry Pi. I am able to set the GPIO expander output pins (to high) using the i2cset command:
sudo i2cset 1 0x20 0x02 0xFF // 0x20 (gpio expander), and register 0x02
I came across a kernel driver for the PCA953X and loaded the kernel module gpio-pca953x.ko + modified the /boot/config.txt to include the dts overlay for the expander. When I run i2detect -y 1 (for i2c-1 bus), I can see "UU" at the 0x20 slave address, which corroborates that the driver is managing it.
If I wanted to do something comparable to what I did with the i2cset command programmatically, what API / interface should I use such that I make use of the PCA953X driver that's installed? As you can see from the code below, nothing there suggests that I would be utilizing PCA953X:
int file;
int adapter_nr = 1;
char filename[20];
snprintf(filename, 19, "/dev/i2c-%d", adapter_nr);
file = open(filename, O_RDWR);
if (file < 0) {
exit(1);
}
// Writes 0xFF to register 0x02
uint8_t cmd[2] = {0x02, 0XFF};
struct i2c_msg msg = {
.addr = 0x20,
.flags = 0,
.len = sizeof(cmd)/sizeof(uint8_t),
.buf = cmd
};
struct i2c_rdwr_ioctl_data tx = {
.msgs = &msg,
.nmsgs = 1
};
ioctl(file, I2C_RDWR, &tx);
Is there a programming route that involves utilizing the PCA953X driver? What does having that module actually do?
Thanks to #TomV for pointing it out in the comments. The PCA953X driver provides a new character device in the form of "gpiochipN". In my case, it was gpiochip2. I had not noticed this extra gpiochip in /dev as I was not looking for it. Because this is a character device, I was able to use ioctl to interact with the lines:
int fd, ret;
fd = open("/dev/gpiochip2", O_RDONLY);
if (fd < 0) {
printf("Unable to open: %s", strerror(errno));
return;
}
struct gpiohandle_request req;
req.lineoffsets[0] = 0;
req.lineoffsets[1] = 1;
req.lineoffsets[2] = 2;
req.lineoffsets[3] = 3;
req.lineoffsets[4] = 4;
req.lineoffsets[5] = 5;
req.lineoffsets[6] = 6;
req.lineoffsets[7] = 7;
req.lines = 8;
req.flags = GPIOHANDLE_REQUEST_OUTPUT;
ret = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &req);
if (-1 == ret) {
printf("Failed to get line handle:%s\n", strerror(ret));
close(fd);
return;
}
// Sets all 8 lines to high (equivalent to setting register 0x3 to 0b11111111 or 0xFF)
struct gpiohandle_data data;
data.values[0] = 1;
data.values[1] = 1;
data.values[2] = 1;
data.values[3] = 1;
data.values[4] = 1;
data.values[5] = 1;
data.values[6] = 1;
data.values[7] = 1;
ret = ioctl(req.fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &data);
if (-1 == ret) {
printf("Failed to set line value\n");
}
else {
close(req.fd);
}
close(fd);
The above code interacts with the gpiochip2 character device, which is made possible by having the PCA953X installed. Without it, I would have to read and write to the registers through the i2c bus as was posted in my original question.

Set CAN Filter to a specific ID STM32

I need to set a CAN Filter to ID "$7E8". But I need to set a ID High, Low and a Mask.
FilterConfig.FilterIdHigh = 0x0000;
FilterConfig.FilterIdLow = 0x0000;
FilterConfig.FilterMaskIdHigh = 0x0000;
FilterConfig.FilterMaskIdLow = 0x0000;
I found this Article: https://schulz-m.github.io/2017/03/23/stm32-can-id-filter/
And tried that code:
FilterConfig.FilterIdHigh = 0x00000008;
FilterConfig.FilterIdLow = 0x00000008;
FilterConfig.FilterMaskIdHigh = 0x1FFFFFFF;
FilterConfig.FilterMaskIdLow = 0x1FFFFFFF;
But it doesn't recognise a message sent with $7E8
The following works for me. id0 and id1 are two message IDs you want to allow (in ID list mode, you always set two per filter bank). If you want to set just one, I assume you can repeat the first one.
CAN_FilterTypeDef filter;
filter.FilterActivation = ENABLE;
filter.FilterFIFOAssignment = CAN_FILTER_FIFO0;
// Allow two IDs per entry
filter.FilterScale = CAN_FILTERSCALE_16BIT;
filter.FilterMode = CAN_FILTERMODE_IDLIST;
// Mask seems to have no effect when using CAN_FILTERMODE_IDLIST
filter.FilterMaskIdHigh = 0x0000;
filter.FilterMaskIdLow = 0x0000;
filter.FilterIdLow = id0;
filter.FilterIdHigh = id1;
filter.FilterBank = 0;
if (HAL_CAN_ConfigFilter(hcan, &filter)) {
Error_Handler();
}
2 corrections:
When using CAN_FILTERSCALE_16BIT in combination with CAN_FILTERMODE_IDLIST the mask high and mask low variables also contain 11-bit-IDs. You can set a filter bank for 4 11-bit-IDs with ID list mode this way.
The ID needs to be shifted to the correct position in the corresponding register. This is different for 16 Bit and 32 Bit scale. For 32 bit scale refer to the microcontroller reference manual. For 16 bit scale here is an example:
CAN_FilterTypeDef tsCanFilter = { 0 };
tsCanFilter.FilterActivation = ENABLE;
tsCanFilter.FilterFIFOAssignment = CAN_FILTER_FIFO0;
tsCanFilter.FilterScale = CAN_FILTERSCALE_16BIT;
tsCanFilter.FilterMode = CAN_FILTERMODE_IDLIST;
tsCanFilter.FilterIdLow = u32Id1 << 5u;
tsCanFilter.FilterMaskIdLow = u32Id2 << 5u;
tsCanFilter.FilterIdHigh = u32Id3 << 5u;
tsCanFilter.FilterMaskIdHigh = u32Id4 << 5u;
tsCanFilter.FilterBank = 0u;
if ( HAL_CAN_ConfigFilter ( &tsCanHandle, &tsCanFilter ) != HAL_OK )
{
// error handling
}

STM32F3xx CAN (controller area network) Receive

I am working with CAN of STM32F303K8. I can transmit data but I can't receive data; the function HAL_CAN_Receive() returns HAL_TIMEOUT. I am working with 1M and 32 clock.
can any one tell me how we use HAL_CAN_Receive() to receive data frome CAN
In order to receive CAN messages on STM32 you have to enable at least one filter.
The message is compared with all filters, and it needs to match at least one filter to be put in a fifo.
You can make a filter that matches all messages.
sFilterConfig.FilterNumber = 0;
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK;
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT;
sFilterConfig.FilterIdHigh = 0x0000;
sFilterConfig.FilterIdLow = 0x0000;
sFilterConfig.FilterMaskIdHigh = 0x0000;
sFilterConfig.FilterMaskIdLow = 0x0000;
sFilterConfig.FilterFIFOAssignment = 0;
sFilterConfig.FilterActivation = ENABLE;
sFilterConfig.BankNumber = 14;

AudioQueue Recording Audio Sample

I am currently in the process of building an application that reads in audio from my iPhone's microphone, and then does some processing and visuals. Of course I am starting with the audio stuff first, but am having one minor problem.
I am defining my sampling rate to be 44100 Hz and defining my buffer to hold 4096 samples. Which is does. However, when I print this data out, copy it into MATLAB to double check accuracy, the sample rate I have to use is half of my iPhone defined rate, or 22050 Hz, for it to be correct.
I think it has something to do with the following code and how it is putting 2 bytes per packet, and when I am looping through the buffer, the buffer is spitting out the whole packet, which my code assumes is a single number. So what I am wondering is how to split up those packets and read them as individual numbers.
- (void)setupAudioFormat {
memset(&dataFormat, 0, sizeof(dataFormat));
dataFormat.mSampleRate = kSampleRate;
dataFormat.mFormatID = kAudioFormatLinearPCM;
dataFormat.mFramesPerPacket = 1;
dataFormat.mChannelsPerFrame = 1;
// dataFormat.mBytesPerFrame = 2;
// dataFormat.mBytesPerPacket = 2;
dataFormat.mBitsPerChannel = 16;
dataFormat.mReserved = 0;
dataFormat.mBytesPerPacket = dataFormat.mBytesPerFrame = (dataFormat.mBitsPerChannel / 8) * dataFormat.mChannelsPerFrame;
dataFormat.mFormatFlags =
kLinearPCMFormatFlagIsSignedInteger |
kLinearPCMFormatFlagIsPacked;
}
If what I described is unclear, please let me know. Thanks!
EDIT
Adding the code that I used to print the data
float *audioFloat = (float *)malloc(numBytes * sizeof(float));
int *temp = (int*)inBuffer->mAudioData;
int i;
float power = pow(2, 31);
for (i = 0;i<numBytes;i++) {
audioFloat[i] = temp[i]/power;
printf("%f ",audioFloat[i]);
}
I found the problem with what I was doing. It was a c pointer issue, and since I have never really programmed in C before, I of course got them wrong.
You can not directly cast inBuffer->mAudioData to an int array. So what I simply did was the following
SInt16 *buffer = malloc(sizeof(SInt16)*kBufferByteSize);
buffer = inBuffer->mAudioData;
This worked out just fine and now my data is of correct length and the data is represented properly.
I saw your answer, there also is an underlying issue which gives wrong sample data bytes which is because of an endian issue of bytes being swapped.
-(void)feedSamplesToEngine:(UInt32)audioDataBytesCapacity audioData:(void *)audioData {
int sampleCount = audioDataBytesCapacity / sizeof(SAMPLE_TYPE);
SAMPLE_TYPE *samples = (SAMPLE_TYPE*)audioData;
//SAMPLE_TYPE *sample_le = (SAMPLE_TYPE *)malloc(sizeof(SAMPLE_TYPE)*sampleCount );//for swapping endians
std::string shorts;
double power = pow(2,10);
for(int i = 0; i < sampleCount; i++)
{
SAMPLE_TYPE sample_le = (0xff00 & (samples[i] << 8)) | (0x00ff & (samples[i] >> 8)) ; //Endianess issue
char dataInterim[30];
sprintf(dataInterim,"%f ", sample_le/power); // normalize it.
shorts.append(dataInterim);
}