2014-05-03 11:01 AM
2014-05-05 10:20 AM
Really, nobody has a clue?
This behaviour is really strange, because for the Rx to happen the Tx must work. Still, the data I write in the first loop doesn't end up in the SPI RAM. But data must be clocked in via Tx to retrieve the bytes, and I see data I wrote via standard polling to SPI before then.I tried a lot already but run out of ideas. Using spixbase or SPI1->DR doesn't make a difference; for both Tx and Rx or only the Tx DMA setup I end up with the same result.Enabling mem2mem bit makes the transfer not working at all (the idea was that this is the DMA software trigger which I need additionally to DMA_ENABLE according to the reference manual.) Also switching the spi_dma_tx() routine commands to enable spi_dma, then dma, then a while loop on SPI_BSY flag, then disabling spi_dma and dma doesn't deliver a good result, although the data shown changes to something like 0 0 2 0 0 1 0 0 0 ... randomly. This is also contradicting the idea to use the CPU while the data transfer takes place.I really want to manage using one of my STM32F103 dev boards with this project, although I already have a STM32F0-Disco and receive a STM32F407-Disco tomorrow.2014-05-05 04:49 PM
Really, nobody has a clue?
Forums can be like that, it's not as if you're a huge contributor yourself. You are mixing polled/dma, not sure how well that will work. Pins look set up Ok as best I can tell. You could check as GPIO. Not sure how effective a disable/enable of DMA is for resetting address/count You use BRR BSRR in RMW fashion, really you don't need to do that, a simple write of the bits you're interested in is sufficient.2014-05-05 10:49 PM
Thank you for the input, Clive.
I didn't mean to sound rude and impolite, I'm sorry. I thought it's a non-offending, non-swearing standard term in english (I'm no native speaker) which means ''Nobody has an idea?'' in more vivid language. Until now I was lurking in this forum, which was considered good style back in the last century on forums. I am active mostly in german forums as it is easier to help out in your native tongue and just started to post here (as I started looking at µCs only 8 weeks ago, first AVR, now Cortex/STM32, and struggle myself with STM32 I currently am not confident enough to be of help myself).I will rewrite the routines to use different buffer sizes and only use DMA transfer without polling, though I'm not sure this makes a difference - the polled Rx command with DMA after that and writing via polling still works. But maybe that is just a random effect?I also tried disabling/enabling SPI_I2S_DMA around the DMA enable/disable, that didn't change the result. I will try to write directly to the DMA registers to reset the address/counter. To be honest, I didn't reset the address until now at all; the results in the Rx buffer were pointing out that this seems unnecessary. But trying that is a good idea.I read that RMW is of course slower and not necessary, but I left it that way because I didn't consider the chip-select/deselect time critical and on other architectures it is necessary, so in a sense of readability it looked like the way to go. Will change that.Thank you again for giving some pointers where to go on!2014-05-08 10:47 AM
Ok, tried the suggested changes to completely change to DMA without any polling.
uart_tx(
''Starting program.
''
);
// Check the RAM first
for
(
int
bank=0; bank<5; bank++) {
bool
is_ok=
true
;
chip_select(bank);
// Send WRITE MODE register command to chip
buffer1_tx[0]=WRMR;
buffer1_tx[1]=SEQUENTIAL_MODE;
spi1_dma_tx(2);
chip_deselect();
chip_select(bank);
// Send WRITE command to chip
buffer1_tx[0]=WRITE;
// as default sequential mode, thus address has to be sent only at start
buffer1_tx[1]=0;
//send even MoreMSByte address first
buffer1_tx[2]=0;
//send MSByte address first
buffer1_tx[3]=0;
//send LSByte address
spi1_dma_tx(4);
for
(
int
i=0; i<buffer_size; i++) {
buffer1_tx[i]=i;
buffer1_rx[i]=0;
}
for
(
int
i=0; i < (ram_size/buffer_size); i++) {
spi1_dma_tx(buffer_size);
}
chip_deselect();
chip_select(bank);
// Send WRITE MODE register command to chip
buffer1_tx[0]=WRMR;
buffer1_tx[1]=SEQUENTIAL_MODE;
spi1_dma_tx(2);
chip_deselect();
chip_select(bank);
// Send READ command to chip
buffer1_tx[0]=READ;
// as default sequential mode, thus address has to be sent only at start
buffer1_tx[1]=0;
//send even MoreMSByte address first
buffer1_tx[2]=0;
//send MSByte address first
buffer1_tx[3]=0;
//send LSByte address
spi1_dma_tx(4);
for
(
int
i=0; i<buffer_size; i++) {
buffer1_tx[i]=0xFF;
buffer1_rx[i]=0;
}
for
(
int
i=0; i < (ram_size/buffer_size); i++) {
spi1_dma_tx(buffer_size);
for
(
int
i=0; i<buffer_size; i++) {
if
(buffer1_rx[i] != i) is_ok=
false
;
}
}
sprintf(text,
''RAM chip %i is %s -> ''
, bank, (is_ok?
''OK.''
:
''not OK!''
));
uart_tx(text);
for
(
int
i=0; i<10; i++) {
sprintf(text,
''[%i] %i ''
, i, buffer1_rx[i]);
uart_tx(text);
}
uart_tx(
''
''
);
chip_deselect();
}
and
void
spi1_dma_tx(
int
size) {
SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Rx | SPI_I2S_DMAReq_Tx, DISABLE);
DMA_Cmd(DMA_Channel_SPI1_TX, DISABLE);
DMA_Cmd(DMA_Channel_SPI1_RX, DISABLE);
DMA_SetCurrDataCounter(DMA_Channel_SPI1_RX, size);
DMA_SetCurrDataCounter(DMA_Channel_SPI1_TX, size);
DMA_Channel_SPI1_TX->CMAR=(uint32_t)buffer1_tx;
SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Rx | SPI_I2S_DMAReq_Tx, ENABLE);
DMA_Cmd(DMA_Channel_SPI1_RX, ENABLE);
DMA_Cmd(DMA_Channel_SPI1_TX, ENABLE);
}
also resetting the base address of the transfer buffer; additionally tried to disable and enable SPI->DMA. I also tried using ''transfer size -1'' just in case, which doesn't change anything though.
The programm now immediatly stalls after
Starting program.
I even built a LED Cube yesterday just to have a little success with working with these cute µCs. _That_ works at least 😉
I'm still lost. What else could I try?
Edit: I just stumbled upon these App notes for STM32F0xx - how to use DMA:http://www.icbase.com/Promotion/download/AN4pdf
In that SPI example, they use TIM2 for generating a 4kHz PWM which is used as trigger for DMA. In the examples on the net I saw nothing like this. Is it necessary to use TIM2 to generate somehting like my SPI clock frequency for DMA to start? Or is that just misleading?
2014-05-09 10:59 AM
A little progress - by shifting some lines of code around, the program starts again at least.
// Reset SPI Interface
SPI_I2S_DeInit(SPI1);
// SPI initial setup - 23LC1024 SPI mode 0
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
// SPI_Mode 0 / High=1
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
// SPI_Mode 0 / 2EDGE=1
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
// set the NSS management to internal | SPI_NSSInternalSoft_Set and pull internal NSS high
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_Init(SPI1, &SPI_InitStructure);
// for SPI transfers, get the function address for pointer arithmetics
get_spixbase();
// ----------------------------------------------------------------------------
// DMA RX SPI1 channel
DMA_DeInit(DMA_Channel_SPI1_RX);
// DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI1->DR;
DMA_InitStructure.DMA_PeripheralBaseAddr = spixbase;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)buffer1_rx;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = buffer_size;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA_Channel_SPI1_RX, &DMA_InitStructure);
// DMA_Cmd(DMA_Channel_SPI1_RX, ENABLE);
// DMA TX SPI1 channel
DMA_DeInit(DMA_Channel_SPI1_TX);
// DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI1->DR;
DMA_InitStructure.DMA_PeripheralBaseAddr = spixbase;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)buffer1_tx;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_BufferSize = buffer_size;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_Low;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA_Channel_SPI1_TX, &DMA_InitStructure);
// DMA_Cmd(DMA_Channel_SPI1_TX, ENABLE);
SPI_Cmd(SPI1, ENABLE);
// tell SPI1 to use DMA
SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Rx | SPI_I2S_DMAReq_Tx, ENABLE);
// Interrupt at DMA transfer complete
DMA_ITConfig(DMA_Channel_SPI1_TX, DMA_IT_TC, ENABLE);
DMA_ITConfig(DMA_Channel_SPI1_RX, DMA_IT_TC, ENABLE);
void
spi1_dma_tx(
int
size) {
DMA_Cmd(DMA_Channel_SPI1_TX, DISABLE);
DMA_Cmd(DMA_Channel_SPI1_RX, DISABLE);
DMA_SetCurrDataCounter(DMA_Channel_SPI1_RX, size);
DMA_SetCurrDataCounter(DMA_Channel_SPI1_TX, size);
// DMA_Channel_SPI1_TX->CMAR=(uint32_t)buffer1_tx;
DMA_Cmd(DMA_Channel_SPI1_RX, ENABLE);
DMA_Cmd(DMA_Channel_SPI1_TX, ENABLE);
}
void
DMA1_Channel2_IRQHandler(
void
){
if
(DMA_GetITStatus(DMA1_IT_TC2))
DMA_ClearFlag(DMA1_FLAG_TC2);
// while( !(SPI1->SR & SPI_I2S_FLAG_RXNE) ); // wait until receive complete
}
void
DMA1_Channel3_IRQHandler(
void
){
if
(DMA_GetITStatus(DMA1_IT_TC3))
DMA_ClearFlag(DMA1_FLAG_TC3);
// while( !(SPI1->SR & SPI_I2S_FLAG_RXNE) ); // wait until receive complete
}
When the waiting-for-SPI-transfer-ready loop is removed, the program runs, even with starting the RAM check completely in DMA. The results are wrong though:
Starting program.
RAM chip 0 is not OK! -> [0] 255 [1] 255 [2] 255 [3] 255 [4] 255 [5] 255 [6] 255 [7] 255 [8] 255 [9] 255
RAM chip 1 is not OK! -> [0] 255 [1] 255 [2] 255 [3] 255 [4] 255 [5] 255 [6] 255 [7] 255 [8] 255 [9] 255
RAM chip 2 is not OK! -> [0] 255 [1] 255 [2] 255 [3] 255 [4] 255 [5] 255 [6] 255 [7] 255 [8] 255 [9] 255
RAM chip 3 is not OK! -> [0] 255 [1] 255 [2] 255 [3] 255 [4] 255 [5] 255 [6] 255 [7] 255 [8] 255 [9] 255
RAM chip 4 is not OK! -> [0] 255 [1] 255 [2] 255 [3] 255 [4] 255 [5] 255 [6] 255 [7] 255 [8] 255 [9] 255
[..]
Reading COM11 again from OV7670: [143] -> Prescaler 16, frequency 500 kHz.
Select first RAM chip.
Writing image succeeded! Wrote 614400 bytes, used 5 RAM chips.
Image dimension: 640 x 480 pixel. 10 bytes captured:
[101]: 159 [102]: 207 [103]: 162 [104]: 222 [105]: 64 [106]: 221 [107]: 123 [108]: 217 [109]: 72 [110]: 215
The results at bottom are via SPI polling an correct. Maybe there is some special order I have to adhere to so that the transfers work?
2014-05-11 12:31 AM
A little progress as I see _some_ data, but not the proper result yet:
Starting program.
RAM chip 0 is not OK! -> [0] 254 [1] 126 [2] 255 [3] 127 [4] 128 [5] 0 [6] 129 [7] 1 [8] 130 [9] 2
RAM chip 1 is not OK! -> [0] 254 [1] 126 [2] 255 [3] 127 [4] 128 [5] 0 [6] 129 [7] 1 [8] 130 [9] 2
RAM chip 2 is not OK! -> [0] 254 [1] 126 [2] 255 [3] 127 [4] 128 [5] 0 [6] 129 [7] 1 [8] 130 [9] 2
RAM chip 3 is not OK! -> [0] 254 [1] 126 [2] 255 [3] 127 [4] 128 [5] 0 [6] 129 [7] 1 [8] 130 [9] 2
RAM chip 4 is not OK! -> [0] 123 [1] 234 [2] 109 [3] 228 [4] 143 [5] 232 [6] 73 [7] 238 [8] 108 [9] 216
I changed the DMA-Transfer routine to disable and enable SPI completely:
void
spi1_dma_tx(
int
size) {
while
(!(SPI1->SR & SPI_SR_TXE));
// Wait for bus free
while
(SPI1->SR & SPI_SR_BSY);
// tell SPI1 not to use DMA
SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Rx | SPI_I2S_DMAReq_Tx, DISABLE);
SPI_Cmd(SPI1, DISABLE);
DMA_Cmd(DMA_Channel_SPI1_TX, DISABLE);
DMA_Cmd(DMA_Channel_SPI1_RX, DISABLE);
DMA_SetCurrDataCounter(DMA_Channel_SPI1_RX, size);
DMA_SetCurrDataCounter(DMA_Channel_SPI1_TX, size);
// DMA_Channel_SPI1_TX->CMAR=(uint32_t)buffer1_tx;
DMA_Cmd(DMA_Channel_SPI1_RX, ENABLE);
DMA_Cmd(DMA_Channel_SPI1_TX, ENABLE);
// tell SPI1 to use DMA
SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Rx | SPI_I2S_DMAReq_Tx, ENABLE);
SPI_Cmd(SPI1, ENABLE);
}
Edit: Will clean up the first posts a little, noone wants to read the whole program code for sure!
2014-05-13 02:55 AM
I added a global variable which is true during DMA transfers and gets set to false on the Transfer Complete interrupts. I use this for example in chip_select() and deselect() so the transfer has a chance to complete before I lift the CS pin. This is also checked before a new transfer gets started.
Interestingly, this doesn't help either; the result stays the same.2014-05-13 09:25 PM
Logic Analyzer at 16 MHz ...
2014-05-14 01:30 PM
If anybody is interested to the solution - it is quite simple: Wait for the damn transfer to finish before writing to the buffers again.
// Check the RAM first
for
(
int
bank=0; bank<5; bank++) {
bool
is_ok=
true
;
chip_select(bank);
// Send WRITE command to chip
buffer1_tx[0]=WRITE;
// as default sequential mode, thus address has to be sent only at start
buffer1_tx[1]=0;
//send even MoreMSByte address first
buffer1_tx[2]=0;
//send MSByte address first
buffer1_tx[3]=0;
//send LSByte address
spi1_dma_tx(4);
while
(transfer);
for
(
int
i=0; i<buffer_size; i++) {
buffer1_tx[i]=i;
buffer1_rx[i]=0;
}
for
(
int
i=0; i < (ram_size/buffer_size); i++) {
spi1_dma_tx(buffer_size);
}
chip_deselect();
chip_select(bank);
// Send READ command to chip
buffer1_tx[0]=READ;
// as default sequential mode, thus address has to be sent only at start
buffer1_tx[1]=0;
//send even MoreMSByte address first
buffer1_tx[2]=0;
//send MSByte address first
buffer1_tx[3]=0;
//send LSByte address
spi1_dma_tx(4);
while
(transfer);
for
(
int
i=0; i<buffer_size; i++) {
buffer1_tx[i]=0xff;
buffer1_rx[i]=0;
}
for
(
int
i=0; i < ((ram_size/buffer_size)); i++) {
spi1_dma_tx(buffer_size);
while
(transfer);
for
(
int
i=0; i<buffer_size; i++) {
if
(buffer1_rx[i] != i) {
is_ok=
false
;
sprintf(text,
''[%i]!=%i ''
, i, buffer1_rx[i]);
uart_tx(text);
}
}
}
sprintf(text,
''RAM chip %i is %s!
''
, bank, (is_ok?
''OK''
:
''NOT OK''
));
uart_tx(text);
chip_deselect();
}
At SPI_Prescaler_8 this results in 1 wrong byte in the last four chips.
Setting prescaler to 4 leads to this:
Starting program.
RAM chip 0 is OK!
RAM chip 1 is OK!
RAM chip 2 is OK!
RAM chip 3 is OK!
RAM chip 4 is OK!