Member | Action | Date | |||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
![]() |
Posted
Build your own lightsaber (sounds)! - Part 1 on Blog
In the last chapter, we learned all about how to create sound using an external I2S chip and digital sound files. In this chapter, we will get a little bit more in depth with sound and generate simple tones with a GPIO pin. Then, we will use the integrated 12-bit DAC within the Wonder Gecko to produce sound with a more analog nature and run that through an external audio amplifier. We will blend together sounds from multiple lightsaber sound effects and trigger those sounds with the accelerometer from chapter 10, resulting in our very own lightsaber sound effects generator.
Sound effects for consumer gadgets can be more forgiving than high-end musical audio. We can get by on just a bare GPIO pin if we are careful about the type of sounds that we try to reproduce. For some gadgets, perhaps a few beeps, clicks, and scratchy noises will do the trick. As we move to voice tracks that have a wider range of audio frequencies, more fidelity is needed. The 12-bit DAC on the Wonder Gecko (also available in most EFM32 models, check the datasheet) can be used to generate sound effects, voices, and lower-quality music.
The EFM32 can drive up to 20 mA on a single GPIO pinThis is probably not going to do the trick for the volume that we need on a small speaker. If we tried to drive a speaker directly from a GPIO pin, it could damage the GPIO or just result in low volume audio. In order to boost the volume, we need to amplify it in either voltage swing, current capacity, or both, to produce the desired power and sound levels for the situation.
Since we have already learned how to use an accelerometer with I2C in chapter 10, we can dust that off now and use it to detect motion that triggers lightsaber sounds that are stored on the MicroSD card that we used in the last chapter. We will drive all of that out of the DAC on the EFM32 and then on to a TI differential mono amplifier. But before go there, we will take a step back just a bit and learn about how to make the most rudimentary sound with nothing but a GPIO pi and, a speaker.
The source code for all examples in this book can be found on github, here.
Materials Required for this Chapter:
Sound and Amplifier Theory Audio theory will melt your brain. When you go looking for information on how to build audio circuits for your electronic gadgets, you will quickly run into discussions of physics, calculus, and all sorts of horrendous-looking mathematical expressions. But all I want to do is play some sound effects! I hear you. I ran into this problem when I first tried to build my first audio circuit. I still have a lot to learn, but I will share with you some simple things that I have learned that should give you a good foundation to learn more.
In the old days when we played music on a record player or a cassette tape in a tape player, the audio information was stored and retrieved in a completely analog format. The resulting audio signal consisted of sine waves and were fed into an analog amplifier, then routed through a speaker and turned into sound, which we can hear.
On the one end of the spectrum, we have the old-fashioned and warm tones of the old analog audio format, and on the other end, we have the precisely calculated, perfect-in-every-way digital recordings of the modern era. Yet, there is middle ground! By using nothing more than a GPIO pin and purely digital signaling, we can produce analog sound. The reason why this works is because of filtering. The digital pulses that we will use to generate audio from a GPIO are fast-switching square waves, and the impedance of the audio speaker coil and magnet is an inductor, which is a low-pass filter. It simply won’t allow the high-frequencies of the digital signal to make it through the speaker coil. The impedance of a typical 8Ω speaker limits the switching frequency, so that what is left on the speaker cone is an average voltage over time. If we can alter the average voltage value many times a second, we have a cheap DAC.
Since we have to control the GPIO many times per second, we will use the EFM32 timer circuits connected to a GPIO in Pulse Width Modulation (PWM) mode to make the quick changes necessary from instant to instant in order to reproduce a sinusoidal waveform. The rule of thumb is to run a sample rate that is 10 times as fast as the frequency of sound that we want to create and a PWM switching frequency is that is 10 times as fast as the sample rate. So if we want to create a 4 kHz tone for example, then we should use a 40 kHz sample rate and a 400 kHz PWM switching frequency. We will vary the PWM duty cycle every audio sample, which will then run for 10 PWM clocks, before the PWM duty cycle is changed for the next sample.
Once we have created an analog signal from the digital audio data, we need to amplify it if we want to be able to hear it loud and clear. This is another area of electronics where your research can cause your head to explode. There is an enormous amount of information and opinions on the Internet, and it can get you sidetracked from finding your way to a solution. There are many ways to amplify a signal. The type of amplification that we will be learning about in this chapter is known as Class D, but also goes by the name of PWM or simply digital amplification.
Class D amplifiers use the same PWM principle that we have just learned about, but are connected to more powerful current and voltage sources than the source audio device. They are up to 95% efficient because at times where there is no audio data, the PWM duty cycle is 50%, and the output signal stays quiescent on both inputs to the speaker. If there is no change in output voltage to either pin of the speaker, then there is no current consumed by the speaker. Class D amps are more efficient than Class A, B, or AB, which are all more analog in nature and can waste power when there is no audio data to amplify. Later in the chapter, we will use TI’s TPA2005D1 Class D differential mono amplifier.
In the next section, we will create a simple audio tone on a GPIO pin, examine that waveform on an oscilloscope.
|
Nov 08 2017, 9:04 PM |
|||||||||||||||||||||||||||
![]() |
Posted
Choosing Electronic Parts for Your Project - Part 1 on Blog
In the last few sections, we summarized the process to create your own PCB. This book/blog series attempts to show you how to design, program, build, and test your prototypes, eventually turning those prototypes into production-worthy gadgets. Every electronic circuit need is different, however, and your solution to your problem will need specific circuits to solve that problem most efficiently and cost effectively.
So you have an idea for an electronic gadget...great! The trickiest step in the whole process is figuring out which electronic components, or parts, are needed to make up your circuits. This is not a step to be taken lightly. There are a billion ways to solve any problem and seemingly a billion parts out there to help you do it. First off, you have to find the components that make your solution possible, and then you will likely need to refine the design such that it is economically viable and power efficient. There are so many parts out there that it is hard for anyone to know all of the possible options. It takes a lot of research.
Often, electronics designers stick to what they know and have experience working on from past projects. This is with good reason; part specifications can be long, confusing, and sometimes inaccurate documents. Designing with something that you already know can vastly improve the chances of the prototypes working in short order. However, technology is always moving forward, and new parts can combine functions of other parts or improve cost and performance over past technologies.
This section aims to give you an overview of the types of parts that are available and teach you the terminology necessary to find the types of parts needed to solve your circuit problem. Your parts will be identified by a set of specifications that detail exactly what the parts can do, how much power they consume or dissipate, the temperature they can withstand, how they mount to your board or how they are packaged, the type of material makeup, and their size and weight. This is just for starters.
The best resource at your disposal to learn about the available options are the electronic part distributors such as Mouser, Digikey, Arrow, Avnet, and Newark, but prepare to be overwhelmed with choices! You can also find harder-to-find or specialized parts on eBay and Alibaba.
This post starts with the basic circuit elements and works up to the more interesting active, smart circuit devices.
Note About Packaging The packages of electronics components refer to how the parts mount to the circuit board. Most parts are either soldered to a Printed Circuit Board (PCB) with a through-hole method or with Surface Mount Technology (SMT). Within the SMT category, devices can utilize legs, pads, or balls to connect to the PCB. Some parts can be wired into your circuit through wire terminals or lugs. Yet other devices can utilize specialized connectors between the device and the circuit board.
The simplest of all components are discrete passive components. These parts only have a singular purpose, such as offering resistance, capacitance, inductance, or adding a voltage or current control. Most discrete components only have two or three pins. Although these are very simple devices in theory, there are still a million choices to be made regarding the construction of each device. Electronics engineering is a very specific industry, so be prepared for that.
Potentiometers are variable resistors that have three terminals. The potentiometer is simply a resistor that has third pin to act as a mechanical “wiper” that allows the resistance between terminals 1 and 2 or 2 and 3 to be varied. Potentiometers can be used to allow user input into a circuit, for example a volume knob on a stereo. They can also allow fine tuning of a design during the production process. Potentiometers are ordered according to the total resistance offered and the degrees of turn, or number of turns that the wiper allows. Higher accuracy and more turns increases the cost. Potentiometers have no polarity are packaged in through-hole and SMT packages for production but are commonly implemented with wire lugs.
Ferrite beads, also known as chokes, offer impedance to a circuit. Ferrites are primarily used to prevent high-frequency noise from propagating past them and are often found on the end of cables that connect devices together. They can be ordered in through-hole, SMT or as a wire shield.
Transformers are essentially two inductors that are packaged together to transmit alternating current (AC) power from one circuit to another through electromagnetic induction. They are commonly used to transform an AC voltage from one value to another. The number of windings in each inductor dictates how the voltage will be transformed. In this way, they are a simple AC voltage converter, based upon how the transformer is constructed. Transformers can be packaged in a variety of different packages from SMT all the way up to house-sized buildings for commercial power delivery.
Diodes are devices that either permit or block the flow of current in a circuit, based on the voltage on its terminals. The current can only flow if the anode terminal is at a higher voltage than the cathode terminal, usually by at least 0.7 volts. Diodes are like a one-way valve for voltage, and prevents voltage from going the wrong way. They are used in power conversion from AC to DC and to protect circuits from an out-of-bounds voltage input. A Zener diode is a special kind of diode that is used as a power regulator. A Schottky diode has a lower forward voltage drop (in the 0.15 to 0.45V range) and a faster turn-on time.
Light Emitting Diodes (LEDs) share many of the same properties with normal diodes, covered above, but are primarily used to generate light.
Photo Diodes perform the opposite function of LEDs, whereby they produce electrical current if light is present. These devices are useful to detect the presence of light.
Transistors are versatile circuit elements that are the most complicated of the passive discretes on this list. The three-pin devices can be used as voltage or current-dependent switches, or as power amplifiers or regulators, depending on the circuit. If you have a problem, there is a solution that can be created with transistors, as a microprocessor is at its heart a very large collection of transistors. There are two general technology types, Bipolar Junction (BJT) and Field Effect (FET.) These two types of transistors differ in pin terminology. The BJT transistors have two sub types, known as NPN and PNP which describe the polarity of the component. The FET transistors have many sub types that pertain to n-channel and p-channel. More information on the types of transistors can be nicely summarized here. Note that the direction of the arrows in the symbol change based on the type of transistor it is describing.
In the next section, we’ll cover more parts that every project likely needs: switches, connectors and sensors.
|
Nov 08 2017, 9:03 PM |
|||||||||||||||||||||||||||
![]() |
Posted
National Week of Making Special - Make your own Printed Circuit Board (PCB) Part 2 on Blog
In the last section, you learned all about how to create the design files that will bring your project from just a figment of your imagination all the way to a working prototype and then into a design that is ready to be manufactured. In this section, I will give you an overview of the process to take those design files to market.
Step 6: Manufacturing Prototypes to Production If you have made it this far, you have a design file that represents the PCB artwork to be laid out in copper. This is where a real board is finally created from all of your careful planning and months of researching, breadboarding, and designing. You have many options to turn that artwork into a real, physical board and then assemble that board with real components.
Manufacturing Terminology
Manufacturing Options By Phase The first PCB’s that you bring up will not be perfect. You will not go straight to production. Despite all of your hard work in schematic and layout, you will fail to connect everything perfectly. It is highly recommended to build just a few boards in the first batch and then bring up just those few to ensure that the artwork is correct before ordering thousands upon thousands more. Then, a pilot build will help hone your process for the onslaught of thousands or even millions of boards. Do you want to find out that your process is broken after you have developed 100 boards or one million boards?
Prototyping Manufacturing Phase – DIY Options The very first time you build a PCB for a new design, the quickest and cheapest way to get it done in very low volumes is to simply do the work yourself. There is some upfront investment in tools that has to be made, but you can continue to use those tools for future projects. There is also a real risk that you will do things improperly and create your own headaches. It depends on how willing you are to learn a new skill and take a risk that you could end up wasting your time and effort.
Old-School Board Etching In order to build the PCB yourself, you have some options. The old-school way to build your own board was to purchase copper-clad circuit board blanks, lay plastic traces on the board, and then etch away the exposed copper using a powerful and toxic etching solution. This method is fine as long as you have large pin pitch components. It won’t work for today’s modern 1/2 mm pitch components.
Laser Printer Board Etching To reach a finer degree of precision, a laser printer can be used to print the Gerber artwork from a CAD program on special transfer paper, available from PulsarFX as a PCB Fab-in-a-Box kit. A laser printer’s toner is essentially powered plastic that is fused to the paper. By fusing the toner to transfer paper, the idea is that you can reheat the toner and it will release from the transfer paper onto your blank copper circuit board, which is part of the kit. You will need a high-temperature laminator, and it can be tricky to find one that works just right. In my experience with this method, it was a little spotty. Thankfully, you can trace over any incomplete traces with an ordinary marker pen, and that will help resist the etching compound from removing the copper at the gaps in the traces. However, you lose some of the precision that was gained by the laser printer process. In order to create a multilayer board, you must print out each layer and etch it separately, and then glue the layers together. Then, you have to drill through any of your through-holes or vias and make a connection between the layers with solder. With enough practice and experimentation, this method can be successful.
Laster printed etch resist and final product with SMT LEDs
Direct Circuit Printer A recent invention in circuit board prototyping is the circuit printer. There are many exciting developments in this area that will surely produce useful tools for your prototyping work in the near future. Some notables to check out are: Cartesion Co’s Argentum and the soon-to-be-produced 3D circuit printer from Voltera. The big concern that I have for these devices in the near term are with fine pitch devices.
Argenutm and Voltera Circuit Printers
Quick-turn PCB Services The best choice for many makers even with these available DIY choices is to simply order a quick-turn board from a board shop. They can be found with very reasonable prices and ship the boards in a few days. You can still assemble the board yourself, which is where you will get more value.
Ordering Parts However you acquire your PCB, the next step in the DIY process is to assemble the board. Before you can do that, you have to source the parts. This is a time-consuming activity. It is nice if you can find all of your parts from a single vendor, but often you will have to order from several and coordinate shipping so that you aren’t waiting on a single part to start your build. You can’t start meaningful assembly until all of the parts arrive.
When ordering parts, be prepared to make decisions that you never had any idea that you would need to make. You may know the exact major electronic components you will be ordering, but there are a collection of supporting components that need to surround those major components. You will find that there are many more types of resistors, capacitors, crystals and other miscellaneous parts that have a million variants. You have to specify things like brand, package, size, value, tolerance, material, lead or no-lead, temperature, wattage, operating voltage, etc., when all you want is a 1µF capacitor! Your parts arrive packaged in little antistatic baggies or boxes. Now you have to match up each one of those components to a refdes on the schematics and formulate a plan. I line all my baggies up in a shoe box in the order that I will apply them to the board and prepare notes for myself so that when the assembly starts, I don’t forget what I am doing.
If you are planning to have your boards assembled by an outside company, you can save money by ordering the parts yourself. Assemblers will charge a markup on any of the parts they order on your behalf. Just be sure to ask your assembler what kinds of machines they will use so that you get the right kind of packaging. An automated “tape and reel” machine requires that your parts are delivered in that fashion. If you don’t order a full reel of parts, can your assembler accept “cut tape” parts that have been cut off of a reel? Parts ready for assembly
SMT Board Assembly You can assemble your SMT board yourself with the help of a toaster oven and solderpaste. If you had your PCB developed by a board shop, you have the option to order a stencil for the solderpaste. They are laser cut, and you can use this to spread solderpaste across the board, laying it down only on the areas where a component pin will land on a pad. You can get by without a stencil, but there is a higher chance of solder bridges, which are bits of solder that connect adjacent pads. Solderpaste needs to be kept refrigerated, and you only have an hour or so before the solderpaste becomes a liquid, although it depends on the temperature of the room. I have extended it to a few hours with no issues. The hardest part of placing components by hand is to get them to land on the solderpaste cleanly, and then not bumping them when you are placing nearby components.
Solderpaste on a board and then the parts are placed and baked
Oven Baking Commercial SMT ovens bake the boards using a precise temperature profile to ensure that all of the solder melts and you don’t cook your components too long. You can purchase a home SMT oven for a few hundred dollars, which includes the temperature profile. It’s all automatic, which is great. I have only used a regular toaster oven from a household store. The directions that I found online were to simply insert the board in the oven, crank the temperature to 400 degrees, then watch for the solder to melt. The grey solderpaste becomes shiny silver when it melts. You can watch this process happen through the window of the toaster oven, as the temperature washes over the board, in my case from the corners inward. Once all of the solder turns silver, you wait about 15 seconds more and then remove the board from the oven. There is risk with this method of some cold solder joints, and you will have to touch up any solder bridges you find with flux and a fine-tipped soldering iron. Use a 10x microscope if you can find one, and look for issues. Take your time. It is better to find the problem now than when you power up or your board or debugging a software issue. Use a multimeter to check for shorts between power and ground and between traces that are hidden. I have been able to get several good boards out of this process in the first and only time that I tried it. Your mileage may vary.
Prototype/Pilot Manufacturing Phase – Outsource Vendor Option The low-volume board development process is not cheap nor fast. If you want it fast, prepare to spend a lot more. Quick-turn PCB builds can be done for a premium in 24 hours, if you are on a tight schedule. Things get a lot more affordable as you approach a 5-day turn, and become most affordable at a 2-week turn. You will have to quote the boards in many different volumes because you may be surprised to find that building 100 boards isn’t ten times as expensive as building 10 boards. It can be just twice as expensive, for example, and may be worth it to get as many prototype boards as you need for your testing purposes.
When you receive your prototype boards, the first thing to do is a visual examination to look for copper traces that may be touching where they are not supposed to be touching. You will need to refer to Gerber files and layout to compare the real PCB to the design files. Measure a few points with a micrometer. Once the board passes visual checks, you can probe the board with a Volt Ohm Meter (VOM) and ensure that there is no short circuit between the power and ground pins of any device. This is heartbreaking if you find it, because it can be hard to trace back where the error occurs. All vias pass through any internal power and ground planes, and if there was not sufficient anti-pad clearance inside those layers around all vias, you could get a power and ground short. You will need to then check every signal versus power/ground to find the offending signal. If you pass that check, you can then measure to ensure that all signal traces don’t short against power or ground. If you find that all of the boards check out, move on to the assembly step.
The assembly of your electronic components onto a low volume of prototype boards can be very expensive if completed by a prototype assembler vendor. Be prepared for some pricey quotes! To make matters worse, the prototype assemblers are not in control of the schedule. They have large repeat customers that will knock your product to the back burner no matter what they may have quoted you regarding turn times. A fast turn time for the assemblers is five days. It takes time for the assembler to examine the BOM, order the parts, receive the parts, configure the tape-and-reel machines (if used for your volume order), find pin #1 on all devices in your silkscreen and layout files, and then do hand soldering touch ups after your boards have been run through the automated assembly process. You may not have a test procedure for the prototype builds, but it would be helpful to define manufacturing tests by the time the pilot builds occur. The pilot builds are meant to test your process against a larger volume of manufacturing that will follow.
Volume Production Manufacturing Phase When moving from pilot builds to manufacturing builds, you biggest concern will be cost. You certainly don’t want to pay the pilot build prices or you will go broke. You must choose between domestic and foreign assemblers and board shops, keeping in mind the tradeoffs between cost, language barriers, shipping charges, and travel abroad to fix issues. Some assembly shops have domestic pilot lines with foreign volume lines, and those can be a good choice because their teams already work together. This phase is more about business strategy than engineering, and approaches will vary greatly between developers.
That’s the big picture overview of what lies ahead if you plan to bring your own gadget to market.
|
Nov 08 2017, 9:03 PM |
|||||||||||||||||||||||||||
![]() |
Posted
Chapter 11.4: Control RGB LEDs with an LED Controller Part 4 on Blog
In the last section, we built a software driver for the TI TLC5940 LED driver and used it to illuminate some test LEDs. We also figured out how to debug the driver with the help of the Simplicity Studio tools. In this section, we will create some data structures to make it easier to work with named colors, and then improve that driver to make use of the DMADRV library. This library allows us to more easily develop code that invokes the DMA peripheral.
Setting Specific Colors We can develop some helper functions to turn on individual colors per LED driver channel and a color mixer to create standard blended colors such as purple.
typedef struct color_code_bits { uint8_t red; uint8_t green; uint8_t blue; } color_code_struct; #define WHITE { 0xFF, 0xFF, 0xFF } #define RED { 0xFF, 0x00, 0x00 } #define GREEN { 0x00, 0xFF, 0x00 } #define BLUE { 0x00, 0x00, 0xFF } #define PURPLE { 0xFF, 0x00, 0xFF } #define YELLOW { 0xFF, 0xFF, 0x00 } #define ORANGE { 0xFF, 0x0F, 0x00 } // Sets the color in memory but does not write it void set_color_buffer(uint8_t led_number, color_code_struct color) { const uint8_t ch0 = led_number * 3; const uint8_t ch1 = ch0 + 1; const uint8_t ch2 = ch1 + 1; // Shift the 8-bit RGB code over by 4 to get to 12 bits of GS stream.channel[ch0].grayscale = (uint16_t) (color.red << 4); stream.channel[ch1].grayscale = (uint16_t) (color.green << 4); stream.channel[ch2].grayscale = (uint16_t) (color.blue << 4); }
These functions can then be used to set whatever color codes we want per channel, like purple/yellow/purple:
const color_code_struct purple = PURPLE; set_color_buffer(0, purple); const color_code_struct yellow = YELLOW; set_color_buffer(1, yellow); set_color_buffer(2, purple); // Now send the stream to the TLC5940 stream.mode = GRAYSCALE_MODE; // Now write the GS data write_serial_stream(); This resulted in a nice purple color on the first and third LED, but the yellow LED had a bit of a greenish tint. Likewise, setting all colors to 0xFF resulted in a blue-ish white. It seems necessary to compensate for the relative brightness of each color LED in the RGB set to get the blended colors just right.
Introduction to Direct Memory Access (DMA) We used DMA in the last chapter with the help of the SPIDRV library, but we didn’t have to program anything in the DMA engine ourselves. We could use SPIDRV again here, but instead we will learn another way to set up DMA ourselves so that we gain a deeper understanding of how it works.
The DMA peripheral is a highly-configurable tool that moves data from one place to another, and it does so without help from the MCU once the transfer is started. This allows the MCU to sleep or do other things. There are multiple channels available to perform multiple transfers between different sources and destinations. The DMA transfer can be triggered through many sources and generates an interrupt when it is done moving the data. It is a simple mechanism, but it is sometimes difficult to understand and can be harder to debug than sequential programming. The toughest part is understanding how to set it up. Fortunately, Silicon Labs has provided a DMA driver called DMADRV. To use this driver, all you need to do is include the dmadrv.h file at the top of your source code as well as copy the required files from the Simplicity Studio emdrv directory. See the example in Github here for the required files that I have copied into the project src directory.
The DMA peripheral can also be set up through use of the em_drv library. There are examples in Application Note AN0013 Direct Memory Access that configure things more manually. In short, the DMA peripheral looks for something called a descriptor table located in RAM that holds the configuration of the data transfer. This configures the DMA behavior, along with the configuration registers that reside inside the DMA peripheral. All of these things need to be configured, and then the DMA peripheral can do the transfer once its trigger event occurs. The descriptor table is used by the DMA peripheral at the trigger event to determine how to complete the transfer, including end addresses, address increment size, number of transfers, etc. When the transfer is complete, the DMA peripheral can be configured to issue an interrupt.
There are four modes of operation of the DMA peripheral. DMADRV only simplifies the use of the Basic DMA mode, but does not do too much to help with the other advanced modes. That is good for this example, since we only need Basic DMA mode for transferring data from our array in RAM to the USART peripheral.
To use the DMADRV, all we need to do is put a bit of code in the beginning of our main program to initialize the DMADRV and let it find an open DMA channel for us. This is great, because we don’t have to worry about using the same channel that SPIDRV or some other module is already using.
// These variables are global, at the top of the file unsigned int dma_channel; // Transfer Flag volatile bool dma_in_progress; // The following code is in the main function Ecode_t result; // Initialize DMA. result = DMADRV_Init(); if (result != ECODE_EMDRV_DMADRV_OK) { DEBUG_BREAK } // Request a DMA channel. result = DMADRV_AllocateChannel( &dma_channel, NULL ); if (result != ECODE_EMDRV_DMADRV_OK) { DEBUG_BREAK } Now, we have a DMA channel that we can pass into the DMADRV functions. To make the transfer, we can replace the for loop that we used inside of the write_serial_stream function with the DMADRV_MemoryPeripheral function. First, we define the callback function that is called when the DMA transfer is complete:
void dma_transfer_complete(unsigned int channel, bool primary, void *user) { // Clear flag to indicate that transfer is complete dma_in_progress = false; } Next, we have to change a few things inside of the write_serial_stream function to handle the DMA transfer. If you recall from the previous section, we incremented through the serial_stream array backwards in order to send the most significant byte first on the USART, as the TLC5940 expects to see the data.
// Now write the stream, MSByte first for (int i=length-1; i>=0; i--) { USART_Tx(USART1, stream_buffer[i]); } But we can’t do that with DMA, as it can only iterate through memory in the forward direction. So we have to fill the serial_stream array backwards instead, and then the direction in which the DMA peripheral fetches the data from memory and pushes it to the USART will be in the correct order.
// This must be global to be used for DMA uint8_t stream_buffer[MAX_STREAM_BIT_LEN/8]; void write_serial_stream() { int length; // Must pack the bits in backwards for DMA driver if (stream.mode == DOT_CORRECTION_MODE) { length = DC_STREAM_BIT_LEN / 8; for (int i=0; i < length; i++) { stream_buffer[length-i-1] = pack_dc_byte(i); } } else { length = MAX_STREAM_BIT_LEN/8; for (int i=0; i < length; i++) { stream_buffer[length-i-1] = pack_gs_byte(i); } } // Set/clear the VPRG pin if (stream.mode == DOT_CORRECTION_MODE) { GPIO_PinOutSet(CONTROL_PORT, VPRG_PIN); } else { GPIO_PinOutClear(CONTROL_PORT, VPRG_PIN); } dma_in_progress = true; // Start the DMA transfer. DMADRV_MemoryPeripheral( dma_channel, dmadrvPeripheralSignal_USART1_TXBL, (void*)&(USART1->TXDATA), stream_buffer, true, length, dmadrvDataSize1, (void *) dma_transfer_complete, NULL ); while (dma_in_progress) ; //EMU_EnterEM2(true); for (volatile int i=0; i < 10000; i++) ; // Latch the data GPIO_PinOutSet(CONTROL_PORT, XLAT_PIN); for (volatile int i=0; i < 100; i++) ; GPIO_PinOutClear(CONTROL_PORT, XLAT_PIN); } You can see that we replaced for loop that sent data to the USART within write_serial_stream with the DMADRV_MemoryPeripheral function, which means that the data is flowing from memory to a peripheral. There is a companion function that goes the other way.
Before the DMA_MemoryPeripheral function, we set a flag for dma_in_progress, and then waited for it to clear before moving on. The flag clear takes place inside the dma_transfer_complete callback function when the DMA transfer completes. However, instead of waiting in a while loop for the DMA to finish, we could have let the MCU go work on something else or entered into a sleep state, as long as we take care ahead of time to configure the DMA interrupt to wake the MCU from sleep.
You should notice that the serial_stream array was moved outside of the write_serial_stream function. It is super important that you make all variables that are referenced by DMA globally persistent. In our example, it makes no difference because we wait for the DMA transfer to finish, but as soon as we remove that blocking logic and allow the system to leave this function, the local variable serial_stream will be reclaimed by the system for other purposes and the data will become mangled. By making serial_stream global, it is preserved even after the write_serial_stream function exists, when the DMA transfer might still be in progress.
This should have been an easy introduction to DMA transfers. We will revisit DMA in future chapters to set up more complicated modes and really try to melt your brain.
This completes the chapter on interfacing with an external LED driver over a non-standard serial stream interface. By now, you should be well on your way to becoming a competent solderer and getting to know your way around the initialization of a few EFM32 peripherals. You should also feel like something of an expert in the types and applications of LEDs.
|
Nov 08 2017, 9:02 PM |
|||||||||||||||||||||||||||
![]() |
Posted
Chapter 10.2: Control an accelerometer over I2C Part 2 - Initialize the I2C Bus on Blog
This is part 2 of a four part series on using the I2C bus to interface an accelerometer. In part 1, we learned how to physically connect the accelerometer to the EFM32 Starter Kit board, and how an I2C bus operates. The background theory about how accelerometers work and their uses were also covered. In this section, we will learn how to configure the EFM32 for the I2C bus through the em_i2c driver library. We will illuminate an LED on the Starter Kit to indicate the successful reading of the Device ID register on the accelerometer.
I2C Software Configuration Now you should have your Starter Kit and ADXL345 connected and ready to go. For this section, I chose to run the Simplicity Configurator tool to create all of the necessary HFXO, GPIO, USART (for serial port output later), and I2C peripheral initialization code for me. You can look at Lesson 8 on Serial Communication where that tool was first demonstrated. I went through the steps to select all of the peripherals that I wanted to use and the tool created this block of initialization code within the enter_DefaultMode_from_RESET function in the resulting InitDevice.c file, located in the src directory of the project that the Configurator created:
// $[Library includes] #include "em_system.h" #include "em_emu.h" #include "em_cmu.h" #include "em_device.h" #include "em_chip.h" #include "em_gpio.h" #include "em_i2c.h" #include "em_usart.h" // [Library includes]$ //============================================================================== // enter_DefaultMode_from_RESET //============================================================================== extern void enter_DefaultMode_from_RESET(void) { // $[Config Calls] CMU_enter_DefaultMode_from_RESET(); HFXO_enter_DefaultMode_from_RESET(); LFXO_enter_DefaultMode_from_RESET(); USART0_enter_DefaultMode_from_RESET(); I2C0_enter_DefaultMode_from_RESET(); PORTIO_enter_DefaultMode_from_RESET(); // [Config Calls]$ } The tool also copied over the library files like em_i2c.c and the others that it includes at the top of the file. Within each of these functions, there is more initialization code. For example, the default I2C initialization code is here: //================================================================================ // I2C0_enter_DefaultMode_from_RESET //================================================================================ extern void I2C0_enter_DefaultMode_from_RESET(void) { // $[I2C0 initialization] I2C_Init_TypeDef init = I2C_INIT_DEFAULT; init.enable = 1; init.master = 1; init.freq = I2C_FREQ_STANDARD_MAX; init.clhr = i2cClockHLRStandard; I2C_Init(I2C0, &init); // [I2C0 initialization]$ }
This code shows that I want it to be enabled, functioning as a master I2C device, running at the standard I2C max frequency. I wasn’t sure what the i2cClockHLRStandard enum was all about, so like always, I clicked on the name, then right-clicked and selected Open Declaration. That brought me to the header file which described all of the possible enums for this init.clhr member. I found a comment that said “Set to use 4:4 low/high duty cycle” for this enum. This has to do with how long to keep the clock high versus low during a single I2C bit time. I didn’t know any reason why I would want anything other than the default, so I left it alone.
I then edited my main.c file to include my standard utilities.h helper file and called the setup function, which sets up the SysTick interrupt so that my delay function will work. I then included the call to the initialization function found in the InitDevices.c file. I also added the library header file for I2C which is em_i2c.h. Note that there are some older examples in the Simplicity Studio installation directory that are based on an older i2cspm.h library, which I am not using here.
At this point, I had the following code: #include "em_device.h" #include "em_chip.h" #include "InitDevice.h" #include "em_i2c.h" #include "utilities.h" /**************************************************************************//** * @brief Main function *****************************************************************************/ int main(void) { CHIP_Init(); enter_DefaultMode_from_RESET(); setup_utilities(); // Code to interface I2C device goes here... // Infinite loop while (1) { } }
Thanks to the Configurator tool and the previously-created utilities helper file, I only have to add two lines of code to get everything configured and ready to go. And, I won’t forget to enable the clock for any of the peripherals, like I always tend to do, since the Configurator took care of that for me.
Read the Device ID The first thing that I do when working with a new part is to read the Device ID register. That is a good way to work out any issues with the libraries, connections, and conventions used in the device.
In order to read a register on an I2C device, you have to first perform a write cycle to the offset of the register that you are targeting on the device, and then immediately perform a read cycle to fetch the value of that register. Confusing? It certainly can be. But fortunately, the em_i2c peripheral inside of the MCU and associated software library takes a lot of that work off of your hands by defining the following I2C transaction types:
The first two types of I2C flags, I2C_FLAG_WRITE and I2C_FLAG_READ are used in situations where no register offsets are needed. The ADXL345 device has no use for these two types because there is no data that can be read or written without first specifying a register offset. However, the second two types of I2C flags, I2C_FLAG_WRITE_READ and I2C_FLAG_WRITE_WRITE are used to specify the register and memory offsets inside an I2C device.
These types are illustrated here by what you see on the I2C bus if viewed on a logic analyzer or scope.
The 7-bit device address packet in the beginning of a frame is reserved for the I2C device address on the I2C bus, and it is required at the beginning of every I2C transaction to select the slave that is targeted by the transaction. It is easy to forget that this address is the address of I2C device itself and not an offset into the device’s register or memory space. Then, the R/W bit in that first address packet indicates the direction that data is to flow in the next sequence of data packets that follow, either from master to slave (write) or slave to master (read).
The tricky part is reading or writing a register or memory location on an I2C device, because it takes two I2C frames to make this happen. The first I2C frame contains the I2C address of the device, with the R/W bit set low, indicating that the next data packet(s) will be driven by master to slave. Those write data packets are either register offsets or memory locations that will be read or written by the master in the next I2C frame and can be a single byte or multiple bytes, depending on the I2C slave’s address space. Then, another START bit is sent (called a repeated start), and the I2C device address is once again sent, followed by the R/W bit. The R/W bit in the second I2C frame is what dictates whether or not the data packet(s) of the second I2C frame will be sent from master to slave or slave to master, i.e. a write or a read data packet.
Why is all of this important? Well, as long as you write perfect code and always connect devices together perfectly, you would never have to worry about all of that interface minutiae. However, if you are human and make mistakes, you will need to break out the scope and figure out what went wrong. When I initially tried to read the Device ID register of the ADXL345, I ran into a few problems. I tried to run an I2C_FLAG_WRITE_READ cycle to fetch the Device ID and compare it to the value of 0xE5 as is published in the ADXL345 specification. I wrote the following functions to help me do that:
#define ADXL345_ADDRESS 0x53 << 1 // This shift is important! #define DEVICE_ID 0xE5 #define CMD_ARRAY_SIZE 1 #define DATA_ARRAY_SIZE 10 // Globals for persistent storage uint8_t cmd_array[CMD_ARRAY_SIZE]; uint8_t data_array[DATA_ARRAY_SIZE]; // Used by the read_register and write_register functions // data_array is read data for WRITE_READ and tx2 data for WRITE_WRITE void i2c_transfer(uint16_t device_addr, uint8_t cmd_array[], uint8_t data_array[], uint16_t cmd_len, uint16_t data_len, uint8_t flag) { // Transfer structure I2C_TransferSeq_TypeDef i2cTransfer; // Initialize I2C transfer I2C_TransferReturn_TypeDef result; i2cTransfer.addr = device_addr; i2cTransfer.flags = flag; i2cTransfer.buf[0].data = cmd_array; i2cTransfer.buf[0].len = cmd_len; // Note that WRITE_WRITE this is tx2 data i2cTransfer.buf[1].data = data_array; i2cTransfer.buf[1].len = data_len; // Set up the transfer result = I2C_TransferInit(I2C0, &i2cTransfer); // Do it until the transfer is done while (result != i2cTransferDone) { if (result != i2cTransferInProgress) { DEBUG_BREAK; } result = I2C_Transfer(I2C0); } } // Read a config register on an I2C device // Tailored for the ADX345 device only i.e. 1 byte of TX uint8_t i2c_read_register(uint8_t reg_offset) { cmd_array[0] = reg_offset; i2c_transfer(ADXL345_ADDRESS, cmd_array, data_array, 1, 1, I2C_FLAG_WRITE_READ); return data_array[0]; }
The i2c_transfer function is generic, and could be used for any kind of I2C device for any of the four types of I2C transactions. I then created the i2c_read_register function tailored to the ADXL345 slave to make it easier to read a single-byte value of a register on this device. Once those functions were there, I called them at the bottom of my main function:
delay(100); I2C_Init_TypeDef i2cInit = I2C_INIT_DEFAULT; I2C_Init(I2C0, &i2cInit); // Offset zero is Device ID uint16_t value = i2c_read_register(0); // Set an LED on the Starter Kit if success if (value == DEVICE_ID) { set_led(1,1); } // Infinite loop while (1) {}
It didn’t work. The I2C cycle never finished and was stuck in the while (result != i2cTransferDone) loop. I found the following waveform on the scope: Channel 1 (orange) is SCL and channel 2 (blue) is SDA. This didn’t look like anything I was expecting. There should be two 8-bit pulses for a read cycle. After some single-stepping in Simplicity Studio, I saw that routing of I2C peripheral to GPIO pins caused a glitch on the I2C bus that looked like a START signal to the ADXL345. The order in which the Simplicity Configurator had configured my I2C pins in the InitDevice.c file was backwards. It was configuring the GPIO output and then routing the I2C peripheral to the GPIO pins. The problem with the Configurator will probably already been corrected by the time you read this. Stuff like this happens, and you have to question everything. So I reordered the calls so that the routing happens first and the configuration of the GPIO pins happens last, and the glitch disappeared.
// NOTE: These cannot come before I2C ROUTE operations or a glitch will appear! // // /* Pin PD6 is configured to Open-drain with pull-up and filter */ // GPIO->P[3].MODEL = (GPIO->P[3].MODEL & ~_GPIO_P_MODEL_MODE6_MASK) | GPIO_P_MODEL_MODE6_WIREDANDPULLUPFILTER; // // /* Pin PD7 is configured to Open-drain with pull-up and filter */ // GPIO->P[3].MODEL = (GPIO->P[3].MODEL & ~_GPIO_P_MODEL_MODE7_MASK) | GPIO_P_MODEL_MODE7_WIREDANDPULLUPFILTER; // // [Port D Configuration]$ // $[Port E Configuration] /* Pin PE10 is configured to Push-pull */ GPIO->P[4].MODEH = (GPIO->P[4].MODEH & ~_GPIO_P_MODEH_MODE10_MASK) | GPIO_P_MODEH_MODE10_PUSHPULL; /* Pin PE11 is configured to Input enabled */ GPIO->P[4].MODEH = (GPIO->P[4].MODEH & ~_GPIO_P_MODEH_MODE11_MASK) | GPIO_P_MODEH_MODE11_INPUT; // [Port E Configuration]$ // $[Port F Configuration] // [Port F Configuration]$ // $[Route Configuration] /* Module I2C0 is configured to location 1 */ I2C0->ROUTE = (I2C0->ROUTE & ~_I2C_ROUTE_LOCATION_MASK) | I2C_ROUTE_LOCATION_LOC1; /* Enable signals SCL, SDA */ I2C0->ROUTE |= I2C_ROUTE_SCLPEN | I2C_ROUTE_SDAPEN; /* Module PCNT0 is configured to location 1 */ PCNT0->ROUTE = (PCNT0->ROUTE & ~_PCNT_ROUTE_LOCATION_MASK) | PCNT_ROUTE_LOCATION_LOC1; /* Enable signals RX, TX */ USART0->ROUTE |= USART_ROUTE_RXPEN | USART_ROUTE_TXPEN; // [Route Configuration]$ // Relocated from above... /* Pin PD6 is configured to Open-drain with pull-up and filter */ GPIO->P[3].MODEL = (GPIO->P[3].MODEL & ~_GPIO_P_MODEL_MODE6_MASK) | GPIO_P_MODEL_MODE6_WIREDANDPULLUPFILTER; /* Pin PD7 is configured to Open-drain with pull-up and filter */ GPIO->P[3].MODEL = (GPIO->P[3].MODEL & ~_GPIO_P_MODEL_MODE7_MASK) | GPIO_P_MODEL_MODE7_WIREDANDPULLUPFILTER; // [Port D Configuration]$
The ADXL345 breakout board has an I2C address of 0x53. I had sent in this address to the em_i2c library routines exactly like that. But the I2C address is only seven bits wide and the em_i2c library expects those seven bits to be left justified, so that the zero-th bit is a “don’t care.” This was clearly documented in the em_i2c library header file but I missed it:
/** * @brief * Address to use after (repeated) start. * @details * Layout details, A = address bit, X = don't care bit (set to 0): * @li 7 bit address - use format AAAA AAAX. * @li 10 bit address - use format XXXX XAAX AAAA AAAA */ uint16_t addr; So once I did a left shift on the I2C device address (which I have already fixed in the example code above) with the statement show here, the Device ID was successfully returned back to me and the light lit on the Starter Kit.
#define ADXL345_ADDRESS 0x53 << 1 This is what an I2C read register is supposed to look like. The first 8-bit cycle is the write of the address from the MCU to the device, and the second 8-bit cycle is the read data returning from the device.
Now if you can get that little test LED to light up on your Starter Kit and ADXL345 breakout board, you are finally ready to start doing fun things with your accelerometer!
We will start experimenting with the accelerometer measurements in the next post.
|
Nov 08 2017, 9:02 PM |
|||||||||||||||||||||||||||
![]() |
Posted
Chapter 10.1: Control an accelerometer over I2C Part 1 - Connect the breakout board on Blog
In this chapter, we will learn how to use the I2C bus to configure and read the live acceleration data from an accelerometer. We will connect the circuit to your Starter Kit, create interface code, and then output the measurement data over a serial port to your computer in real time. We will then create an interrupt routine to detect a “freefall” event to trigger an LED on the Starter Kit.
This chapter aims to first teach how to connect to an I2C device with the help of the EFM32 I2C library. I2C is a commonly utilized communication interface for embedded designs. Thanks to only requiring two pins on the MCU, plus the fact that many devices can connect over those two pins makes it very versatile. In order to give us an interesting I2C device to talk to on the other end of that bus, I chose an accelerometer.
An accelerometer is a device that can measure acceleration on one or more axes. It can be used to detect the orientation of your device relative to the earth’s gravity. This can be useful for tilt sensors, to tell your object if it is right-side up, or upside-down. But they can also measure acceleration due to movement, which would be good know if your device needs to know how it is moving through space. And it doesn’t just tell you that it is moving, but how much it is accelerating, and in which direction even if that means that there is no acceleration on your device at all, as in a free fall situation. That information could be helpful if your device is delicate needs to prepare itself for a hard landing!
Accelerometers are a relatively new invention available to electronics developers. Long ago, an accelerometer was a large mechanical device that contained springs and plates. They were heavy, large, and expensive. That has changed for two reasons 1) Miniaturization of the mechanical parts of an accelerometer has shrunk it down to fit on a single surface-mounted chip and 2) they are employed in smart phones the world over, which drives down the cost for the rest of us.
Materials Needed for This Lesson
Accelerometer Theory Modern accelerometers are Micro Electro-Mechanical Systems (MEMS) devices, which means that they are able to fit on a small chip inside the smallest of gadgets. One method to measure acceleration employed by MEMS accelerometers is to utilize a tiny conductive mass suspended on springs. The acceleration of the device causes the springs to stretch or contract, and the deflection of the conductive mass can be measured through a change in capacitance to nearby, fixed plates.
Accelerometers are specified by the following features:
The ADXL345 accelerometer used in this lesson is a digital output accelerometer that has three axes and a selectable g-range of 2/4/8/16 g’s. It connects to the MCU using an I2C or SPI interface. Note that we are going to demonstrate the I2C bus for this lesson, but I2C limits the measurement frequency to 800Hz whereas SPI allows a higher frequency of 1600Hz. Keep that in mind if your application requires the higher frequency.
Connect the Accelerometer to the Starter Kit via I2C The I2C bus is a shared bus that consists of only two signals, a clock line (SCL) and a data line (SDA). A single I2C bus can connect two or more devices and any device can be a master or a slave (the same device can even assume both roles at different times). Only masters can initiate communication. Slave devices are assigned unique 7-bit or 10-bit device addresses through hardware configuration and monitor the bus for their address to be called, responding to any master device that begins a transaction. That means that you have to ensure that each slave device on your I2C bus has a unique address.
An I2C bus requires pull-ups on the SDA and SCL lines to keep the bus at a high voltage level when neither the master nor slave device is driving the bus. The pull-ups are created by attaching each of the interface wires to the supply voltage through an appropriately-sized resistor. The size of the resistor is chosen by the length of trace that will be used, which impacts the capacitance of the trace, and the speed of the I2C bus. Long traces create higher capacitance and require a lower value resistor to return the wire to a high voltage level after an I2C device has stopped driving the wire to a low voltage level. More information can be found about I2C by viewing the official specification here. Sparkfun has a good tutorial here, and there’s another good tutorial from Robot Electronics here.
I found that the location 0 on I2C0 was not available on the Starter Kit, but the second one was available. I connected PD7 to the SCL pin on the ADXL345 breakout board and connected PD6 to the SDA pin on the breakout board. I then connected 3V3 on the Starter Kit to the VIN pin on the ADXL345 breakout board. I then placed a wire between GND pins on each board.
NOTE: The 3V3 pin on the ADXL345 breakout board is an output pin to power other devices, not an input pin for the board. The input pin is labeled as VIN.
In the next section, we will cover software configuration of the I2C interface on the MCU for the ADXL345 accelerometer.
|
Nov 08 2017, 9:02 PM |
|||||||||||||||||||||||||||
![]() |
Posted
Chapter 9.2: Store Lots of Data in an SPI Flash – Part 2: Connecting and Reading the JEDEC ID on Blog
This is a five part series on the SPI communication protocol. In the first section, we learned all about SPI and how to make a SPI flash breakout board from a bare chip. In this section, we will connect the SPI flash to the EFM32 Starter Kit and then write some code with the help of the USART library to fetch the JEDEC ID from the register on the part. We will continue to rely on the Spansion flash chip’s spec to accomplish this.
Flash Chip to MCU Connections The SPI bus requires either three or four wires, depending on the mode. I will demonstrate the use of SPI using the four-wire mode, which is more common in my experience than the three-wire mode. The EFM32 USART supports the three-wire mode and calls this mode Synchronous Half Duplex Communication. In that case, the MISO line is not used and the MOSI line becomes a bidirectional signal. You can read more about it in the Reference Manual.
In SPI terminology, a SPI device can be a master or a slave device, at any given moment in time. The USART peripheral in the EFM32 can be configured as either one. One device has to be the master and the other a slave for a single SPI transaction. We will configure the MCU as master since the MCU is driving the interaction with the flash chip. We might someday configure the MCU as a slave if we have another MCU in a system and intend to share information between the two of them. But we could configure our USART in the MCU dynamically to be a slave on one cycle, then a master on the next cycle.
The Master In Slave Out (MOSI) terminology is an improvement over the RX/TX nomenclature of the serial port connections. Now, a master device’s MOSI output pin connects directly to a slave’s MOSI input pin, and MISO pin to MISO pin. This is great, because the connections are made between like-named, or at least similarly-named pins. Unfortunately the Data Sheet tables for the USART pins don’t specifically list MOSI/MISO, and you still need to know to map the USART TX pins to MOSI and the USART RX pins to MISO. This information is covered in the Reference Manual for the USART peripheral. In addition, some devices don’t use the MOSI/MISO naming and instead use SDI, DI, SI, etc. for an input. If you can find the one that says input, attach that to the master’s MOSI and you will do fine.
The flash chip breakout board has additional signals that must be attached to your Starter Kit. The first thing to look for are the power and ground connections. Whenever you are connecting your MCU to an external device, you need to take a look at the voltage requirements in the spec for that device. This is listed in the Electrical section for the Spansion chip.
The Supply Voltage (also known as Vcc and sometimes called Vdd) requires between 2.7V and 3.8V. Sometimes you will find chips that require 1.8V or 2.8V, which would then need an external power supply to be provided. In this case, the chip can be powered directly from the Starter Kit’s 3.3V supply pins that are labeled as 3V3. Connect the signals as shown in the table below. We are using specific pins of USART1 location 1 for the SPI signals, since those are brought out to pins on the Starter Kit, and just any old GPIO’s for the other signals.
SPI Software Configuration Now that everything is wired up, it is time to communicate with the flash memory chip from the MCU with software. You can use Simplicity Configurator to configure the part like we did in the last lesson, or manually configure your USART to connect to the SPI signals CLK, CS, MOSI (TX in the USART) and MISO (RX in the USART). I will demonstrate how to do it manually for this chapter.
There is a decision to be made about the clock mode when configuring the clock signal in the USART software setup. With SPI, very little is set in stone. It is up to the chip designers to determine exactly how the electrical signals should operate. The USART provides configuration registers that handle the polarity and phase in order to work with all devices. These are referred to as CPOL or CLKPOL (depending on the chip) for the clock polarity and CPHA or CLKPHA for the clock phase. The clock polarity can be set to either 0 or 1, which determines the idle state of the clock line. The clock phase can also be 0 or 1, which determines whether data is latched on the rising or falling edge of the clock signal. This gives four possible choices for clock modes 0 through 3, which make up the four clock modes. There are diagrams in the Reference Manual that describe all four modes.
When configuring the CS line, there is a decision to be made about whether or not the USART peripheral will control the CS line automatically, known as AUTOCS, or if the software will explicitly control the CS line to the slave SPI device. We will need to control the CS line explicitly, because if we were to pick AUTOCS mode and fail to feed the USART the two bytes of data fast enough, the USART-controlled CS line will go high in the middle of the above waveform, and the device will essentially forget all about the command we requested. We need to drive the CS line low for the entire time as shown in the diagram. The AUTOCS mode is better used when we are sending bytes to the USART through another hardware mechanism rather than software.
We will leave the SPI bus frequency at the default of 1MHz. The table in the Spansion spec states that the chip supports SPI bus frequencies up to at least 44MHz, but you should be aware that the maximum that the EFM32 chip can reach is about one half of the HFPER clock frequency. With the default HFRCO clock setup, that means it can only reach about 7MHz. Plus, our breadboard setup is more likely to see noise that could cause glitches at higher frequencies.
void usart_setup() { // Set up the necessary peripheral clocks CMU_ClockEnable(cmuClock_GPIO, true); CMU_ClockEnable(cmuClock_USART1, true); // Enable the GPIO pins for the USART, starting with CS // This is to avoid clocking the flash chip when we set CLK high GPIO_PinModeSet(gpioPortD, 3, gpioModePushPull, 1); // CS GPIO_PinModeSet(gpioPortD, 0, gpioModePushPull, 0); // MOSI GPIO_PinModeSet(gpioPortD, 1, gpioModeInput, 0); // MISO GPIO_PinModeSet(gpioPortD, 2, gpioModePushPull, 1); // CLK // Enable the GPIO pins for the misc signals, leave pulled high GPIO_PinModeSet(gpioPortD, 4, gpioModePushPull, 1); // WP# GPIO_PinModeSet(gpioPortD, 5, gpioModePushPull, 1); // HOLD# // Initialize and enable the USART USART_InitSync_TypeDef init = USART_INITSYNC_DEFAULT; init.clockMode = usartClockMode3; init.msbf = true; USART_InitSync(USART1, &init); // Connect the USART signals to the GPIO peripheral USART1->ROUTE = USART_ROUTE_RXPEN | USART_ROUTE_TXPEN | USART_ROUTE_CLKPEN | USART_ROUTE_LOCATION_LOC1; }
#include "utilities.h" // Re-used from earlier lesson for delay function #define JEDEC_ID_CMD 0x9F int main(void) { CHIP_Init(); usart_setup(); setup_utilities(); delay(100); uint8_t result[3]; uint8_t index = 0; GPIO_PinModeSet(gpioPortD, 3, gpioModePushPull, 0); // Send the command, discard the first response USART_SpiTransfer(USART1, JEDEC_ID_CMD); // Now send garbage, but keep the results result[index++] = USART_SpiTransfer(USART1, 0); result[index++] = USART_SpiTransfer(USART1, 0); result[index++] = USART_SpiTransfer(USART1, 0); GPIO_PinModeSet(gpioPortD, 3, gpioModePushPull, 1); // Check the result for what is expected from the Spansion spec if (result[0] != 1 || result[1] != 0x40 || result[2] != 0x13) { DEBUG_BREAK } while (1) ; }
Execute the code shown and break in on the while loop. You should see a value of 1 in the result array at the 0 position. This proves to a 1/254 chance that we are reading the register that we think we are reading. Had this register’s default been zero or 0xff, we could not be sure that we were reading the right register because 0xff is the default state of flash memory at reset and zero is the usual default state for most configuration registers. But since the default value for this Manufacturer ID is 1, we can be confident that this SPI driver is beginning to work.
You should notice something funny going on in this code. We read a single value from the USART_SpiTransfer function and don’t do anything with the return value, then we read another value before storing the return value. That is because a four-wire SPI bus is ALWAYS bidirectional. When a master drives a cycle out on MOSI, the slave is required to drive something on MISO for every clock edge, even on the first clock. But since the slave doesn’t even know what is being asked until after that first clock, the slave device will either send back a useless value, or sometimes slave devices will use this first bit time as a chance to send back a status message. You have to examine the SPI chip’s spec to find out what it will do. Once the command from the first bit time is latched and processed by the slave, it can respond with the necessary information on the second SPI transfer.
The code verifies that all three values from the JEDEC ID command are as expected, which gives us near certainty that we have indeed read from the Flash SPI device properly and the USART driver is working well so far.
In the next section, we will take a look at these SPI waveforms on the oscilloscopes and learn about more advanced SPI topology.
|
Nov 08 2017, 9:02 PM |
|||||||||||||||||||||||||||
![]() |
Posted
Chapter 8.1: Communicating Asynchronously Between Devices: Overview of USARTs on Blog
In this chapter, we will open our embedded board up to exchange information with the outside world. Up until now, the only link that we have used is the built-in USB programming interface on the Wonder Gecko Starter Kit and some GPIO lines for controlling a few discrete components. Now we will share data across a communication protocol.
Our EFM32 has quite a few built-in communication standards. It is quite the Swiss army knife. We will begin learning about the simplest of these interfaces in this chapter before learning about more in upcoming chapters. You can use these communication protocols to enrich your project by connecting multiple smart devices together and share information. The IoT is all about connected devices, and this is where it begins.
First, we will cover the basics of serial communication protocols, terminal emulator programs, UARTs, USARTs, and serial cables. Then we configure the chip for USART communication using the Simplicity Studio Configurator tool, that takes care to make all of the connections and communication settings for the USART, through the GPIO block and to the outside world. We will build an interrupt handler to let us know when there is data received on the serial interface and see what signals look like on an oscilloscope, to make sure that things are really toggling like we expect. Finally, we will construct a print function so that we can use this newly acquired connection to aid in our debugging process.
Materials Needed for This Lesson
Communication Classes Communication is achieved between devices via three classes of communication methods:
A serial communication method forces the transmitted data to march in a single-file line, as all of the data flows bit-by-bit one after another. This is generally a slower form of communication but requires fewer wires between devices. This type of communication can be transmitted in a synchronous mode, which means that there is a clock line to coordinate the bits at the other end, or without a clock, which is an asynchronous mode. I will be using and explaining asynchronous mode in this lesson.
The large majority of your embedded system operates in a synchronous mode. Nothing ever happens if the clock doesn’t toggle. Therefore, the devices that utilize asynchronous signals must be able to bridge the gap between clocked and clock-less domains. This presents some challenges in finding proper clock sources.
The standard that is most often used for asynchronous serial communication is the RS-232 standard, which is based upon the UART communication protocol. More on this later. This standard describes how devices asynchronously communicate information, including the voltages and optional flow control siganals. It is not required to make use of this particular standard, but it is most common and the one that we will be using for this lesson. However, just because there is a standard doesn’t mean that all systems will work well together. The standard doesn’t specify how software should deal with the data that is transferred. In addition, the RS-232 interface specifies a +5V and -5V as part of the electrical signaling and we will not be implementing that part, as both the EFM32 MCU and the USB-to-serial port adapter don’t use these voltages, but instead use 3.3V and 0V as the logic 1 and logic 0 values. RS-232 also adds optional flow control signals such as the Request To Send (RTS), Clear To Send (CTS), to manage the bidirectional transmission of data between two systems, usually modems, and we don’t need all of that extra signals. So what I am referring to as RS-232 here is really just the packet format and not much more. This happens a lot in technology. It’s hard to keep track of all of the nuances between different specs and even different versions of specs. The people who make the components get confused at times too. That’s why it’s so hard to get things to work together sometimes!
Serial Interfaces and Terminal Emulator Programs In 80’s and 90’s, all computers shipped with a serial port that had a ridiculously large DB9 connector, by today’s standards, and implemented the full RS-232 standard. Sometimes it was necessary to enter into the computer’s BIOS setup utility and turn them on, or assign them to a different address range or IRQ lines to not conflict with your groovy new drawing pad or speech synthesizer. Those were the days.
USB has supplanted RS-232 to be used for most peripheral needs, and wireless links are replacing those. But, the lowly serial port is still used wherever a design calls for a cheap and easy way to get some rudimentary console-based access to a host system. Embedded designers routinely choose serial ports for diagnostics and debug of heavyweight computers that otherwise have multi-GB interfaces. When those beasts fail to boot up properly, the serial port can be the salvation to figure out what went wrong.
Often, the data that is transmitted via an serial interface is ASCII text for human consumption or as commands to be sent to a device. ASCII stands for American Standard Code for Information Interchange and is the binary code behind all of the text that makes up the alphabet, punctuation characters, and other miscellaneous characters. But it is also possible to transfer information data over a serial interface in a pure binary format in order to program a part with new machine instructions, for example. To interface with the serial port on your computer, you will need to find a suitable terminal emulator program.
A computer terminal was widely used in the days before PC’s as the primary human interface to mainframe computers. It had a keyboard, a display screen and just enough smarts to send and receive RS-232 data. Today, since our computers are so much more capable than just displaying text and accepting keystrokes, you can download a program that emulates a terminal in a window on your computer. This terminal will be the portal in which you will communicate with your embedded device. Windows users of any recent installation need to turn to external software to be able to access the serial port. Putty and TeraTerm are two of my favorite free terminal emulators. Download and install one of those now.
Note that Linux and Mac computers have a built-in terminal and a built-in serial port device at /dev/ttyX, in which X is the identifier of the serial port. Serial input and output can be routed to the built-in terminal with just a simple command like:
screen /dev/ttyUSB0 115200
where ttyUSB0 is wherever your USB-to-serial port adapter is located, and 115200 is the baud rate, described below.
Configure the following parameters in your chosen terminal emulator:
The most customary settings going back decades for all of these settings is known 9600 8N1. This means 9600 baud, 8 data bits, no parity, and 1 stop bit. I will actually be using a faster baud of 115200, which is a more modern standard speed. In fact, most implementations can now support non-standard speeds like 115201, etc.
Once you have a serial port adapter connected to your computer and a terminal emulator program up and running, it is time to put them to use and communicate with your MCU on the Starter Kit.
USART Peripheral There is a very powerful Swiss army knife of a peripheral on the EFM32 MCU called a Universal Synchronous Asynchronous Receiver Transmitter (USART). Aren’t you glad that I explained what those big words meant beforehand? What that basically means is that the USART can universally handle just about any kind of serial transfer, whether that be synchronous or asynchronous, and it can receive as well as transmit. The MCU USART hardware, as well the software libraries that are built on top of that hardware, are at your disposal to automate a lot of the data transmission process, allowing your software to do other things and make the programming easier.
You don’t need a USART to make this happen at slower speeds of 9600 baud or so. The serial interface is still around because it one of the simplest of communication protocols and can be designed completely in software in today’s modern embedded systems. However, your resources are always limited, and it still takes some CPU time and energy to toggle GPIO pins to emulate a serial link in software. It is better to use a USART if you have it and allows for much higher speeds like 115200 baud.
We will configure USART instance 0 through the use of the Configurator tool in Simplicity Studio in the next section.
|
Nov 08 2017, 9:02 PM |
|||||||||||||||||||||||||||
![]() |
Posted
Controlling External Devices: Part 4: Button Input and Deep Sleep States on Blog
Controlling the LED Strip with a Button Input
Both pushbuttons are pulled up to VMCU (the supply voltage for the MCU) with 1M-ohm resistors. These are considered to be weak pullup resistors but they are enough to keep the MCU-side of the switches held high to VMCU until a user pushes a button. At that time, the 100-ohm resistor that is tying the circuit to ground sets up a resistor divider that is 100,000 times closer to ground than it is VMCU, and the pushbutton circuit is pulled low. The normally high circuit is momentarily pulled low, and we can detect that change in our firmware.
You might think that UIF_PB0 and UIF_PB1 map back to port B, pin 0 and 1. But they actually map to port B, pins 9 and 10. PB0 stands for pushbutton 0 and PB1 stands for pushbutton 1. Let this be your first lesson in to never assume anything in embedded systems development! There is usually no feedback when you read from the wrong pin and no real way to figure it out except to double check all your connections.
Let’s enable one of these pins as an input and read values from it in our loop. We can use that input to cause the LED strip to blink only when the pushbutton is pushed.
First, create a function to hold a single on/off cycle of LED blinking: void blink_one_cycle(void) { // Turn on the LED GPIO_PinModeSet(LED_PORT, LED_PIN, gpioModePushPull, 1); // Add some delay for(volatile long i=0; i<100000; i++) ; // Turn off the LED GPIO_PinModeSet(LED_PORT, LED_PIN, gpioModePushPull, 0); // Add some more delay for(volatile long i=0; i<100000; i++); ; } Then, create #defines for PB9 as an input button and call that function from within the new code: #define BUTTON_PORT gpioPortB #define BUTTON_PIN 9 int main(void) { /* Chip errata */ CHIP_Init(); CMU_ClockEnable(cmuClock_GPIO, true); GPIO_PinModeSet(BUTTON_PORT, BUTTON_PIN, gpioModeInput, 0); while (1) { // Grab the state of the button, 1 for high voltage, 0 for low bool live_button_state = GPIO_PinInGet(BUTTON_PORT, BUTTON_PIN); // If the button is currently pushed, blink a single cycle if (live_button_state == 0) { blink_one_cycle(); } } } If you have everything wired correctly, pressing the button will blink the LED strip and releasing it will stop the blinking.
Now, let’s make the LED strip toggle, where one button press turns it on and a second press turns it off. In order to do that, we have to keep track of the button state, as follows: int main(void) { /* Chip errata */ CHIP_Init(); CMU_ClockEnable(cmuClock_GPIO, true); GPIO_PinModeSet(BUTTON_PORT, BUTTON_PIN, gpioModeInput, 0); // The initial state of the button is high when not pushed bool past_button_state = 1; // Start out not blinking bool blinking = false; while (1) { // Grab the state of the button, 1 for high voltage, 0 for low bool live_button_state = GPIO_PinInGet(BUTTON_PORT, BUTTON_PIN); // Invert the blinking mode every time a button is pressed // which generates a low voltage on a pin if (past_button_state == 1 && live_button_state == 0) { past_button_state = 0; // Invert the blinking mode, so that it is buffered and will // keep blinking/not blinking when the button is released blinking = !blinking; } // Reset the past state when the button is released if (live_button_state == 1) { past_button_state = 1; } // Finally decide if there is going to be a blink cycle or not if (blinking) { blink_one_cycle(); } } }
So there you have it, you are now controlling the MCU from an input pin with a user interface. But the MCU is just looping for infinity looking for a button press. That wastes power, and since we probably want to embed this MCU into a battery-operated device, we can put it to sleep while we wait.
Put the MCU to Sleep While it Waits We can nearly turn the MCU off with a state called EM4, the lowest possible energy state in the MCU. Upon exit of the state, it has nearly the same effect on the MCU as if we had pressed reset, so we cannot expect any of our variables to be preserved when entering and exiting EM4 state. That’s OK for this example. But you should treat EM4 as a “soft off” state.
Unfortunately, the pins that are used for the pushbuttons on the Starter Kit are not capable of waking the part up from EM4, so if we put it into EM4, the only way we can ever wake it back up again is by removing power or pushing the reset button. That’s not what we want. Fortunately, we can use a jumper wire to connect the pushbutton circuit to an input pin that has the capability to wake the system up from EM4. The best place to find this information is in the Reference Manual, in the GPIO section, toward the end of the register section, you will find a few configuration registers that control EM4 Wake Up as part of the EMU peripheral.
Configuration registers are used to control how the MCU peripherals operate and should not be confused with the ARM core registers. The registers inside the core are used for executing assembly code. Configuration registers are built in hardware and mapped by the memory controller into the normal memory address space, so that they are easily accessible by your firmware program. The header files for the MCU library created #define’s for all peripheral configuration registers, so you will see that I use those in the code below. You can always hover your mouse over the name of a register, and the IDE will show you the actual memory location, if you are curious.
Inside the GPIO configuration register space, the register called GPIO_EM4WUEN is the enable, which is also used to select the pin (or pins, if we want) to be used to wake the system up. The GPIO_EM4WUPOL register holds the polarity of the wakeup pin, so we can configure it to wake up on a high or low voltage. Note that we will also need to turn off nearly all GPIO’s in use manually since we are going to need to leave the GPIO module powered in order to look for a wake up signal on one of the GPIOs. If we leave GPIO pins in an output mode, they will continue to drive those pins, consuming power. That might be a good thing for your design, if you need to continue to drive a GPIO in a deep-sleep mode, but it could just waste power.
I’ll pick pin PC9 for the EM4 wake up pin, since that is the only one I could find that is brought out on the Starter Kit and it is on the J100 jumper. I will make a physical connection between the switch input pin PB9 and PC9, and then enable PC9 in the GPIO_EM4WUEN register. So now the pushbutton not only enters the MCU on PB9, it also enters the MCU on PC9. We could switch all of our code over to PC9 if we want or use them both. They are electrically connected copies.
Since the system is essentially waking up in a reset state when it comes out of EM4, we need to give the system an initial state that the user will think is just a continuation of the operation. The user never knows that the system went to sleep and entered the EM4 state.
You will need to add the em_emu.c file to your emlib directory from the em_src directory, just like we did last lesson for em_gpio.c and em_cmu.c. Then, add the header file to the top of your source code i.e. #include "em_emu.h".
Then, create a new function called enter_em4() and call it from within the code that stops blinking. Then, in order to set things up so that exiting em4 causes things to blink, I set the blinking variable to 1. Then it will remain blinking until the button is pushed again.
#define EM4_PORT gpioPortC #define EM4_PIN 9 #define EM4_WAKEUP_ENABLE 0x04 // Must change when changing w/u pin void enter_em4(void) { // Set PC9 as an input, used to wake the system GPIO_PinModeSet(EM4_PORT, EM4_PIN, gpioModeInputPull, 1); EMU_EM4Init_TypeDef em4_init = EMU_EM4INIT_DEFAULT; EMU_EM4Init(&em4_init); // Retain GPIO modes while in EM4, to wake it up with button press GPIO->CTRL = 1; GPIO->EM4WUEN = EM4_WAKEUP_ENABLE; GPIO->EM4WUPOL = 0; // Low signal is button pushed state // Wait for the button to be released before we go to sleep // or else we will immediately wake back up again while (!GPIO_PinInGet(EM4_PORT, EM4_PIN)) ; // Add some delay to let the switch settle for(volatile long i=0; i<100000; i++); GPIO->CMD = 1; // EM4WUCLR = 1, to clear all previous events EMU_EnterEM4(); } Once inside of the enter_em4() function, we set up the chip to wake up to the PC9 GPIO pin on low polarity. I added while loop to wait until the button is released, otherwise the MCU would immediately go to sleep, find the PC9 GPIO low, and exit from EM4 state.
Finally, in the main code I switched the initial state of the blinking variable and added an else condition: int main(void) { /* Chip errata */ CHIP_Init(); CMU_ClockEnable(cmuClock_GPIO, true); GPIO_PinModeSet(BUTTON_PORT, BUTTON_PIN, gpioModeInputPull, 1); // Wait for the button to be released before we start blinking // or else we will immediately go back to sleep while (!GPIO_PinInGet(BUTTON_PORT, BUTTON_PIN)) ; // Add some delay to let the switch settle for(volatile long i=0; i<100000; i++); // The initial state of the button is high when not pushed bool past_button_state = 1; // Start out blinking at first bool blinking = true; while (1) { // Grab the state of the button, 1 for high voltage, 0 for low bool live_button_state = GPIO_PinInGet(BUTTON_PORT, BUTTON_PIN); // Invert the blinking mode every time a button is pressed // which generates a low voltage on a pin if (past_button_state == 1 && live_button_state == 0) { past_button_state = 0; // Invert the blinking mode, so that it is buffered and will // keep blinking/not blinking when the button is released blinking = !blinking; } // Reset the past state when the button is released if (live_button_state == 1) { past_button_state = 1; } // Finally decide if there is going to be a blink cycle or not if (blinking) { blink_one_cycle(); } else { enter_em4(); } } } If you run this code, you will see that the LED strip is flashing upon the initial start up. When you press the pushbutton, the IDE will tell you that it lost contact with the Starter Kit and your LED will stop flashing. That is because it has gone into deep sleep and it shut down the debug port. When you press the pushbutton again, the LED strip starts blinking immediately, but it will not automatically connect to the debugger.
TIP: If you want to examine the state of a running system, you can do that by picking Run -> Attach To in the IDE menu. This will connect to a running system without loading the flash nor issuing a reset to the MCU. This only works if the system is powered on and blinking, and not in EM4 state.
Now your MCU is only consuming microwatts of power when the LED strip is off. It’s a first step toward running your projects on a coin cell battery, solar power, or even on an energy harvesting solution.
If you are wondering if the MCU could drop into a deep sleep state while the LED is lit, to be awakened by a timer when it is time to turn off the LED strip while in the blinking mode, you are already thinking ahead to the next chapter. I will cover one more foundational topic to cover on clocks, timers and interrupts on tap for the next chapter before we start working on other peripherals.
|
Nov 08 2017, 9:01 PM |
|||||||||||||||||||||||||||
![]() |
Posted
MCUs vs Computers and Which EFM32 MCU Is Right for Your Project on Blog
Welcome back to Chapter Two of the series that will teach you how to program an embedded MCU from the ground up!
Microcontroller (MCU) Differences vs. Computers All computing devices have at their heart a little “calculator” that crunches numbers. At the most fundamental level that is all it does. A processor “core” as they are called is really nothing more than a machine that gobbles up numbers (interpreted as instructions) and spits out other numbers, which are the results of those instructions. It doesn’t matter if we are discussing the world’s most powerful servers or the tiniest of gadgets. All computers are the same in this regard.
The key differences between the most powerful computer processors and small MCUs are generally:
Computer versus Microcontroller
Therefore, big servers that run the Internet are likely to be clocked up to several GHz, with lots of hardware-accelerated 64-bit wide instructions that run on at least four cores and surrounded by large caches, which are reservoirs of fast local memory and other hardware acceleration circuitry. That takes a lot of energy, but these are servers that are connected with a power cord to an AC outlet, so they can afford to do all of that. MCUs on the other hand often run on battery power and need to consume as little energy as possible, with as low a clocking rate as possible (since it saves power), running only a handful of instructions that can be as small as 8-bits wide on a single core. The aim of an embedded MCU is to do as much as possible with as few resources as possible. This also makes the MCU cheap enough to be placed into nearly disposable gadgets.
A key point however, is that the MCU is no different at the most fundamental level from the standard desktop computer or web server that you may already have had some experience programming in the past. They both have a very similar number cruncher at their heart, but with different set of peripherals surrounding them. Therefore, any skills you might already have in programming regular old computers will help you learn how to program embedded devices. Does that make you feel better?
However, while all mobile/desktop/server computers a standard set of resources available to programmers like hard drives, displays, and network access, the MCU world is much different. There are severe resource constraints in most situations, with lots of specialization between manufacturers and models. You must choose your MCU carefully so as not to waste money or energy while trying to solve your particular embedded task. And you must be very careful about how you use your available resources. I’ll help you with that.
In addition, all mobile/desktop/server computers ship with an Operating System (OS) that helps programmers write, store, and execute source code, as well as optimizing the vast resources of the machine. If things go awry, the OS is there to save the day to inform the programmer that something has gone wrong. An OS is great to have, but it consumes a lot of those precious resources in order to exist, and this is sometimes too much power to bring to your appropriate MCU solution. While there are specialized “real time” OSes available for MCU’s called “RTOS” and a whole dedicate following for them, many embedded solutions don’t require that overhead or justify the expense to run them.
About the EFM32 Family of MCUs The EFM32 family of MCUs that were chosen for this course are 32-bit cores. In general, this means that 32-bits of information can be processed at once. There are MCUs that operate at 8- or 16-bits and as such, less data can be “gobbled up” by the MCU at one time. Processing more bits at once can speed up the execution time of the program.
The type of processing core in the EFM32 family is an ARM Cortex M0+, M3 or M4 (depending on the EFM32 model) licensed from a company called ARM. These ARM cores are very popular in the mobile device market, and licensed by thousands of companies. The ARM core accepts machine instructions that are in the Thumb format. However, from a programmer’s perspective, the type of instruction format is usually not too important because software programming is usually written in a higher level language and the compiler takes care of translating the higher level language into machine instructions. This process will be covered in the next lesson.
MCU Bare Minimum Components
But the MCU is much more than just an ARM core. In order to use the core at a bare minimum, an MCU needs some non-volatile memory (memory that keeps its programming when the power is removed, typically flash memory) to store the software instructions, some Random Access Memory (RAM) to store the results of those instructions and some input/output pins to accept the program instructions and communicate the results of those instructions to the outside world. While the ARM core itself is licensed from a single company, the rest of the blocks on the MCU can be sourced from other vendors or custom-built by the semiconductor company that produces the MCU.
Once we have a bare minimum MCU, we could use it to embed our programs inside of electronic gadgets of all kinds. The programmer would have to work within the limitations of this bare-bones MCU, but it would work perfectly fine for some applications. Within the EFM32 family of MCUs however, the programmer has many more options. In order to make a more powerful and energy-friendly MCU, Silicon Labs has included a number of onboard hardware accelerator blocks (called peripherals) that automatically perform many tasks that would otherwise be cumbersome or time consuming to program strictly in software.
Which EFM32 is Right for Your Project? The good news is that you don’t have to decide this right away. The Wonder Gecko Starter Kit is nearly the fully-loaded model so you can learn about all of the features that the EFM32 family offers. Once you learn how to use the MCU, you can choose a different model and port your software to that model easily.
This will get you started in making the decision. The EFM32 family has six basic models and each model has a variety of options regarding RAM, flash, and pin count. The models are Zero Gecko, Tiny Gecko, Gecko, Leopard Gecko, Giant Gecko and Wonder Gecko. A very detailed (and awesome!) breakdown of the different models can be found here:
http://www.silabs.com/SiteDocs/selector-guide/mcu/efm32-selector-guide.pdf
|
Nov 08 2017, 9:01 PM |