Reading out the LIS302DL by STmicro - Page 1
Forum section: electronics / general

author: Torrentula
author_rank: Regular Contributor
timestamp: September 29, 2012, 04:22:17 pm
content: Hello again!I am currently trying to get the LIS302DL (Datasheet) on my STM32F4 Discovery board up and running but it just refuses to send any response.I am not using the STM32F4 Firmware library by ST. I have already had a look at it but it's not very helpful because it seems to do exactly what I do as well, with the exception that it works with the STM32F4 Discovery demo application so I know the device is working perfectly fineI have set up SPI1 in SPI mode 3, meaning that SCK is high when idle and the data is sampled at the second clock transition (low to high). I have already had a look at the communication lines with an oscilloscope and the SCK, CS and MOSI lines look alright but the LIS302DL will just not send it's data on the MISO line and I don't know why.It even refuses to send back it's ID from the WHO_AM_I register...I set the following bits:The BOOT bit in CTRL_REG2The PD, Zen, Yen, Xen bits in the CTRL_REG1Here are the relevant parts of my code:The SPI1 routines:Code:void SPI1_init(void){// configure the SPI1 I/O pins// used pins on GPIOARCC->AHB1ENR |= (1<<0); // enable clock for GPIOAGPIOA->MODER |= (1<<15) | (1<<13) | (1<<11); // set PA7 (MOSI), PA6 (MISO) and PA5 (SCK) to Alternate Function (AF)GPIOA->OTYPER &= ~( (1<<7) | (1<<6) | (1<<5) ); // set PA7, PA6 and PA5 to push pull modeGPIOA->OSPEEDR |= (1<<15) | (1<<13) | (1<<11); // set PA7, PA6 and PA5 speed to 50MHz fast speedGPIOA->PUPDR |= (1<<14) | (1<<12) | (1<<10); // enable pull-ups on PA7, PA6 and PA5GPIOA->AFR |= (1<<30) | (1<<28); // connect PA7 to AF5 via AFRLGPIOA->AFR |= (1<<26) | (1<<24); // connect PA6 to AF5 via AFRLGPIOA->AFR |= (1<<22) | (1<<20); // connect PA5 to AF5 via AFRL// used pins on GPIOERCC->AHB1ENR |= (1<<4); // enable clock for GPIOEGPIOE->MODER |= (1<<4); // set PE2 to GPIOGPIOE->OTYPER &= ~(1<<2); // set PE2 to push-pull ouput typeGPIOE->OSPEEDR |= (1<<5); // set PE2 speed to 50MHz fast speedGPIOE->PUPDR |= (1<<4); // enable pull-up on PE2// configure SPI1RCC->APB2ENR |= (1<<12); // enable clock for SPI1SPI1->CR1 |= (1<<4) | (1<<3); // set SPI clock to F_PCLK / 16SPI1->CR1 |= (1<<9) | (1<<8) | (1<<2); // set NSS management to internal and set the internal NSS to high, set MSTR bitSPI1->CR1 |= (1<<1) | (1<<0); // set CPOL high = idle high, set CPHA high = second clock transition is data sample edgeSPI1->CR1 |= (1<<6); // enable the SPI}int8_t SPI1_send(uint16_t data){SPI1->DR = data; // write value to transmit into the data registerwhile( !(SPI1->SR & (1<<1)) ); // wait until TXE flag is set --> transmit buffer emptywhile( SPI1->SR & (1<<7) ); // wait until SPI1 is not busy anymorewhile( !(SPI1->SR & (1<<0)) ); // wait until RXE flag is set --> receive buffer not emptyreturn SPI1->DR; // read received data from data register and return it}Routines and defines for the LIS302DL:Code:// LIS302DL register map#define LIS302DL_WHO_AM_I 0x0F#define LIS302DL_CTRL_REG1 0x20#define LIS302DL_CTRL_REG2 0x21#define LIS302DL_CTRL_REG3 0x22#define LIS302DL_STATUS_REG 0x27#define LIS302DL_OUT_X 0x29#define LIS302DL_OUT_Y 0x2B#define LIS302DL_OUT_Z 0x2D// LIS302DL bits// communication#define DUMMY_BYTE 0x00#define LIS302DL_R 0x80#define LIS302DL_W 0x00#define LIS302DL_M 0x40#define LIS302DL_S 0x00// CTRL_REG1#define LIS302DL_XEN 0x01#define LIS302DL_YEN 0x02#define LIS302DL_ZEN 0x04#define LIS302DL_STM 0x08#define LIS302DL_STP 0x10#define LIS302DL_FS 0x20#define LIS302DL_PD 0x40#define LIS302DL_DR 0x80// CTRL_REG2#define LIS302DL_HP_COEFF1 0x01#define LIS302DL_HP_COEFF2 0x02#define LIS302DL_HP_FF_WU1 0x04#define LIS302DL_HP_FF_WU2 0x08#define LIS302DL_FDS0x10#define LIS302DL_BOOT 0x40#define LIS302DL_SIM0x80void LIS302DL_init(void){uint8_t whoami;// configure LIS302DL// A = address bits, D = data bits// AADDGPIOE->BSRRH |= (1<<2); // set PE2 (CS) lowSPI1_send(LIS302DL_CTRL_REG2); // CTRL_REG2 @ 21h,SPI1_send(LIS302DL_W | LIS302DL_BOOT); // bits BOOT set, R/W and M/S not setGPIOE->BSRRL |= (1<<2); // set PE2 (CS) highGPIOE->BSRRH |= (1<<2); // set PE2 (CS) lowSPI1_send(LIS302DL_CTRL_REG1); // CTRL_REG1 @ 20hSPI1_send(LIS302DL_W | LIS302DL_PD | LIS302DL_ZEN | LIS302DL_YEN | LIS302DL_XEN); // bits PD, Zen, Yen, Xen, R/W and M/S not setGPIOE->BSRRL |= (1<<2); // set PE2 (CS) highDelay(0xFFFF);GPIOE->BSRRH |= (1<<2); // set PE2 (CS) lowSPI1_send(LIS302DL_R | LIS302DL_WHO_AM_I); // WHO_AM_I @ 0F, R bit setwhoami = SPI1_send(DUMMY_BYTE); // bits PD, Zen, Yen, Xen, R/W and M/S not setGPIOE->BSRRL |= (1<<2); // set PE2 (CS) highif(whoami == 0x3B){GPIOD->BSRRH |= (1<<14); // set PD14 lowGPIOD->BSRRL |= (1<<12); // set PD12 high}else{GPIOD->BSRRH |= (1<<12); // set PD12 lowGPIOD->BSRRL |= (1<<14); // set PD14 high}}void LIS302DL_read(void){GPIOE->BSRRH |= (1<<2); // set PE2 (CS) lowSPI1_send(LIS302DL_R | LIS302DL_OUT_X); // X_OUT @ 29h, R bit set, M/S not setaccel_x = SPI1_send(DUMMY_BYTE);GPIOE->BSRRL |= (1<<2); // set PE2 (CS) highGPIOE->BSRRH |= (1<<2); // set PE2 (CS) lowSPI1_send(LIS302DL_R | LIS302DL_OUT_Y); // Y_OUT @ 2Bh, R bit set, M/S not setaccel_y = SPI1_send(DUMMY_BYTE);GPIOE->BSRRL |= (1<<2); // set PE2 (CS) highGPIOE->BSRRH |= (1<<2); // set PE2 (CS) lowSPI1_send(LIS302DL_R | LIS302DL_OUT_Z); // Z_OUT @ 2Dh, R bit set, M/S not setaccel_z = SPI1_send(DUMMY_BYTE);GPIOE->BSRRL |= (1<<2); // set PE2 (CS) high}Has someone else experienced this problem before?I really appreciate your help!

author: free_electron
author_rank: Super Contributor
timestamp: September 29, 2012, 04:58:45 pm
content: do you use the ST bootcode ?some chips contain hardware discrepancies in relation to the spec . the boot code solves thesethe boot code in the ST project is typically a small piece of assembly that is run prior to entering the main function.check that one out. there may be a patch in there for something spi related ..

author: Torrentula
author_rank: Regular Contributor
timestamp: September 29, 2012, 05:22:57 pm
content: My project does have the file "startup_stm32f4xx.s" which contains the startup code, it's the one shipped with Keil IDE. I don't think it has to do with the SPI peripheral because sending the data looks perfectly fine on the oscilloscope but the accelerometer actually seems to refuse to answer...Maybe it's some simple config bit I'm missing out but I have been trying this pretty much all day and now I just can't find anything anymore... I have searched the net already but they all did it the way I do so I really don't know what the problem is right now.Thanks!

author: andyturk
author_rank: Frequent Contributor
timestamp: September 29, 2012, 08:45:54 pm
content: I've got an FDisco board blinking at me right now. :-) See if you can find a working accelerometer demo program and flash that onto the board. There are a couple that come with ChibiOS (an RTS with good STM32 support), but you can probably get one from ST too. Once you've got it running, scope *that* program and see what the signals should look like.I've got a project that uses STM32 SPI to talk to a LCD display. The lower levels are all handled by a hardware abstraction layer, but once you get the configurations right, it's pretty simple. One thing that had me scratching my head was the slave select. My code toggles it explicitly, but that may be because I'm using DMA instead of a byte-by-byte spin-wait.If you've got a good clock on the scope and your chip select is doing the right thing, maybe the initialization sequence for the '302 is off somehow?

author: Torrentula
author_rank: Regular Contributor
timestamp: September 30, 2012, 08:07:36 am
content: Ok now I feel dumb... should've checked this in the first place.The the STM32F4 Discoverydocumentationshows on page 28 that the CS pin of the LIS302DL is connected to PE2.The library uses PE3 as the CS pin, and what do you know? It actually works fine with PE3 being CS!Again another day of work wasted due to crappy ST documentation but now it works at least...EDIT: Now it's only turning out random or no data. The data is all over the place without even touching / moving it... I have no clue what's wrong this time but it has become touchy to get it working with the demo as well so I might have fried the chip...

author: andyturk
author_rank: Frequent Contributor
timestamp: September 30, 2012, 04:29:49 pm
content: Seems unlikely that you could have fried anything unless you were making physical changes to the board.

author: mrflibble
author_rank: Super Contributor
timestamp: October 10, 2012, 05:10:39 pm
content: Thanks for the code snippets. After changing PE2 to PE3 it worked straight away.Quote from: Torrentula on September 30, 2012, 08:07:36 amIndeed. Incidentally, on page 36 the schematic does correctly show PE3 being used for CS.Probably a last minute revision that didn't quite make it to the written docs.

author: Torrentula
author_rank: Regular Contributor
timestamp: October 14, 2012, 03:07:25 pm
content: Yep seems like it just wasn't picked up by the documentation guy or just forgotten (we all make mistakes, right?) - annoying but I'm glad it works now

author: mrflibble
author_rank: Super Contributor
timestamp: October 14, 2012, 05:03:47 pm
content: Quote from: Torrentula on October 14, 2012, 03:07:25 pmIndeed. It's just a bit unfortunate that documentation guys at ST are more prone to such mistakes than some other companies. ;-) Or maybe we should just blame the non-quality assurance department.Anyways, about this bit of code ...Code:// configure the SPI1 I/O pins// used pins on GPIOARCC->AHB1ENR |= (1<<0); // enable clock for GPIOAGPIOA->MODER |= (1<<15) | (1<<13) | (1<<11); // set PA7 (MOSI), PA6 (MISO) and PA5 (SCK) to Alternate Function (AF)GPIOA->OTYPER &= ~( (1<<7) | (1<<6) | (1<<5) ); // set PA7, PA6 and PA5 to push pull modeGPIOA->OSPEEDR |= (1<<15) | (1<<13) | (1<<11); // set PA7, PA6 and PA5 speed to 50MHz fast speedGPIOA->PUPDR |= (1<<14) | (1<<12) | (1<<10); // enable pull-ups on PA7, PA6 and PA5GPIOA->AFR |= (1<<30) | (1<<28); // connect PA7 to AF5 via AFRLGPIOA->AFR |= (1<<26) | (1<<24); // connect PA6 to AF5 via AFRLGPIOA->AFR |= (1<<22) | (1<<20); // connect PA5 to AF5 via AFRLAny particular reason to use that style of coding? No right or wrong here, just curious. It works, so no arguments there. I personally just find it less readible. Certainly because I am an stm32 n00b, so I don't know all the funky bit locations in the registers. So right now I use something like this:Code:GPIO_InitTypeDef GPIO_InitStructure;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);/* Configure PA5, PA6, PA7 in alternate function push-pull mode */GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6| GPIO_Pin_7;GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; // maybe use GPIO_PuPd_NOPULL ?GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1);// SCKGPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1);// MISOGPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1);// MOSIThat does the same as your code snippet, but for me this is a bit easier to maintain. Mostly when I want to use the same code snippet, but then for say SPI2. I don't know the bit locations in the AFR registers without checking a datasheet. But changingGPIO_AF_SPI1toGPIO_AF_SPI2I think I can manage without too much chance of introducing an error. ;-)So I was wondering if there was any specific reason to use theGPIOA->AFR |= (1<<30) | (1<<28);code style? All I can think of is maybe smaller code size and faster execution... While that is nice, personally I favor code that is maintainable and less error prone. Certainly for initialization code that runs only once.Anyways, just wondering if there's a particular reason...

author: andyturk
author_rank: Frequent Contributor
timestamp: October 14, 2012, 10:31:13 pm
content: Quote from: mrflibble on October 14, 2012, 05:03:47 pmIt may be a matter (again) of documentation. Sometimes the hardware datasheets are much easier to deal with than OEM firmware documentation. The register info in the datasheet is all about bit fields and that leads to the "(x << y)" type of code.

author: Torrentula
author_rank: Regular Contributor
timestamp: October 15, 2012, 09:57:38 am
content: QuoteNo it doens' really matter how you do it and yes your code is probably easier to maintain, the only reason I do this is that I wanted to get the thing up and running with the Keil IDE and I could't seem to figure out how to get the peripheral lib by ST to get compiled into that project.The maintainability is a good argument and I would prefer to use that style as well, the problem is that I just don't want to always turn on my laptop in order to use the toolchain built for Linux.I am still looking for some good explanation on how to get Keil to use the ST peripheral lib so if you have any information on how to set up Keil correctly then I'd appreciate it

author: andersm
author_rank: Super Contributor
timestamp: October 15, 2012, 11:58:45 am
content: Quote from: mrflibble on October 14, 2012, 05:03:47 pmST's library code at least used to be miserably slow. Doesn't matter for initialization code, but on one project some bit-banging code ran probably an order of magnitude faster after switching to accessing the GPIO port registers directly.Quote from: Torrentula on October 15, 2012, 09:57:38 amIt's been a while since I used Keil, but does't it set up that automatically when you create a new project? If you want to use an external copy of the library, the download should come with some example projects or you could adapt one of ST's sample projects from their website.

author: Torrentula
author_rank: Regular Contributor
timestamp: October 15, 2012, 02:37:17 pm
content: Yep Keil includes it's own peripheral lib but I just figured out how to set this thing up after about an hour of checking build commmands etc.The thing with the lib specifically provided for the STM32F4 Discovery is that it also includes functions for the LIS302DL and the onboard Cirrus Logic audio DAC.I wouldn't bother with using functions to do such simple tasks as setting a bit in an output data register like so:Code:GPIOD->BSRRL |= (1<<15); // set PD15 highGPIOD->BSRRH |= (1<<14); // set PD14 lowGPIOD->ODR |= (1<<13); // set PD13 highGPIOD->ODR &= ~(1<<12); // set PD12 lowCalling a function to do this would be a waste of execution time because in the given application you will know which pins you are using for bit banging.Initializing a peripheral is different though because much more bits have to be set and that's where the library comes in handy and does all the non time-critical initialization stuff for you.OT: Does anyone know of a good screen recording software for Windows? What does Dave use? I might do a video on how to get this thing to work.

author: OndraSter
author_rank: Contributor
timestamp: October 17, 2012, 11:06:26 pm
content: Not sure how about Dave, but I use Microsoft's Expression Encoderhttp://www.microsoft.com/expression/products/EncoderPro_Overview.aspx- I have done few recordings without any issues - just install and use.

author: mrflibble
author_rank: Super Contributor
timestamp: October 18, 2012, 12:50:35 am
content: Quote from: Torrentula on October 15, 2012, 09:57:38 amThe toolchain situation would be a good enough reason, yes.No explanations on Keil from me I'm afraid.... Under linux I use the obligatory eclipse + arm-none-eabi combo. And on windoze I use Atollic, which is based on eclipse. That way I can use inspiration gleaned from the atollic install on the linux install. Out of sheer frustration I caved in and installed atollic, which at least worked out of the box. Then when I slowly got the hang of it I moved to linux again...I guess my point is, you could try Atollic. That worked straight away without any crazy library setup required. You can install the Lite version (making sure to  check the ST-Link install). After that you can do Import => Example from Teh Internets => your fav demo board => Blinkenlights, etc. That got me up and running fairly quick.As for this code snippet:Code:GPIOD->BSRRL |= (1<<15); // set PD15 highGPIOD->BSRRH |= (1<<14); // set PD14 lowYou may want to do this:Code:GPIOD->BSRRL = (1<<15); // set PD15 lowGPIOD->BSRRH = (1<<14); // set PD14 highNote that the low/high situation is precisely opposed to what was in your comments. You probably grabbed some random googled code, but just so you know. In case you didn't know, then this serves as a nice example of "optimization" vs "the illusion of optimization". Because even the crappy ST lib results in something faster than the inadvertent |= based code. Your code with the bit-wise or results in a read-modify-write, which results in waaay too many instructions. The above does it's thing in 4 instructions as you'd expect.Personally I am still perfectly happy to use this equivalent:Code:GPIO_ResetBits(GPIOD, GPIO_Pin_15 | GPIO_Pin_14);GPIO_SetBits(GPIOD, GPIO_Pin_13 | GPIO_Pin_12);... certainly for non-critical things as blink-the-led.The rationale for using the GPIOx->ODR notation for bit-banging is only partially rational IMO. One of the reasons why I picked the stm32f4 is because it has a nice set of peripherals,preciselyso I would not have to do any bit-banging to do trivial mundane boring stuff like i2c or spi.All the stuff I've used so far on it could either be handled by the onboard peripherals, OR was slow enough that I didn't really care too much about optimizing it to the ns.Incidentally, if you are going the optimization route you might as well do this for the previous bit of code:Code:*((__IO uint32_t *)&GPIOD->BSRRL) = 0xC0003000; // PD15=PD14 = 0 , PD13=PD12=1That does a 32-bit write to the BSRRL and BSRRH registers in one go. I was curious if the BSRR register accepts 32-bit writes, and indeed it does. It even gives the right result.I just checked it on my discovery board and 1) the leds go blinkie as intended, and 2) the resulting asm for it is 3 instructions. Two for the constant loads, and one for the write. That's with -O1 optimization.And no, I don't advocate using that code. But if you want to optimize like that, might as well go all the way and shave of 25% of your execution time.

author: andersm
author_rank: Super Contributor
timestamp: October 18, 2012, 01:12:11 am
content: The BSRR register (bit set reset register register?) also has the nice property that the "set" bit has precedence over the "reset" bit, so you can have the "reset" part be a constant mask of all the GPIO pins and put the actual data in the "set" bits. Much nicer than having to do the read-modify-write dance.

author: mrflibble
author_rank: Super Contributor
timestamp: October 18, 2012, 01:59:08 pm
content: Yeah, I noticed that as well. Very handy feature, that.

author: andyturk
author_rank: Frequent Contributor
timestamp: October 18, 2012, 02:12:48 pm
content: Quote from: mrflibble on October 18, 2012, 12:50:35 amAnd if you *really* need to flip a single bit in one instruction, there's always bit banding. I've never had a need for it though.

author: Javier Revilla
author_rank: Newbie
timestamp: August 16, 2013, 06:31:03 pm
content: Dear Torrentula,Thank you for your code for configuration and usage of the STM32F4 accelerometer (LIS302DL).You say that PE3 is used as the CS pin, instead of PE2.Please, could you post a new version of your code taking this correction into consideration ?I am really a newbie and I do not know how to adapt your code for PE3, instead of PE2.Thanks.Best regards,Javier Revilla