How To Write A Driver (STM32, I2C, Datasheet)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

This dude's youtube channel is pure gold. He has a couple of videos on signal processing in embedded settings, and although I know most of this stuff by the book already, it's something that I really struggle to pass along to folks less experienced (that haven't had a full course on signal processing or control systems, for example).

His KiCad stuff is also tits. Highly reccomend it.

👍︎︎ 42 👤︎︎ u/SpyreMeown 📅︎︎ Aug 22 2021 🗫︎ replies

You are a very good .

👍︎︎ 7 👤︎︎ u/thambiTeaInnumVarala 📅︎︎ Aug 22 2021 🗫︎ replies

One of the best YouTube channels out there .. Always having the best time watching this dude, and of course, learning a ton of cool advanced stuff !

👍︎︎ 6 👤︎︎ u/radixties 📅︎︎ Aug 22 2021 🗫︎ replies

Nice stuff, I subbed. I also started a yt channel recently and since you are experienced in low level programming, could you tell me if I'm on the right track and if my videos are any good?

👍︎︎ 1 👤︎︎ u/cyberchase_ 📅︎︎ Aug 22 2021 🗫︎ replies

First time I see this dude's stuff...

Is this dude's videos aimed at beginner hobbyists?

👍︎︎ 1 👤︎︎ u/santasnufkin 📅︎︎ Aug 22 2021 🗫︎ replies
Captions
in this video we'll be looking at how to interface with an accelerometer sensor and write an i squared c driver from scratch for an stm32 microcontroller we'll only be using the datasheet stm32 cube id as our development environment and the hardware abstraction layer i'll be hooking this accelerometer this adx l355 you see on the left which i made a breakout board for to the board on the right which is the little brain board i featured in previous videos which contains an stm32f4 microcontroller so let's get started the balls you just saw were produced and assembled by glc pcb in china this is also where i got this adx l355 accelerometer from as you can see it's actually quite expensive and it's a reason why i chose it it's a low noise low drift and low power three axis mems accelerometer and i thought that'd be quite interesting to give a little try so we're going through this data sheet you see here figuring out what is actually relevant for us to write a driver what information we need and then how to convert that into an actual driver in the stm32 cube id development environment so we'll go through pretty much the whole process if you'd like to load the schematic for example for this little brain board you can go to my github page at github.com pms67 click on repositories and then you can navigate the little brain sensor board repository i have everything with the phone there and there the hardware files assembly files and global files and so on so you can actually get this board manufactured yourself but we're referring back to the schematic throughout this video a big thank you also to altium for sponsoring this video i use ultimate my work and also my spare time for more complicated designs you can see how to man action for example this rpe 2040 video our team is actually offering a free trial version of outing designer i'll leave the link in the description below this video but you can also go to autumn.comtrial.flow before we get started writing the driver let's look at the adxl355 breakout board schematic which consists of roughly three sections which is basically an input filter and regulator the accelerometer itself and a header where i break out all the pins from the accelerometer for example spi i squid c and so on so first polarity protection i'm using a p channel mosfet with a zener diode and a resistor capacitor just for some protection of the gate then i have this pi network which is a third bead and two capacitors for some filtering and lastly this 3.3 volt regulator with the relevant decoupling capacitors then the accelerometer is very very simple basically all this accelerometer needs according to the data sheet are some decoupling capacitors and i've created this symbol and footprint myself seeing here you can see all the decoupling capacitors and we can get this from the datasheet so first we can get the pin configurations and function descriptions which tell us what each of these pins do and this is really useful so what do we need to hook up what can we tie to ground and so on and they actually usually give you an application circuit so just a basic example circuit that will that will get this thing up and running and that's what i've taken over and implemented in kicad lastly i have a header just a simple i think 10 pin header i'm putting power input and power output the interrupt pins from the accelerometer the spi and i squared c pins and some current limiting resistors to protect the device and of course some esd protection to protect the device further this accelerometer is quite expensive i don't really want to mess it up so here we are on keycard in the pcb layout editor as well this is a simple two layer board i've second things in power alex accelerometer and header it's a very simple board rooted power i've got a ground plane on the second or lower bottom plane and that's about it we can see in 3d view and this is also the board you saw at the beginning of this video so really really simple design just to break out this accelerometer and so i can interface it with different mcus for example as i mentioned at the beginning of this video i actually have the little brain stm32f4 sensor board files available in my github repository and we'll actually be looking at this little brand schematic as a pdf file now scroll to the microcontroller page and this is where you can see the pin out of the stm32f4 microcontroller i've connected to 16 megahertz high-speed external crystal i'm using i squared c1 to interface with the accelerometer and i'll be using one of these rgb leds then i also have a usb connector which i'll be using to power the stm32f4 board and also to interface for example via virtual com port i'm also using serial wire debug to debug this program i'll be using the stm32 cube ide development environment which is a free eclipse based ide provided by st to program their stm32 microcontrollers now in stm32 cube id i've created a new project selected my part which is an stm32f405 and already done the pin out so let's get through what i've done in rcc i selected my high speed external crystal then in the system settings i enable sierra wire debug now i'm using it with the swo pin which is the trace pin for extra debugging features which is usually on pb3 for these microcontrollers then i've enabled i squared c and you can do that by either clicking on the relevant pins and selecting scl and sda on the left hand side in the drop down you can also select i squared c1 i've also enabled usb which you can find in connectivity and then usb full speed and i'm using it in device only mode another thing to enable while we're at it is in down there in middlewares click on usb device and i would like to use a driver for a virtual com port and st provides this already so we have to do minimal work then i've enabled an led pin which is adjusted gpio output and i can actually right click on that pin and enter user label to give it a sensible name rather than you know gpio1 or something like that the accelerometer also has an interrupt or data ready pin and i can use an external interrupt gpio if i right click and select the bottom option that'll then fire and enable me to for example read the accelerometer values when this pin is set high by the accelerometer in the nvic settings i also have to enable this line interrupt to make sure it actually fires one more thing we have to do is actually set up our clock with our plls and we can go to clock configuration we enter our hse frequency which is 16 megahertz according to the schematic we select hse and the pll clock and we type in our desired clock frequency which i chose to be 168 which is the maximum then you can press enter and let everything be solved by cube id and then just click save to generate code now that we set up our cube id i've hooked my adxl345 breakout board to the stm32f4 breakout board as shown here with an st link connected so here we are back in stm32 cube ide and we're pretty much ready now to write our driver for the adxl 355 accelerometer so once you've completed your pin assignment and wired everything up you can click up here on save and this will automatically generate the basic low level code we'll need to run this on our stm32 microcontroller then if you navigate over to our main function you can see i've already done a tiny bit of work and i'll walk you through it so first of all i've added this adxl355.c file in my source folder so you can simply right click click new and then create source file give it a relevant name and we'll also need a header file which has the similar name so adxl355.8 again right click on the include folder and click on header file and give it an appropriate name and this will form the basis for our c driver so i'm going to be writing this in c rather than c plus plus but the choice is entirely up to you in our dot h file this is our header file will define everything for example register ids device ids what functions we have and in adxl305.c our source file we will then implement those functions and finally in main.c we will call those functions and include those driver files so here i am in main.c and these are the basic includes that stm32 cube id has generated to use the usb virtual com port i also have to include this header file and then again i've already included the adxl355.h header file which we'll be writing in just a second so let's go over to that so here we are on the adxl355.h header file which i've already written and i would like to just talk you through what i've done and why i've done it first of all it's always good to just include some sort of comment what this driver is for who the author was when it was created of course you can add much more detail but this is a good start then we have this preprocessor directive if not defined this name define this name and the end of the file we will then have the relevant endif and this is just to make sure that we maybe might include this file or this header in multiple places uh adsl 345.h if we use an include statement multiple times we will not have the same code copied into our program more than once secondly you will see that i have this include here this stm32f4xx underscore how dot h and this is needed because we'll be calling some hardware abstraction layer or how i squared c functions i always recommend that you comment pretty much all of your code even if it seems really basic or elementary so we'll need this include to include some things like memory reads memory rights to and from registers in my drivers i typically like to include a defined section so i'll define things for example i squared c address and certain things for example the sensor's device id or part id and so on which we'll be checking later now the i squared c address you can actually get from the data sheet of course so let's go over to that so this is the adxl355 datasheet and if you browse through the documentation or simply do a search with control f you can see that we are detailing here the iscode c protocol but it also tells us about the i squared c device id if you saw from the schematic of the adxl 355 there's an a cell pin and we can pull that low or high that'll actually change the isequity device address so in theory we could have two of these accelerometers running on the same i squared c bus i only have one and i've pulled the a select pin low to zero which gives us a device address of ox1d in hex and you can see if i go back to stm32q ide that's what i've put in here so x1d is from the datasheet and again i've written a comment that if a cell is low we get this address if a cell is high we have this address i've also written the relevant page of the datasheet in my comment now you'll notice here i'm actually left shifting this address by one and let me tell you why so if we go back to the device configuration tool and to i squared c you can see that actually the primary address length is 7 bits this is because actually we're using 8 bits but the first 7 bits are the address and the last bit bit 0 is the read or write bit that's why we have to shift this i squared c address left to make sure the bit 0 is free for our write or read bit before we move on to the next section there's usually always a register map in data sheets for sensors like this and we have that on page 31 of the datasheet so we have the adxl 355 register map and this will tell us the address of each register for example zero one two three four and so on what the register name is and what the register contents are and the bits and so forth and if we can read or write or both for example also when the is first powered on it'll have this reset state or when the sensor is reset and this will be the default contents of that register this is going to be really useful for writing our driver because we can already see okay we can get the temperature data out here we can get the x axis accelerated data out here and so on we also have some sort of setup registers down here and we'll be going through which ones we'll actually use and how to write to them or read from them so back in stm32 cube ide you saw that the device actually has a device id mems id and part id and that's given in the datasheet if i go back and scroll a bit down the first three registers are device id mems id and part id and these are fixed for this sensor and at reset they'll have oxad ox1d and oxed as values and that's pretty much what i just copied over here now the reason for doing this is because at power or when i start up my stm32f4 board i want to communicate with the sensor and check if this is the same this is the same and this is the same just to make sure first of all does my communication work can i read the register and get the right addresses and contents out and to make sure i'm connected to the right sensor so i typically will do a device id or part id check at the beginning of my ice cream c driver and i'll show you how to do that once we get to writing the functions now below that i've made my register defines again i've put in a comment which part of the datasheet that can be found that's page 31 and that's the page we just saw with this register map essentially what i'm doing with my defines if i go back is i'm defining the name of my sensor on a score that it's a register and then the name of that register and simply giving it the address so start from zero all the way down here if you're writing a really really simple driver you might only need certain registers you might only need the device id you might only need the data registers and so on but i've just included all of the registers from this device data sheet just to be complete by writing the registers and defines we can just reference for example the device id register rather than writing oxo and it makes the code clearer to read and you'll see that in just a second one thing i typically like to do with my c drivers as well is include a struct and this struct i'll be creating an instance of in my main function and this will be passed to my for example initialization functions or register read functions and so on essentially this is really simple and it just stores the i squared c handle so if i'm using i squared c one i squared c two and that depends on my stm32 and my connections this struct also stores the acceleration data so not the raw but rather processed and converted to meters per second squared and i always typically include the units then i lastly have my temperature data in degrees c this is a very simple and very simple example but this struct might grow larger for more complicated sensors and depending on data you want to store finally we reach the end of our header file which contains our actual function definitions the first one here is our initialization function i've given it the sensor name and i've given it underscore initialize as arguments where i'm passing essentially the struct as a pointer and the iso c handle we'll be using and we'll be calling this from our main function this initialization function essentially just sets up the sensor it makes sure the device id part id and mems id and so on are correct we can set up our for example our output data rates we can set up our low pass filters high pass filters and so on everything that's internal to the sensor will be set up here and checked now i'm returning a u and eight so zero to two five five to be essentially an error code next we have these data acquisition functions so i've again sent the name underscore for example read temperature and as is quite obvious this will return the temperature or give us the temperature reading from the accelerometer and we pass the struct as usual the second one is the read accelerations function which reads x y and z accelerometer values and converts them to meters per second squared again we're passing the struct now you can see our return value is actually this how status type def and this is essentially just because we're doing one single i squared c transaction in each of these functions this will give us if the how i squared c transaction was successful if it were there was an error or if the bus was busy for example in the stm32 cube id i can control click and that'll show us this enum in the stm32 drivers so you can see hello okay how error how busy or how timeout that's quite useful for debugging lastly what i like to do is it just include very simple low level functions and these are just there to read the sentences registers or write to the census registers so you have a read register function where i can just pass the address of a register and a pointer where i get the data out and this is just one byte i'm reading i also have a second function which is read registers which will read multiple bytes of data starting from a certain register address saving the data in this pointer and the given these number of bytes finally i have a write register function down here which does the same thing just in reverse so i give my struct i say which register i want to write to and what byte of data i want to write and that's pretty much it so now we've written our header file we can move over to the adxl355.c source file and then actually implement these functions so we're going to write the initialize we're going to write the lower level functions and we're going to write the read temperature and read acceleration functions of course using the datasheet so here we are in adxl 355.c and first of all we'll look at the low level functions because we'll be using these in pretty much all of the other accelerometer or driver functions we'll start off with the read register function again we're passing the struct as a pointer we're saying which register we want to read and we're passing a pointer where the data that will then be stored and we're returning the house that is typedef in essence this is just a rewrite of a function just to make it a tiny bit more elegant a bit simpler i'm using the hal i squared c mem read function again if i control click on this function it'll take me to the relevant driver and i can actually see how it's written and luckily st has done this work for us so all we have to do is look at what arguments it takes so the first argument is the iscsi handle then we have which device address so this will be our sensor address shifted left by one the memory address so which register you want to read the memory address size this will be eight bits then a pointer where you want to store that outcoming data how many bytes you want to read and what timeout will have in milliseconds i believe so if we go back to our driver that's all what i've filled in so i'm calling the hell i squared c memory function to read a register i'm passing the iscsi handle we've stored in our struct then i'm saying read from our defined i squared c address which we defined in adxl345.h i'm saying i want to read the register we passed to this function this is a define saying okay we want eight bits or one byte of data to be read i'm passing the pointer where i want to store the data and i'm saying i want to read one byte of data and i'm not giving it essentially a short timeout i'm saying the how maximum delay is how long i'm willing to wait for this transaction to finish now how i squared c mem read actually returns the health status type def so all i'm doing in this function is returning the outcome of how i square c memory so really really simple and essentially just to rewrite of that iso cmem read function so i don't always have to pass i switch c address uh the mem adds size 8 bit and how much data i want to read for the read registers function where i'm reading more than one byte all i'm changing is that i have another argument which is the length and i'm passing that to my memory function and that's it the write which is a function uses the hell is good c mem write function so instead of memory it's been right and again i can control click and see okay what arguments does it take and it's pretty much the same as before other than the fact that i'm writing the data that's in this pointer here and that's all i'm doing again just rewriting the iso c memory function to be a bit neater and that was it for the low level functions so really really simple thanks to the how that st provides with the low level functions being written we can actually now return to the adxl355 initialize function and if you remember back this is where we actually set up our struct we initialize i squared c handles we perform sensor initializations for example setting our output data rates our filters and so forth so we take in the struct and we pass the handle from our main function the first thing i do is then actually set the struct parameters over here so i store the i squared c handle as a pointer in the struct so we don't have to keep passing this i squared c handle at every function and i set all my measurements to zero okay so the way i sometimes do this and this is a very very crude driver so there are much better ways of doing this but essentially because i'm having so many transactions in this initialization function so every time i read or write to register i'll get a hell status type def i'm essentially adding up my errors and this is my error number which i start at zero and every time i get an error or busy or timeout or something i'll add one to this and finally i'll return this error number at the end of the function to indicate to my main function okay this is how many errors i've had there are much better ways of doing this but for simplicity's sake let's just stick with this so the first thing i always do with the driver is actually check as i said before the device mems and part ids for this specific sensor again this was on datasheet page 32. i'm creating a variable where i store the resulting register data which then have to pass as a pointer to my read register function so again i'm calling my low level function which we just saw i'm passing my struct i'm saying okay i want to read from my with the register which is called device id and i'm passing this variable by reference then i'm storing the status to see if it's hello k how busy time timeout and so on if the status is not hello k i'm adding one to my error numbers so basically i'm checking if this transaction goes all right i'm reading this register and storing the result in this 8 bit variable if the register data is not equal to my device id and again my device id i stored in my defines in adxl355.h so if the device id isn't equal to oxad there's pretty much no point in continuing because we can't really talk to the sensor that's why i just returned 255 to indicate okay the device id doesn't match i'll do the same thing for for example the mems id so i read the register for relevant to mems id i check it actually is the mems id if not there's no point in continuing and a third try of course with the part id so this is just to check does the communication work and is this the sensor we actually want to be talking to now once we're we've passed this point and we haven't returned from this function due to an error or something like that and we could of course check if the error number is greater than zero at this point because then there probably isn't any point in continuing but for now let's just continue assuming as long as the device ids mems ids and part ids are correct we can then configure the sensor and that's what we're going to be doing now so here we are back at the adxl355 datasheet and looking at the register map so for initialization we want to see what registers are actually of interest to us for setting up the sensor so for example the x data wide it is that data for initialization that's not important we already checked the part id device id mems id and so on what we might want to do now is actually look down here for example the filter settings so this sensor actually includes a low pass filter and high pass filter and we can set essentially the cutoff frequencies for those filters as well as the output data rate of the sensor and that's something that's pretty important for this type of sensor we can also look at this sensor here to set what actually appears on the interrupt pins of this sensor another thing we can do is set the i with c frequency and then actually get this device out of standby mode in our case just this being a really simple driver all i'm going to do is set the fill to high pass and low pass corner frequencies as well as the output data rate and then i'm going to get the sensor out of standby mode so let's first of all look at the filter register which is at ox28 and if i scroll down i actually get a more detailed description of this filter settings register now it's addressed at ox28 which we already stored in our defines in our dot h file and the default setting you can see here which is oxo that means okay the first bit here is reserved that's the most significant bit if it's zero zero zero the next three bits that means no high pass filters enabled and the last four bits essentially mean okay we have a 4000 hertz output data rate and a 1000 hertz low pass filter corner all right so 4 000 hertz output data rate seems a bit much initially for me for my application i might want to use it for i don't want a high pass filter okay so let's see how we would want to maybe set these bits so the first bit reserved so that's just zero then i would don't want to have a high pass filter in my application so the next three bits are three zeros and then i want to set my output data rate and low pass filter so four thousand and one thousand seems a bit high to me i'm going to want something much slower for example so i'm going to use 125 hertz as my output data rate and 31.25 hertz is my low pass filter corner so that's why i have to set the last four bits as zero one zero one so overall actually my bits are zero most significant then three zeros and then zero one zero one and those are my eight bits to give me one byte so zero zero zero zero zero one zero one now this is in binary and we want to convert this to hex now if you aren't that familiar with binotex conversion or the other way around you can use something like a binary hex converter which you can just google online i'm typically use this rapid tables one so i can enter my binary number which i copied depending on the register settings i want and then i click convert then i would like to set the register contents to be hex five or ox05 so if i go to cube ide you can see that's exactly what i've done i just wrote in my comment that i don't want a high pass filter i 125 hertz output data rate and a 31.25 low pass field to cut off that's why my register data is 0x05 so i set my register data then i call my low level function to write the register which is the rake filter and i pass this register data and this will make sure according to the data sheet that we have no high pass filter we have 125 hertz output data rate and a 31.25 hertz low pass field corner and that's all there is to that finally the last part of our initialization function is actually looking at this power control register which is located at address ox 2d now default the reset value is ox01 and let's see what that actually means okay so the first five bits so seven to three are reserved so they're all going to be zero and the data already off bit means essentially that there's no not going to be a data ready output on that pin if we set this high which we don't want we want this data ready pin to go high when there's new data and temperature processing we don't want to disable so this has to be zero this has to be zero and this has to be zero but the last bit by default given by this reset value here is actually one and if it's one it's in stand by one so it won't actually do any processing and won't give it any measurements that's why we have to actually change this one to a zero because you want to put this sensor in measurement mode so essentially this whole register has to be zero for this to turn on to gather measurements and to process temperature data and to put a data ready on the data ready pin so if we go back to stm32 cube ide you can see that's exactly what i did so i'm writing ox00 to put the sensor to measurement mode again using the right register function addressing the correct register and passing this register data now if this all goes well this whole function and we've passed the part id msid and device id and so on we can return the number of errors now as i said before if the number of errors is zero we have a successful initialization which we can check in our main function and that concludes actually our initialization function so now let's look at our first measurement function and that'll be for reading the temperature data so the accelerometer also includes a temperature sensor which gives us uncalibrated temperature data and essentially we can get this from two registers which we have to combine and then convert to degrees celsius the address is at ox06 and 0x07 and we can see how the bits are formed here so for the first register we have some reserve bits and then we have four temperature bits which forms the most significant part of the 12 bit temperature data and they also have this other register which forms the lower eight bits or the least significant bits of this temperature data we also see here the nominal intercept which is one eight five two lsbs leads significant bits at 25 degrees c which is essentially an offset and we have a slope of minus 9.05 lsbs per degree c so essentially we need to find out how can we read these registers combine the information and then convert that to sensible units for example degrees c i have already written this function in stm32 cube id the read temperature function and i'd like to go through this how again from the data sheet to actually writing this code so here i've written a little note of how i did it so we're looking at the temperature data first which essentially comes as two eight byte registers where only 12 bits of those actually useful to have temp two here at ox06 and temp one at oxo seven now the first four bits here are reserved they're useless to us and then we have another four bits and then we have these eight bits here so 12 bits total going from most significant to least significant we want to read temp two first we don't care about the first four bits but we care about the last four bits so what we have to do if we have a u and eight t type is actually and this so logical and with a bit mask so we can say zero zero zero if we and that with this these will be all zeros and if we add one one one one with this we'll get all of these four bits here and then hex 000 is oxof so we have to read temperature 2 and and it with this bitmask to extract these four bits then we have to left shift by eight bits so we have four bits here if we left chip by eight our register or data width will be twelve bits and we have to store that in a larger variable of course so that'll be u and 16t then we can form our temperature raw which is our essentially left shifted temp 2 register and of course bitmast and then we want to put the temp 1 register which is the lower 8 bits or the 8 least significant bits we're going to put that right next to it and we can do that by logical or so we put temp 1 here and we put temp 2 bit mask and left shifted by 8 here and we saw that all in a un16t variable so if you go over to the code that's what i do i read essentially those two registers starting from temp two and then they're reading temp one and i store that in reg data then i combine the register values to give the raw temperature reading which is twelve bits so i bit mask temp two left shifted by eight and or it with temp 1. now we have to convert it to degree c the way we do that is of course from the data sheet again we saw that there's an intercept at 1.852 at 25 degrees c and we have this slope here essentially all we're doing is a linear mapping so we have the offset and we have a slope we want to go to from lsb's to degree c's now when temp raw is one eight five two the data sheet tells us that the temperature in degree c is actually 25 degrees so therefore we have to subtract one eight five two from our raw reading and add 25 and that takes care of our offset then of course we have this slope but they give it to us as least significant bits per degree c we actually want the inverse of that so we want how many degrees c do we get per least significant bit so we just take the inverse and we find out it's some sort of constant then we can add these two together so the offset and the slope and that's how we convert our lsbs or raw data into degree c that's exactly what i've done here so i have my inverse slope and my offsets here now i could make this nicer by writing defines for this this and this but this is just a bit easier to see or simpler to see for this video and that pretty much concludes our read temperature function over here let's move over to the accelerometer function for the raw accelerometer data we actually have this x-axis data register y-axis and z-axis but the procedure will be the same for x y and z so i'm just going to show it to you for one axis so we have three registers one by each so 24 bits in total we have to read now only 20 bits of this are actually significant data as you can see here the first data register all eight bits are what we want the second register all eight bits are we want for the last register only the top four bits are what we want the last are reserved we'll have to mask and then also bit shift one important fact here is that is actually formatted as two's complement and that'll be important later because it'll actually be assigned value that we get out of it again let's look at some notes i made accelerometer we're eating 24 bits but only 20 bits are significant for us and we have to remember it's two's complement so here's the structure as usual with the most significant data first on the left then we read our next significant data and then we have our least significant data over here so this is a whole register this is a whole register and this is a whole register but for the last register we are only interested in the first four bits of that register again we'll have to use a bitmask to get rid of these bits so the usual procedure as with many drivers is to bit shift combine and mask in a sensible order to form the raw data which is then 20 bits and again this will be a signed integer so i'm using an n32t type to score these 20 bits of data so essentially i can left shift the x data 3 by 24 or it with my x2 data shifted left by 16 and then or it with this state of x data 1 with my bitmaster oxfo meaning 1 1 1 1 0 0 0 0 extract these four bits and then left shift eight once i've combined those i've got a 32 bit sign integer but i want a 20 bit integer so i need to right shift by 12 to go from 32 bits to 22 bits but i'm still storing it in an n32 on a score t again looking at the code for the accelerometer i'm reading the registers for xyz so 24 bits each and storing in this reg data array then i have my raw signed variables of type in 32t 0 1 and 2 for the xyz axes and i'm doing exactly that i'm casting to the n32t left shifting left shifting bit mask and left shift and finally right shift by 12. exactly what i showed you in the notes then of course we only have raw values and those don't really make sense because we want to convert them either to units of g or meters per second squared so let's see how we actually do that again if we look at the data sheet for this sensor we can actually see that the default range for the adxl 355 is plus or minus 2.048 g so all of our bits essentially map to plus or minus 2.05 g we can write 2.048 g as 2 to the 11 divided by a thousand so to the 11 is 2048 and we divide by 1000 to get 2.048 times g we also need to know that the number of bits this maps to now we said accelerometer data is actually 20 bits but it because of the two's complement we actually lose one of these bits for the sine bit which is the most significant bit so actually we have 20 bits minus one bit giving us 19 total bits so 20 total bits minus the two's complement or the sign bit another way of seeing this is that we have plus or minus 2.048 g essentially half of the numbers are mapped to 2.048 and zero and the other half of the numbers are mapped between 0 and minus 2.048 so essentially our sensitivity which is our meters per second squared divided by our least significant bit so how many meters per second squared do we get per unit is our range divided by 2 to the power of number of bits and if we work that out we get a constant which we then use to convert our lsbs or raw data to the meters per second squared that's exactly what i've done here given the range setting of plus or minus 2.04 he i do g times this constant and then times the raw data and i do that for the x y and z axes and that's pretty much it now that we've written a pretty crude driver for the adxl355 accelerometer we can go back to main.c and actually use our functions and include our driver and trying to see if this actually works so as i said before i've included my axl 355.8 header and then i'm going to use the functions we defined in there so the first thing i have to do is actually create my adxl 355 struct and i'm just calling it for short what i've also set up is an actual external interrupt pin which fires every time the data ready pin of the accelerometer goes high so essentially every time it goes high i set my data ready flag so every time there's new data i'll set it to one and i'll check in the main while loop if data is ready and then i can do something with it so here in our main function i have to of course initialize the accelerometer so i passed my struct that i defined earlier and the i squared c handle which in this case is i squared c1 i will also be printing data via the usb virtual com port so you have a little buffer here i have a timer that just essentially counts at a fixed sample rate when i need to send data via the usb and also when i want to toggle my led so initially i set my data ready flag to zero and then i start my main while loop so this main while loop essentially checks with this if statement if the data ready flag has been set by the interrupt if it has i read the temperature first and then i read the accelerations after that i clear the flag so the interrupt can set it again at a constant sample time which i've defined at the top of my file i actually send the accelerator readings and the temperature by the virtual com port and i can do that using these functions here and lastly i toggle my add using these functions over here but the main thing is that as soon as the interrupt fires i set a data ready flag i read the temperature and i read the accelerations so let's upload this to the little brain board and actually see if we read the great device part ids if the initialization works out and if you read sensible temperature and acceleration values so i have my little brain board plugged in via the usb cable as well as an st-link v2 connected to it so i can click on this bug up here which compiles and then uploads the firmware to my device so let's go into the adxl345.c file and actually set some breakpoints to see what's going on so i'll just set one for example after our first check of the device id and then i'll check just before we return our own error number so let me click resume and as you can see okay it's try to read the register the status is hello k which is great which means we can talk to the device we've read the device id so let's see reg data is set to oxad as you can see down here now let's check the adxd adxl 305 device id is actually oxad so this check will pass but that's really good so we click continue we can actually see we have zero errors all the right and all the reads seem to work so this sensor seems to be configured okay so i'll just get rid of these break points here and here and now set new breakpoints in my read functions so i'll do that just after i calculate my temperature in degree c as well as once i've converted to meters per second squared for my accelerometer values so let me click resume and hope the interrupt triggers there we go it triggered and i've read my temperature so if i just hop over here temperature is about 24.3 degrees and that's about room temperature so that seems about right let me hold my finger on the accelerometer and just resume a couple times get some more measurements and you can see the temperature actually went up to 25 degrees because i got my finger on it okay so that seems to work quite well and seems to give a sensible reading for the temperature that's good news i'll click play again let's check the accelerometer values so i've currently have the board flat on the table which means i should get about 9.8 on the z-axis and that's what i seem to be getting here so let me rotate the board to one side click resume a couple of times there we go and you can see i've turned it to one side and now the second here this y-axis is now 9.8 that's really good so if i turn it one more time just to check the x-axis and here we go we get about 9.8 on the x-axis reading so it looks like our conversions work reading the sensor works are interrupt fires and so forth so it looks like our very crude driver is working so thank you very much for watching this video i hope it was helpful again everything you need to write a driver is usually in the date sheet also for doing the schematic and so forth if you like the video please give me a thumbs up and i hope to see you in the next one thank you very much
Info
Channel: Phil’s Lab
Views: 75,280
Rating: 4.9516835 out of 5
Keywords:
Id: _JQAve05o_0
Channel Id: undefined
Length: 38min 21sec (2301 seconds)
Published: Fri Aug 20 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.