Skocz do zawartości

STM32F4 SPI full duplex problem


davidpi

Pomocna odpowiedź

Cześć. Potrzebuję Waszej pomocy w takiej sprawie.

Mam dwa mikrokontrolery STM32F407VGT6 komunikujące się po SPI. W obydwu korzystam z SPI2.

W układzie slave odbiór danych zrealizowany jest za pomocą DMA w trybie circular. DMA wrzuca dane do bufora kołowego. Wysyłanie ze slava również zrobione za pomocą DMA w trybie circular z osobnym buforem. Co jakiś czas zaglądam do bufora odbiorczego i sprawdzam czy są tam jakieś dane. Oraz co jakiś czas wrzucam do bufora nadawczego dane do wysłania.

Układ master ma możliwość podłączenia 5 układów slave. Na razie testowałem tylko z jednym.

W układzie master nadawanie jest zrobione bez DMA i bez przerwań, natomiast odbiór zrobiony jest z DMA i buforem kołowym, tak jak w układzie slave.

I teraz na czym polega problem: Cała komunikacja jest ustawiona na full duplex. Ale jeżeli układ slave nic nie wysyła, lub jego bufor nadawczy jest pusty (same znaki NULL) to wszystko działa OK. Natomiast jeżeli slave zacznie coś wysyłać to jednocześnie dana przychodzące do niego zaczynają być zakłócane i komunikacja zaczyna szwankować. Czy ktoś z Was miał podobny problem?. Sprawdźcie, proszę, czy mój kod jest sensowny i czy w ogóle takie podejście do tej komunikacji jest sensowne.

Poniżej kody dla slava i mastera. Całość pisana w Keil uVision. Oczywiście taktowanie dla wszystkich układów włączone. Procesor pracuje na 168MHz. Problemy występują niezależnie od prędkości SPI. SPI na porcie B na pinach PB12 do PB15.

Układ slave:

static void COM_SPIConf(void)
{
DMA1_Stream3->PAR 	= (uint32_t)&SPI2->DR;
DMA1_Stream3->M0AR 	= (uint32_t)pC->bufread.buf;
DMA1_Stream3->NDTR 	= (uint16_t)BUFR_LEN;
DMA1_Stream3->CR 		= DMA_SxCR_MINC | DMA_SxCR_CIRC | DMA_SxCR_EN;

DMA1_Stream4->PAR 	= (uint32_t)&SPI2->DR;
DMA1_Stream4->M0AR 	= (uint32_t)pC->bufwrite.buf;
DMA1_Stream4->NDTR 	= (uint16_t)BUFW_LEN;
DMA1_Stream4->CR 		= DMA_SxCR_MINC | DMA_SxCR_CIRC | DMA_SxCR_DIR_0 | DMA_SxCR_EN;

GPIOB->AFR[1]		|= 0x55550000;
GPIOB->MODER		|= GPIO_MODER_MODER12_1 | GPIO_MODER_MODER13_1 | GPIO_MODER_MODER14_1 | GPIO_MODER_MODER15_1;
GPIOB->OSPEEDR	|= GPIO_OSPEEDER_OSPEEDR12 | GPIO_OSPEEDER_OSPEEDR13 | GPIO_OSPEEDER_OSPEEDR14 | GPIO_OSPEEDER_OSPEEDR15;
SPI2->CR2				|= SPI_CR2_RXDMAEN | SPI_CR2_TXDMAEN;
SPI2->CR1				|= SPI_CR1_CPHA | SPI_CR1_SPE;
}
void ClearStr(char* str, uint32_t l)
{
for(uint32_t i=0;i<l;i++)
	str[i] = '\0';
}
static void Mot_Send(void)
{
ClearStr(pC->bufwrite.buf, BUFW_LEN);
int32_t x[7]={0,0,0,0,0,0,0};
x[0] = 1;
x[1] = pC->Mot[0].pos*10000;
x[2] = pC->Mot[0].current*10000;
x[3] = 2;
x[4] = pC->Mot[1].pos*10000;
x[5] = pC->Mot[1].current*10000;
for(uint16_t i=0;i<6;i++)
	x[6] += x[i];
sprintf(pC->bufwrite.buf, "$%d %d %d %d %d %d %d*", x[0],x[1],x[2],x[3],x[4],x[5],x[6]);
}
void COM_Read()
{
LED3_OFF;
char buf[BUFR_LEN], c=0x00;
uint16_t j=0, cnt1=0, cnt2=0;
eOnOff frame = Off;
for(uint16_t i=0;i<BUFR_LEN;i++)
{
	c = pC->bufread.buf[i];
	if(c == '$')
	{
		ClearStr(buf, BUFR_LEN);
		frame = On;
		j=0;
		cnt1 = i;
	}
	else if(frame == On && c != '*')
		buf[j++] = c;
	else if(c == '*' && frame == On)
	{
		cnt2 = i;
		a++;
		int32_t x[3] = {0,0,0};
		sscanf(buf, "%d %d %d", &x[0], &x[1], &x[2]);
		if(x[2] == (x[0]+x[1]))
		{
			b++;
			a--;
			LED3_ON;
			pC->Mot[0].posref = (float)x[0]/10000.0f;
			pC->Mot[1].posref = (float)x[1]/10000.0f;
			pC->tick = 0;
			for(uint16_t k=cnt1; k<cnt2; k++)
				pC->bufread.buf[k] = 0x00;
			frame = Off;
		}
	}
}
}

Kod dla mastera

static void COM_SPIConf(void)
{
DMA1_Stream3->PAR 	= (uint32_t)&SPI2->DR;
DMA1_Stream3->M0AR 	= (uint32_t)pC->bufread.buf;
DMA1_Stream3->NDTR 	= (uint16_t)BUFR_LEN;
DMA1_Stream3->CR 		|= DMA_SxCR_MINC | DMA_SxCR_CIRC | DMA_SxCR_EN;

GPIOB->MODER 		|= GPIO_MODER_MODER12_0;
GPIOB->OSPEEDR	|= GPIO_OSPEEDER_OSPEEDR12;
GPIOB->PUPDR		|= GPIO_PUPDR_PUPDR12_1;
GPIOD->MODER 		|= GPIO_MODER_MODER8_0 | GPIO_MODER_MODER9_0 | GPIO_MODER_MODER10_0 |GPIO_MODER_MODER11_0;
GPIOD->OSPEEDR	|= GPIO_OSPEEDER_OSPEEDR8 | GPIO_OSPEEDER_OSPEEDR9 | GPIO_OSPEEDER_OSPEEDR10 | GPIO_OSPEEDER_OSPEEDR11;
GPIOD->PUPDR		|= GPIO_PUPDR_PUPDR8_1 | GPIO_PUPDR_PUPDR9_1 | GPIO_PUPDR_PUPDR10_1 | GPIO_PUPDR_PUPDR11_1;
GPIOB->AFR[1]		|= 0x55500000;
GPIOB->MODER		|= GPIO_MODER_MODER13_1 | GPIO_MODER_MODER14_1 | GPIO_MODER_MODER15_1;
GPIOB->OSPEEDR	|= GPIO_OSPEEDER_OSPEEDR13 | GPIO_OSPEEDER_OSPEEDR14 | GPIO_OSPEEDER_OSPEEDR15;
SPI2->CR2				|= SPI_CR2_RXDMAEN;
SPI2->CR1				|= SPI_CR1_BR_1 | SPI_CR1_SSM | SPI_CR1_SSI | SPI_CR1_MSTR | SPI_CR1_CPHA | SPI_CR1_SPE;
SPI_CSALLOFF;
}
void ClearStr(char* str, uint32_t l)
{
for(uint32_t i=0;i<l;i++)
	str[i] = '\0';
}
static void COM_SendToOneDrive(uint8_t n)
{
ClearStr(pC->bufwrite.buf, BUFW_LEN);
int32_t x[3];
x[0] = pC->Mot[0].posref*10000;
x[1] = pC->Mot[1].posref*10000;
x[2] = x[0]+x[1];
sprintf(pC->bufwrite.buf, "$%d %d %d*", x[0],x[1],x[2]);
COM_SelectDrive(n);
for(uint32_t i=0;i<BUFW_LEN;i++)
	SPI_Send(pC->bufwrite.buf[i]);
SPI_CSALLOFF;
}
void COM_SendToDrives(void)
{
for(uint8_t i=0;i<5;i++)
	COM_SendToOneDrive(i);
}
Link do komentarza
Share on other sites

Sprowadziłem problem do prostego przypadku. Jeden master i dwa układy slave.

Master wysyła dane bez DMA. Układy slave odbierają za pomocą DMA i wysyłają za pomocą DMA. Aby było łatwo analizować master nadaje kolejno w pętli for liczby od 1 do 50 z 10ms odstępami. Prędkość ustawiona na podzielnik przez 256. Układy slave odbierają dane do bufora, który następnie podglądam sobie w STMStudio. Jednocześnie układy slave nadają liczby od 10 do 1 z bufora 10-elementowego.

Problem wygląda następująco. Jeżeli komunikacja odbywa się tylko od mastera do slava to wszystko działa znakomicie, nawet przy kilku układach slave. Dane, które trafiają do slava są OK. Również gdy układ slave nadaje, ale tylko zera (czyli pewnie linia MISO jest ciągle w stanie niskim) dane trafiające do slava są OK.

Natomiast jeżeli układ slave nadaje jekieś dana inne niż zera to dane które przychodzą do slava są zakłócone.

Czy ktoś z Was spotkał się z czymś takim? Sprawdźcie, proszę, moją inicjalizację SPI w obu układach.

Testowałem to zarówno na docelowych płytkach jak i na Discovery i efekt taki sam.

Kod dla mastera

static void COM_SPIConf(void)
{
GPIOB->MODER 	|= GPIO_MODER_MODER12_0;
GPIOB->OSPEEDR	|= GPIO_OSPEEDER_OSPEEDR12_1;
GPIOB->PUPDR		|= GPIO_PUPDR_PUPDR12_0;
GPIOD->MODER 	|= GPIO_MODER_MODER8_0 | GPIO_MODER_MODER9_0 | GPIO_MODER_MODER10_0 |GPIO_MODER_MODER11_0;
GPIOD->OSPEEDR	|= GPIO_OSPEEDER_OSPEEDR8_1 | GPIO_OSPEEDER_OSPEEDR9_1 | GPIO_OSPEEDER_OSPEEDR10_1 | GPIO_OSPEEDER_OSPEEDR11_1;
GPIOD->PUPDR		|= GPIO_PUPDR_PUPDR8_0 | GPIO_PUPDR_PUPDR9_0 | GPIO_PUPDR_PUPDR10_0 | GPIO_PUPDR_PUPDR11_0;

GPIOB->MODER		|= GPIO_MODER_MODER13_1 | GPIO_MODER_MODER14_1 | GPIO_MODER_MODER15_1;
GPIOB->OSPEEDR	|= GPIO_OSPEEDER_OSPEEDR13_1 | GPIO_OSPEEDER_OSPEEDR14_1 | GPIO_OSPEEDER_OSPEEDR15_1;
GPIOB->AFR[1]		|= 0x55500000;
SPI2->CR1		|= SPI_CR1_BR | SPI_CR1_SSM | SPI_CR1_SSI | SPI_CR1_MSTR | SPI_CR1_SPE;
SPI_CSALLOFF;
}
static void SPI_Send(uint8_t data)
{
while (!(SPI2->SR & SPI_SR_TXE)){;}
SPI2->DR = data;
while ((SPI2->SR & SPI_SR_BSY)){;}
}
static void COM_SelectDrive(uint8_t n)
{
switch(n)
{
	case 0:	SPI_CS0ON;	break;
	case 1:	SPI_CS1ON;	break;
	case 2:	SPI_CS2ON;	break;
	case 3:	SPI_CS3ON;	break;
	case 4:	SPI_CS4ON;	break;
	default:						break;
}
}
static void COM_SendToOneDrive(uint8_t n)
{
delay_ms(10);
COM_SelectDrive(n);
for(uint32_t i=1;i<=50;i++)
{
	SPI_Send(i);
	delay_ms(10);
}
SPI_CSALLOFF;
}
void COM_SendToDrives(void)
{
for(uint8_t i=0;i<5;i++)
       COM_SendToOneDrive(i);
}

Kod dla slava

uint8_t b1[]={10,9,8,7,6,5,4,3,2,1};
uint8_t b2[55];
static void COM_SPIConf(void)
{
DMA1_Stream3->PAR 	= (uint32_t)&SPI2->DR;
DMA1_Stream3->M0AR 	= (uint32_t)b2;
DMA1_Stream3->NDTR 	= (uint16_t)55;
DMA1_Stream3->CR 		= DMA_SxCR_MINC | DMA_SxCR_CIRC | DMA_SxCR_EN;

DMA1_Stream4->PAR 	= (uint32_t)&SPI2->DR;
DMA1_Stream4->M0AR 	= (uint32_t)b1;
DMA1_Stream4->NDTR 	= (uint16_t)10;
DMA1_Stream4->CR 		= DMA_SxCR_MINC | DMA_SxCR_CIRC | DMA_SxCR_DIR_0 | DMA_SxCR_EN;

GPIOB->MODER		|= GPIO_MODER_MODER12_1 | GPIO_MODER_MODER13_1 | GPIO_MODER_MODER14_1 | GPIO_MODER_MODER15_1; 
GPIOB->OSPEEDR	|= GPIO_OSPEEDER_OSPEEDR12_1 | GPIO_OSPEEDER_OSPEEDR13_1 | GPIO_OSPEEDER_OSPEEDR14_1 | GPIO_OSPEEDER_OSPEEDR15_1;
GPIOB->AFR[1]		|= 0x55550000;	
SPI2->CR2		|= SPI_CR2_RXDMAEN;
SPI2->CR2		|= SPI_CR2_TXDMAEN;
SPI2->CR1		|= SPI_CR1_SPE;
}

Link do komentarza
Share on other sites

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...