Skip to main content
SNeub.1
Visitor II
February 12, 2020
Solved

Control AMIS-30543 with STM32F030R8

  • February 12, 2020
  • 13 replies
  • 3684 views

I'm setting up a MCU to control a stepper motor. I'm aiming to control the stepper motor driver (AMIS-30543) over SPI. My current goal is to get the driver to respond to my SPI commands so I know it's correctly receiving my commands. The driver responds, but only ever with 0xFF. Once earlier today I got it to respond with 0x02 (the hexadecimal I should receive), but it hasn't repeated since then. So where I need help is figuring out why its behaving like this and how to get the driver to respond correctly.

I'm using Keil uVision 5. Writing in C as a beginner of beginners.

The pins are connected according to the following description.

STM AMIS
3.3v IOREF
GND GND
PB3 NXT
PB5 DIR
PA7 DO
PA6 DI
PA5 CLK
PA4 CS
 
AMIS 9v Battery
VMOT + terminal (also tried with STM 5V)
GND - terminal (also tried with STM GND)
 
AMIS 26M024B2B
MXP + coil 1
MXN - coil 1
MYP + coil 2
MYN - coil 2

Relevant segments of my code is below.

int main(void)
{
	Clock_Init();
	USART2_Init();
	SPI1_Init();
	
	init();
	
	nxtLow();
	dirLow();
	
	DelayMS(1);
	
	resetSettings();
	
	setCurrentMilliamps(132);
	
	setStepMode(16);
	
	enableDriver();
	
	printf("WR:	");
	printHex(readReg(WR));
	printf("\r\n");
	printf("CR0:	");
	printHex(readReg(CR0));
	printf("\r\n");
	printf("CR1:	");
	printHex(readReg(CR1));
	printf("\r\n");
	printf("CR2:	");
	printHex(readReg(CR2));
	printf("\r\n");
	printf("CR3:	");
	printHex(readReg(CR3));
	printf("\r\n");
	
	while (1)
	{
		dirHigh();
		for (unsigned int i = 0; i < 16; i++)
		{
			Step();
		}
		
		DelayMS(300);
		
		dirLow();
		for (unsigned int i = 0; i < 16; i++)
		{
			Step();
		}
		
		DelayMS(300);
	}
}
 
// INITIALIZATIONS: start
void Clock_Init(void)
{
	// enable ports					-> port A, AHB, bit 17
	//								-> port B, AHB, bit 18
	//								-> port C, AHB, bit 19
	RCC->AHBENR = 0x000E0000;
	
	// enable USART2 				-> bit 17, APB1
	RCC->APB1ENR = 0x00020000;
	
	// enable SPI1 					-> bit 12, APB2
	RCC->APB2ENR = 0x00001000;
}
 
void USART2_Init(void)
{
	// define GPIO pins				-> PA2, alternate function
	//								-> PA3, alternate function
	GPIOA->MODER = 0x000000A0;
	
	// define alternate functions		-> PA2, AF1, USART2 TX
	//								-> PA3, AF1, USART2 RX
	GPIOA->AFR[0] = 0x00001100;
	
	// set baud rate					-> 8000000 / 9600 to HEX = 9600 @ 8MHz
	USART2->BRR = 0x00000341;
	
	// format usart					-> enable tx, bit 3
	//								-> enable rx, bit 2
	USART2->CR1 = 0x0000000C;
	
	// enable usart					-> enable ue, bit 0
	USART2->CR1 |= 0x00000001;
}
 
void SPI1_Init(void)
{
	// define GPIO pins				-> PC7, output
	//								-> PB3, output
	//								-> PB4, output
	//								-> PA4, alternate function
	//								-> PA5, alternate function
	//								-> PA6, alternate function
	//								-> PA7, alternate function
	GPIOC->MODER = 0x00004000;
	GPIOB->MODER = 0x00000140;
	GPIOA->MODER |= 0x0000AA00;
	
	// define alternate functions		-> PA4, AF0, SPI1 CS / NSS
	//								-> PA5, AF0, SPI1 SCK
	//								-> PA6, AF0, SPI1 MISO
	//								-> PA7, AF0, SPI1 MOSI
	GPIOA->AFR[0] |= 0x00000000;
	
	// format spi						-> set br, bit 3-5
	//								-> enable ssm, bit 9
	//								-> enable ssi, bit 8
	//								-> set mstr, bit 2
	SPI1->CR1 = 0x00000314;
	
	// format spi						-> set ds, bit 8-11
	SPI1->CR2 = 0x00000700;	
	// enable spi						-> enable spe
	SPI1->CR1 |= 0x00000040;
}
 
// INITIALIZATIONS: end
//
//
// SPI1: start
void csHigh(void)
{
	GPIOC->BSRR = 0x00000080;
	GPIOA->BSRR = 0x00000010;
}
 
void csLow(void)
{
	GPIOC->BSRR = 0x00800000;
	GPIOA->BSRR = 0x00100000;
}
 
void selectChip(void)
{
	csLow();
	// begin spi transaction, unnecessary?
}
 
void deselectChip(void)
{
	csHigh();
	// end spi transaction, unnecessary?
	
	// stabilization delay
	DelayUS(3);
}
 
uint8_t transfer(uint8_t value)
{
//	while (!(SPI1->SR & 0x00000002));	// wait for TX buffer to be empty
//	SPI1->DR = value;					// start the transmission
//	while (!(SPI1->SR & 0x00000080));	// wait for TX to be complete
//	return SPI1->DR;					// return the received byte
	
	
	while (!(SPI1->SR & SPI_SR_TXE));
	SPI1->DR = value;
	//while (!(SPI1->SR & SPI_SR_RXNE));
	while (!(SPI1->SR & SPI_SR_BSY));
	return SPI1->DR;
}
 
uint8_t readReg(uint8_t address)
{
	selectChip();
	transfer(address & 0b11111);
	uint8_t dataOut = transfer(0);
	deselectChip();
	return dataOut;
}
 
void writeReg(uint8_t address, uint8_t value)
{
	selectChip();
	transfer(0x80 | (address & 0b11111));
	transfer(value);
	deselectChip();
}
 
// SPI1: end
//
// GENERAL FUNCTIONALITY: start
void DelayTicks(uint32_t ticks)
{
	SysTick->LOAD = ticks;
	SysTick->VAL = 0;
	SysTick->CTRL = SysTick_CTRL_ENABLE_Msk;
	
	while ((SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) == 0);
	SysTick->CTRL = 0;
}
 
void DelayMS(int n)
{
	SysTick->LOAD = 8000 - 1;
	SysTick->VAL = 0;
	SysTick->CTRL = 0x00000005;
	
	for (int i = 0; i < n; i++)
	{
		while ((SysTick->CTRL & 0x00010000) == 0);
	}
	
	SysTick->CTRL = 0x00000000;
}
 
void DelayUS(uint32_t n)
{
	DelayTicks((n * 1000));
}
 
// GENERAL FUNCTIONALITY: end

This topic has been closed for replies.
Best answer by waclawek.jan

At this point, I don't know. As it's the AMIS chip which does not answer to what appears to be a correct sequence, it's then either incorrect wiring, bad solder joints, or faulty chip...

JW

13 replies

waclawek.jan
Super User
February 13, 2020

> SPI1->DR = value;

This starts a 16-bit transfer, see "Data packing" in SPI chapter of RM and here on forum. Is this what you wanted?

Generally, you want to observe the pins using logic analyzer and compare the observed waveforms with those given in the sensor's datasheet.

JW

SNeub
Associate
February 13, 2020

JW,

Thanks for the response. Yes, that's something I realized after making this post. I did go back and update the FIFO reception threshold so it flagged the RXNE event if the FIFO level is greater than or equal to 1/4 (8bit) with the SPIx_CR2 register. Would that have addressed what you brought up? Even after this update I'm still receiving 0xFF (or whatever dummy data I send) back from the driver.

SPI1_Init(void) now contains the following code for CR2...

// format spi									-> set ds, bit 8-11
//										-> set frxth, bit 12
SPI1->CR2 = 0x00001700;

SN

SNeub
Associate
February 13, 2020

JW,

Thanks for the response. Yes, that's something I realized after making this post. I did go back and update the FIFO reception threshold so it flagged the RXNE event if the FIFO level is greater than or equal to 1/4 (8bit) with the SPIx_CR2 register. Would that have addressed what you brought up? Even after this update I'm still receiving 0xFF (or whatever dummy data I send) back from the driver.

SPI1_Init(void) now contains the following code for CR2...

// format spi									-> set ds, bit 8-11
//										-> set frxth, bit 12
SPI1->CR2 = 0x00001700;

SN

waclawek.jan
Super User
February 13, 2020

No, I'm talking about Tx. Have you read the "Data packing" section in SPI chapter in RM and have you looked it up here on the forum?

Have you observed the waveforms?

JW

SNeub
Associate
February 13, 2020

I've read through it in the RM, but I'm barely even a beginner in C/embedded programming so I'm probably missing something important. I haven't looked it up on this forum. I don't have a logic analyzer/oscilloscope, if I did then troubleshooting this would be easier.

Are you referring to how the slave device receives 8 bits of data that specify the command and address then an additional 8 bits of the actual data?

Is this the correct procedure?

0690X00000DAMERQA5.png

waclawek.jan
Super User
February 14, 2020

>  I don't have a logic analyzer/oscilloscope

Get one, borrow or buy some cheap one. Otherwise it's just blind shooting.

JW

SNeub
Associate
February 14, 2020

Until I can convince my boss we need a logic analyzer/oscilloscope is there any other advice/observations? As I understand it, the SPI read process is as follows...

  1. set NSS low
  2. wait for the SPI_SR_TXE flag to be set high(?) by the hardware
  3. place 8 bits of data in the data register, these 8 bits specify the command type (Read or Write) and the target slave device register
  4. wait for the SPI_SR_RXNE flag to be set high(?) by the hardware
  5. wait for the SPI_SR_TXE flag to be set high(?) by the hardware
  6. place 8 bits of dummy data in the data register
  7. wait for the SPI_SR_RXNE flag to be set high(?) by the hardware
  8. read 8 bits of data from the data register, these 8 bits are the contents of the slave device register
  9. set NSS high

Is this correct? Or should I be changing the 8 bits to 16 bits? I specified in the control register I'm using 8 bit data frame.

SNeub
Associate
February 14, 2020

Until I can convince my boss we need a logic analyzer/oscilloscope is there any other advice/observations? As I understand it, the SPI read process is as follows...

  1. set NSS low
  2. wait for the SPI_SR_TXE flag to be set high(?) by the hardware
  3. place 8 bits of data in the data register, these 8 bits specify the command type (Read or Write) and the target slave device register
  4. wait for the SPI_SR_RXNE flag to be set high(?) by the hardware
  5. wait for the SPI_SR_TXE flag to be set high(?) by the hardware
  6. place 8 bits of dummy data in the data register
  7. wait for the SPI_SR_RXNE flag to be set high(?) by the hardware
  8. read 8 bits of data from the data register, these 8 bits are the contents of the slave device register
  9. set NSS high

Is this correct? Or should I be changing the 8 bits to 16 bits? I specified in the control register I'm using 8 bit data frame.

waclawek.jan
Super User
February 15, 2020

I don't know the sensor's protocol, but as I've said, above:

>> SPI1->DR = value;

> This starts a 16-bit transfer, see "Data packing" in SPI chapter of RM and here on forum.

JW

SNeub
Associate
February 21, 2020

So, I've gone back through the documentation for the MCU and the slave device (AMIS-30543) and got a logic analyzer. I've updated the code some and I think I've narrowed down where the error may lay. Here is a screen grab of the logic analyzer results and updated code.

The slave device's documentation is here. It states it must receive a command+address byte followed by an empty byte in order for one of its registers to be read.

https://www.pololu.com/file/0J869/AMIS-30543-D.pdf 

It looks like I'm correctly sending 8 bits of data to the slave device, but am still receiving blank data in return.

0690X00000DC7CZQA1.png

Code:

int main(void)
{
	Clock_Init();
	USART2_Init();
	SPI1_Init();
	
	while (1)
	{
		DelayMS(1);
		writeReg(CR0, 0x11);
		readReg(CR0);
	}
}
 
void Clock_Init(void)
{
	// enable ports			-> port A, AHB, bit 17
	//				-> port B, AHB, bit 18
	//				-> port C, AHB, bit 19
	RCC->AHBENR = 0x000E0000;
	
	// enable USART2 		-> bit 17, APB1
	RCC->APB1ENR = 0x00020000;
	
	// enable SPI1 			-> bit 12, APB2
	RCC->APB2ENR = 0x00001000;
}
 
void SPI1_Init(void)
{
	// define GPIO pins		-> PC7, output
	//				-> PB3, output
	//				-> PB4, output
	//				-> PA5, alternate function
	//				-> PA6, alternate function
	//				-> PA7, alternate function
	GPIOC->MODER = 0x00004000;
	GPIOB->MODER = 0x00000140;
	GPIOA->MODER |= 0x0000A800;
	
	// define alt. func.s		-> PA5, AF0, SPI1 SCK
	//				-> PA6, AF0, SPI1 MISO
	//				-> PA7, AF0, SPI1 MOSI
	GPIOA->AFR[0] |= 0x00000000;
	
	// format spi
	SPI1->CR1 = 0x00000014;	// br = f_pclk / 8
	
	// format spi
	SPI1->CR2 = 0x00001704;	// ds = 8 bit
	
	// enable spi
	SPI1->CR1 |= 0x00000040;
	
	// flush data register
	uint8_t flushValue = SPI1->DR;
}
 
void writeReg(uint8_t address, uint8_t value)
{
	selectChip();
	transfer(0x80 | (address & 0b11111));
	transfer(value);
	deselectChip();
}
 
uint8_t readReg(uint8_t address)
{
	selectChip();
	transfer(address & 0b11111);
	uint8_t dataOut = transfer(0x00);
	deselectChip();
	return dataOut;
}
 
uint8_t transfer(uint8_t value)
{
	while (!(SPI1->SR & 0x00000002));	// wait for the transmission to complete
	*(uint8_t*)&SPI1->DR = value;		// start the transmission
	while (!(SPI1->SR & 0x00000001));	// wait for the reception buffer to be full
	return (uint8_t)SPI1->DR;			// return the received byte
}
 
void selectChip(void)
{
	csLow();
}
 
void deselectChip(void)
{
	csHigh();
}

waclawek.jan
waclawek.janBest answer
Super User
February 24, 2020

At this point, I don't know. As it's the AMIS chip which does not answer to what appears to be a correct sequence, it's then either incorrect wiring, bad solder joints, or faulty chip...

JW

SNeub
Associate
February 24, 2020

I'm very happy to report it is working now; soldering the joints did the trick. I soldered the pins now instead of simply laying the chip on top of the breakout pins (I thought the contact was sufficient, but was clearly wrong in that assessment). The stepper motor now spins as instructed.

I did notice that I'm unable to flash the MCU while the power supply is connected to the stepper motor and the motor will continue spinning when the MCU is unplugged from the computer (I was powering the MCU via USB for development). The LD2 LED is also faintly illuminated, for some reason.

My frankenstein-ian translation of the Arduino AMIS-30543/SPI libraries here:

https://www.dropbox.com/s/rd56urolci0ou82/main.txt?dl=0

Thank you for your help, JW.

waclawek.jan
Super User
February 24, 2020

Thanks for coming back with the result.

JW