2018-2019 TEI4M Assembly Tasks

 

40. THE 4-WIRE DC FAN. Researching and developing this project has been extremely rewarding and it is a superb context for assembling and reviewing much of the engineering content we have explored over the past two and a half years. Furthermore, this project requires the use of all three timers available on the Arduino!

Keeping electrical components at their optimum temperature is key to their performance. It's not surprising then that Intel, one of the world's leading chip maker, offers manufacturer's precise specifications for DC cooling fans in their document, 4-Wire PWM-Controlled Fans.

The specific model chosen for our investigation is Sunon's 4-wire 12VDC ball bearing fan. This device offers a number of features that can be explored under elementary and advanced microcontroller operation. Additionally, a sentence from the Intel specification caught my eye. In its overview it claims, "The expectation is a 4 wire PWM controlled fan, when properly implemented, will be significantly quieter than a similar 3 wire fan". I'd like us to confirm this.

PART A. GETTING ACQUAINTED WITH THIS FAN

  1. Look over its datasheet and familiarize yourself with as many of its characteristics as you can.
  2. Review Intel's Specifiation for 4-Wire PWM-Controlled Fans.
  3. Here are some questions you should be able to answer if you've read the two pdfs thoroughly,
    1. What is the typical operating voltage and current draw of this fan?
    2. Identify the name, colour and function of each of the four wires.
    3. What is the operating voltage range of the PWM signal?
    4. What is the recommended frequency range of the fan's PWM signal?
    5. What operating conditions produce a detectable audio hum emanating from the fan and how can it be eliminated?
    6. What is the frequency of the Arduino's PWM signal from it's analogWrite() function? Explain why.
    7. Can the Arduino handle the Fan's PWM requirements 'easily'? If not, what course of action must be considered?
    8. What is meant be the term open collector (aka open drain)? To which wire colour does this pertain and what additional component is required to facilitate the correct performance of this function?
    9. What is the FREQUENCY:ROTATION ratio of the output sense wire?
  4. Identify the direction of airflow and fan rotation from the arrows on the housing before taping the index card and paperclip to monitor the action of the fan.

PART B. IMPLEMENTATION. Apart from your ISPs, this DC Fan Project is the marquee entry in your DER. In future employment interviews, this is the project you should draw your recruiter's attention to as it speaks to an extremely high level of competency and facility.

You've earned the right to implement reports through your own eyes so I'm giving you the freedom to organize your summary as you see fit. On the other hand, I still remain in judgment of the depth of your Reporting so take your time and make sure you include EVERYTHING. This is big.

For this followup DER entry from Part A, you need to take your readers through a comprehensive discussion of the hardware and software support required to exploit the characteristics described in Part A.

Your report will provide text, graphic, and video content detailing the power, speed (ADC/PWM), and sense (TACH/Input Capture) features of the 4-wire cooling fan.

On the software side, incorporate a discussion of the Assembly language control and manipulation of the AVR peripherals (Timers and ADC).

I'll leave the rest to you. Additional material remains below from the 2017-2018 version of the project. Feel free to incorporate some/most of it.


The web has a great deal of instructional content for interfacing 2- and 3-wire fans. For 4-wire fans there is far less and some if it is inefficiently designed or simply wrong.

  1. With all the knowledge gained from the previous stage, develop an (initial) Fritzing diagram that includes a 12V source to power the fan and a 5V source for the Arduino logic. Someone has created a Fritzing image of a 2-wire fan to make the diagram (somewhat) more realistic. Here's an explanation of how to add new parts to Fritzing.

PART C. PWM DESIGN. PWM signal design (modulation) requires consideration of two characteristics: frequency and duty cycle. Our (fun) challenge is to configure an AVR timer to provide a PWM signal to match the optimal requirements outlined in fan's datasheet. Whereas the frequency of the signal remains constant, the duty cycle (speed) should be variable (in this stage, we'll hard code the changes to the duty cycle. In the next stage we'll consider external control (manual or sensor) over the duty cycle). Finally, as you develop your solution, remember, the best (most efficient) strategy places as much, if not all, the burden on the hardware that can perform in the background.

T. Morland (RSGC '18) developed a working solution after class Wednesday and offers us this scope trace of his assembly implementation ...

PART C Task.

  1. MaxEmbedded offers one of the web's most straightforward overviews of AVR Timers, in my opinion, if you need it. (Hint: You'll find this discussion applicable to this project in more ways that one :)
  2. The author of the AVR Timers article says, "With a clock frequency of 32 kHz and 8-bit counter, the maximum delay possible is of 8 ms." Explain this, as simply as possible, using powers of 2.
  3. Review Ken Shirriff's Secrets of Arduino PWM blog for some extremely informative background.
  4. From what you know of this fan's preferred PWM frequency and our requirement for a variable duty cycle, suggest the optimal WGM for controlling the device.
  5. After deciding on the respective Timer to use (Timer0, Timer1, or Timer2) work out the complete set of register values to create the signal you chose in Step 2.

PART D. Speed Control (ADC). We've reached the stage in this project where the speed of the fan (duty cycle) is turned over to the operator. For this, we need to explore low-level register manipulation of the AVR's ADC unit.

PART D Task.

  1. Chapter 24 of the ATmega328 datasheet discusses the Analog-to-Digital Converter. You should keep this handy for reference. MaxEmbedded, again, provides a terrific primer on the ADC concept and register set for this stage. An excerpt appears below. We'll go through it together in class.

  2. I have assembled a summary of the applicable ADC Registers (below) from Chapter 24 of the ATmega328P datasheet. After reviewing the recommended ADC example, implement the code necessary to have your pot, acting as a voltage divider, control the speed of your fan by reading the sweep leg (C) on Analog pin 0. Upload your code and test. The fan's speed should be responsive to your turning of the pot.


PART E. SENSE (FEEDBACK, TACHOMETRY, YELLOW) WIRE (Demodulation of PART C's PWM).

OPEN loop systems (no feedback) are undesirable as the user has no idea if the process is functioning as designed.

CLOSED loop devices such as our 4-wire fan
offer a means of monitoring and adjusting our input PWM by sensing the speed of rotation and placing this signal on the fourth (yellow) wire. This variable PWM signal can be captured and demodulated by our microcontroller, analyzed and the data used to adjust the input PWM as necessary to ensure the fan is maintaining a desired performance. Note. All sorts of information can be carried by a PWM signal. An image from the datasheet you are about to read suggests how the duty cycle of a PWM signal could be used to carry ASCII data. Inspiring stuff.

The 328P's Timer1 facilitates the capture of the yellow wire's PWM signal through it's ICP1 function (mapped to Arduino's digital pin 8). Demodulation of this signal will expose the fan's actual RPM. Furthermore, through Atmel Studio's Data Visualizer feature we can explore AVR's Serial Communication feature and plot both the theoretical and actual RPM. This sets us up well for the final stage of this project, PART F: PID.

 

PART E. Task (TACH Wire Demodulation)

  1. Software Design Considerations. The goal of this stage is to begin the process of confirming the claim on page 11 of the fan's datasheet with respect to the input duty cycle vs the RPM of the fan. A screen capture of the graph appears to the right for your convenience. Just like Ken Shirrif, Nick Gammon has posted some highly respected insights into AVR programming. Check out his Timers and Counters Blog searching for, Timing an interval using the input capture unit when you get there. This will give you some useful insights.
  2. What Arduino pin maps to the Input Capture facility of the ATmega328P?
  3. Add the function T1Init to the project and call it immediately following rcall T0Init. Timer 1 is responsible for two interrupt sources: Input Capture and Timer Overflow. With the provided links and the Register references above, implement Timer1's Input Capture feature based on the falling edge of the signal provided by the fan's Tach wire. Within the TIM1_CAPT ISR, simply increment a register with every interrupt.
  4. We intend to publish the monitored RPM every second. So, configure Timer1 to overflow every second.

 

 


PART F. Serial Communication (USART: Universal Synchronous/Asynchronous serial Receive and Transmit) After all the groundwork previously laid we're now in the enviable position of acquiring data from our system and placing it into serial data stream for external retrieval.

In this stage we're going to confirm and display as text, the value of OC2B and number of falling edges the tach wire is yielding per second.

Just as the Serial Monitor and Serial Plotter are available tools within the Arduino IDE, Atmel Studio offer a far more comprehensive utility, the Data Visualizer, that does a great deal more. To fully exploit its capabilities we must learn to navigate the AVR's USART subsystem.

Reference 1: Serial Communication is discussed in Chapter 20 of the ATmega328P datasheet
Reference 2: Microchip's Data Visualizer Home Page
Reference 3: Microchip's Data Visualizer Users' Guide

PART F. Task.

  1. Add the function USARTInit to the project and call it immediately following rcall T1Init. In this function we need to set the Baud Rate Register (UBBR0H:L), enable the transmit capability (TXEN bit) and declare the Character Size (Tucson bits) to be 8.
  2. Return to your ISR and let's set the the duty cycle, correctly. You see, the value obtained from the ADCH must be mapped to a value between 0 and OC2A (80) to be accurate. The good news is that this turns out to be easier than you might think!
  3. Within your TIM1_OVF ISR place the OC2B values on the serial communication channel. These values should range over the interval [0,80]. Offset the values read by 32 to avoid the range of ASCII control codes.
  4. Next, place the count of the number of pulses per second captured in the input capture pin. These values should range in lock step with your OC2B values and range over the interval [30,170] corresponding to the RPM interval, [900,5100].
  5. Launch the Data Visualizer, open the Terminal Window and confirm the data stream. You might with to add a Return to each line of the stream. How do you think you do this? It might easier to interpret the values in hexadecimal rather than their ASCII interpretations.



PART G. Graphic Data Visualization. In this stage we wish to generate a graphic plot comparing the theoretical RPM to the empirical RPM obtained through feedback, setting the stage for the final leg of this [epic 8-part] marathon: PID in which we expect to the expected and actual plots to merge. A plot of the Duty Cycle, Theoretical RPM and Empirical RPM are displayed over a FULL range of input duty cycles provided by a potentiometer was undertaken weeks ago in the high-level Arduino IDE and visualized by it's Serial Plotter utility over 1 s intervals. in this stage we'll see to what extent we can produce a similar visualization through AVR assembly exploiting Atmel Studio's Data Visualizer.

Arduino Serial Plotter Atmel Studio 6 Data Visualizer

PART G. Task.

  1. The first modification to our stated goal will be to scale RPM, back to RPS (theoretical and empirical). Duplicate your RPM array to a new RPS array and reduce each of the elements by a factor of 30. (Note. While it might appear that we should reduce RPM by a factor of 60 to achieve RPS, since the tach wire reports two falling edges per rotation, we save ourselves an extra division by 2 (bit-shift right one) if we use 30 as the reduction factor)
  2. As of the day of creating this exercise, I have not discovered a simple strategy to have two plots appear on the same graph. Until I do, I recommend placing only the difference between the theoretical and empirical results leading to an ideal range of 0.
  3. Within your Timer1 Overflow ISR (TIM1_OVF) immediately move the (empirical) pulseCount into a register and reset pulseCount to 0 (so as not to miss any half-rotations). Next, map the duty cycle to the index of theoretical RPS array (bit-shifting again should do the trick!) and obtain the element within the vector. Now, we have two issues: the difference could be negative, and the terminal window would treat a value less than 32 as a control code. I suggest we add an offset, say 65 ('A') which become the ideal target.
  4. At the end of the TIM1_OVF ISR, load the desired value into txByte and call the transmit function.
  5. Launch the Data Visualizer. Click on Configuration in the top left corner. Pressing the Help button, , will access context-sensitive support. Under External Connections, double-click Serial Port to launch the Serial Port Control Panel. Click Connect to see the Terminal window. Confirm the data stream.
  6. Return to the Configuration tree in the top left corner and select Graph under the Visualization header. To plot your serial stream, drag the RCA plug from the top of the Serial Port Control Panel to the plot area. Confirm.

PART H. PID. You are well-aware that simply supplying a device with a required signal is not sufficient to GUARANTEE required performance. To achieve this you must create a closed loop that continually measures the process output (actual device performance) and dynamically adjusts the setpoint signal by reducing or eliminating the variance (error-e).

PID Principle 1: Introduction: PID Controller Design
PID Principle 2: PID Controller on AVR Devices
PID Principle 3: Detailed PID Library Discussion
PID Principle 4: The PID Controller (Nuts&Bolts Magazine)

Application 1: Arduino: DC Motor Control Position Control using PID

Part H. Task.


AVR TIMERS

38. Timer1. Fast PWM Mode 15. In this exercise you are asked to develop standalone assembly code that will result in the the horn of your HXT500 standard servo motor sweeping through its 180ˆ of rotation, continuously.

Task.

  1. Watch this video: RSGC ACES: HXT500

37. USART Debugging Utility. In this exercise you are asked to develop a utility that can be used to debug your assembly code by dumping the contents of SRAM the Serial Monitor.

Task.

  1. Open the file to obtain the driver code for the SerialCommunicationTestAssembly Project we started to develop last class. Do not modify this code.
  2. Create a project by the same name and drop the code into a .ino file by the same name.
  3. Add the partially-completed file SerialComm.S linked from our course page to this project and begin to develop the list of functions from the UML diagram on the first page of the handout.
  4. Add the file ASCII.h to a folder by the name (ASCII) in the libraries folder of your Arduino projects folder. Feel free to add more entries...
  5. Using a diminishing set of comment brackets (/*..*/) implement the required functions within SerialComm.h to support the project driver.
  6. Your results should be similar to this output: SerialCommOutput.txt.

36. Timer1. Clear Timer on Compare Match. Mode 4 (Output CTC) and Mode 12 (Input CTC) result in clearing the TCNT1H:L register pair when it matches the preset OCRnA/B H:L register pair (Mode 4) or the ICRnA/B register pair (Mode 12). The latter can be exploited for recording timestamps for input events. Interrupts can be configured for both OC1A/B. The attached pins (9 and 10) can be manipulated (cleared, set or toggled) using the COM1A/B bits in TCCR1A to shape the resulting PWM Signal. This is somewhat easier to use than setting the start count less than the TCNTn resolution an climbing all the way to the overflow.

 

 

 

Task.

  1. Listen to the audio file below recorded on Pin 9 under both the Timer1Clear Timer on Compare Overflow Hardware and Software Interrupts. Over this stretch, again, the volume remains constant but the pitchdecreases. The mono waveform, recorded in stereo, is shown to the right as it appears in Adobe's SoundBooth.

  2. Review this MaxEmbedded blog for a pretty thorough explanation of the CTC Mode.
  3. Create the Arduino project, Timer1CTC. Add the define file, prescales.h to the project if you wish.
  4. Develop the standalone assembly code for Timer1CTC.S that recreates this audio file.

35. Timer1. Normal Mode with Output Compare (Software and Hardware Interrupts). In addition to a strictly hardware output response to the occurrence of a Compare event, on A and/or B, one can add a software response as well.

Task.

  1. In addition to the configuration steps undertaken in the previous Exercise, to enable a Timer 1 Compare event software interrupt in parallel with the hardware interrupt, you need to set the OCIE1A/B flag of the TIMSK1 Register.
  2. The syntax for the Compare Event ISR on A is a follows,
    	.global TIMER1_COMPA_vect
    	TIMER1_COMPA_vect:
     		...
      	reti
        
  3. Modify either of the previous exercises that will result in an LED on pin 7 flashing at a quarter of the frequency of Pin 9.

34. Timer1. Normal Mode with Output Compare (Hardware Intr). A closer look at the pin mapping of the Arduino reveals 6 digital pins with alternate functionality labelled OCnX (pins 3, 5, 6, 9, 10, & 11).

These pins can be configured to change their state (cleared, set or toggled) when the timer counter register pair (TCNT1H:TCNT1L) matches the value preloaded into the Output Compare Register pair (OCR1A/BH:OCR1A/BL). Since this functionality is achieved primarily at the hardware level, it has the potential to offer high, asynchronous, efficiency. On the other hand, the constant comparison of the TCNT1H:L register with the OCR1AH:L and/or OCR1BH:L register pairs places such a burden on CPU performance, even the 328p datasheet does not recommend its use (p. 146).

One final note. As attractive as it is to forgo the need for an explicit software interrupt, the issue of clearing the OCF1A and/or OCF1B flags in the TIFR1 regsiter remains a problem. Since polling the flag and clearing it by writing a one to the bit is one solution, but the preferred soltion for us is to simply include dummy ISRs for the OC1Aaddr and/or OC1Baddr interrupts addresses in the interrupt jump table so the flags are cleared automatically.

Task.

  1. Create the AS7 project T1NormalOutputCompareLED to affect a HARDWARE-level interrupt of OC1A (pin 9) resultng in an LED flashing at a frequency of 1 Hz just as the gif suggests.
  2. Modify the sketch T1NormalOutputCompareLED to maintain a HARDWARE-level interrupt that flashes a biColor LED using OC1A/B (pins 9 and 10) (red-off-green-off-red-off-green-off-...) with a frequency of your choice (modify OCR1A/BH:L)

33b. Light It Up! (This should be fun:) Each of you has been given a Piezo Buzzer with which we'll attempt to crank up the audio in the DES confirming each of the 7 frequency bands of the MSGEQ7 used in Ethan's Equalizer at the front of the room.

Day 1. The Arduino tone() function

  1. Review the Arduino tone() function, in particular, it's use of the 8-bit Timer2 peripheral. Questions: Which pins can be used for the tone() function? Which pins on the Arduino are mapped to Timer2?
  2. Create an Arduino IDE project Timer2ToneFunction and drop in the driver code, Timer2ToneFunction.ino.
  3. Review the code and it's resources. As you can see the body of the loop() function is empty. Run the code and open the Serial Monitor noting the default contents of the important Timer2 registers.
  4. Add statements that will invoke the tone() function on piezoPin to generate a tone of 63Hz (MSGEQ7's lowest band) and hold the note for 2500 ms, before caling the registers() function a second time to see the effect of the call and then entering an infinite, do-nothing loop. Question: Did the left band of the DES Equalizer at the front of the room respond accordingly to the 63Hz signal? Yes? Great! No? Hmmmm...
  5. Compare the contents of Timer2's registers with values suggested by the online AVR Timer Calculator. By doing so, we can begin to trust the Calculator to produce the results needed for Day 2 of this exercise.
  6. Finally, comment out both calls to the registers() function and insert a call to the plot() function after the call to tone(). Run the code and open the Serial Plotter.

Day 2. AVR Assembly Equivalent: Time2ToneFunction.asm

  1. Open AS7 and create an AVR Assembler project by the same name as Day 1's high level project, Timer2ToneFunction. I recommend adding the prescalers.h header file to the root folderof the project to access prefined constants for clock prescalers for all three ATmega328 Timers.
  2. Develop the assembly language equivalent of the high-level code to prpduce the identical 63Hz signal on pin 11.
  3. Use the AVR Timer Calculator, to determine optimum regsiters values to produce each of the remaining 6 frequencies of MSGEQ7 (160Hz, 400Hz, 1kHz, 2.5kHz, 6.25kHz, 16kHz)

33a. Sound: Timer2 Normal Mode with PreLoad. In the previous exercise you simply let the Timer's counter run freely, restarting at 0x00 on the overflow. In this exercise you are asked to undertake an audio exploration of Timer2's Overflow Interrupt (in Normal Mode) while manipulating the TCNT2 register.

In this exercise you are asked to toggle Pin 13 with Timer2 Interrupt Overflows so as to recreate the provided audio file.

Task.

  1. Listen to the audio file below I recorded from Timer2 Overflow Interrupts. Over the four captured overflow periods, you'll appreciate that the volume remains relatively stable while the frequency (pitch) increases. The mono waveform, recorded in stereo, is shown below right as it appear in Adobe's SoundBooth.

  2. Review this guy's Arduino 'blag' to reacquaint yourself with mid-level Timer2 Interrupt coding. I'm calling C code 'mid-level' if it employs direct and maximum reference to the ATmega328P's registers.
  3. Create the Arduino project, Timer2OverflowWthPreload. Download and add the (minor) updated define file, prescales.h to the project.
  4. Develop the mid-level sketch Timer2OverflowWithPreload.ino.
  5. With the mid-level version working, use this as a base from which to write the standalone assembly version, Timer2OverflowWthPreload.S.

32. Timers as Audio Frequency Generators. AnalogWrite PWM and the tone() Function.

AnalogWrite PWM

  1. We have put the 6 Arduino PWM pins to good use in the past to affect the duty cycle of our signals but not the frequency. The frequency remained beyond the scope of the analogWrite(pin,dutyCycle) function.
  2. From the previous link we find that although the 6 pins offer two different default frequencies (~490 & 980 Hz), both fall within the range of frequencies of the human ear (20Hz to 20KHz). So, let's listen and analyze to the range of the default frequencies of the 6 PWM pins to confirm...
  3. Assemble the prototype depicted to the right. The volume output from pin 11 (brown wire) is reduced by the current limiting 100kΩ resistor to ground (reduce it to 47kΩ if you'd like it a little louder). Tie the left (TIP) and right (RNG) leads of the 3.5mm audio jack breakout board together with a jumper wire (mono).
  4. (Arduino High Level) Create the project, AnalogWriteAudio, that includes the following high-level fragment,
      uint8_t pwmPin = 11;
      void setup(){
          pinMode(pwmPin,OUTPUT);
      }
      void loop() {
          for (int i=0; i<255; i++) {
            analogWrite(pwmPin,i);
            delay(30);
         }
      }
    You will recall that the parameter to the analogWrite() function affects the duty cycle (mark/space ratio). Frequency and volume are the two primary audio characteristics you'll experience. Which of the two are affectd by the changing parameter? Does this make sense?
  5. Move the brown wire to pin 3, modify the code and run the sketch again. Confirm that it sounds the same.
  6. As can be seen from the Arduino's pin diagram, pins 3 and 11 are built on Timer2. It stands to reason that sf we change the prescalar for Timer2 by modifying the values in the TCCR2A register (p.156) we should be able to affect the frequency and hear the audio results. There are 7 useful prescalar values (1,8,32,64,128,256,1024). By adding and modifying the statement TCCR2B = 1<<CS22 | 1<<CS21 | 1<<CS20; confirm by experimentation which prescalar the core analogWrite function uses.
  7. Download the Excel workbook Timers.xlsx and confirm the calculations that yield the default frequency of approximately 490Hz.

tone(pin, frequency, duration) Function

  1. Many good high-level theory and practical references discussing the Arduino's tone() function exist and here is one of them. We'll see. In this exercise we're going to undertake a low level exploration of tone generation and listen to the waveforms capable of being generated in Normal Mode from the various timers on the ATmega328P.
  2. (AVR Assembly) Add the file prescales.h to the project. This file contains all the convenient defines for the prescale values for each of the three Timers on the ATmega328P. Look over the contents of this file.
  3. Download the Excel file, SoundNormalMode.xlsx. You task is to complete each entry in the table and confirm each by listening to the output of the assembly sketch below.
  4. Comment out the high-level code above. Develop the pure assembly code SoundNormalMode.S that is equivalent (not identical) to the high-level code in Step 2 that uses a Timer1 Overflow Interrupt, with no prescaling, TCNT1H:L starting from 0, to toggle pin 13. (Note that pin 13 is not a designated PWM pin!) Plug your headphones into the audio breakout board and consider what you're hearing. Identify the frequency of this signal and make your enties in the worksheet.
  5. Run through the code several more times, changing only the prescale constant and confirm to yourself what your hearing and the frequency. Review this frequency table of notes I believe we examined last year and is included in a tble within your Excel worksheet.
  6. While remaining in Timer 1's Normal Mode, modify your code to get through (and listen to) all the available prescalers. Update your worksheet.
  7. Timer0 and Timer2 are both 8-bit timer/counters. Switch to a Timer2 implematation and confirm you can generate their notes/frequencies.
  8. Once the Excel worksheet is complete, inpsired students are invited to sample their waveforms on the oscilloscope.

31. Timer1. Normal Mode. The absolute simplest of 16 different Timer1 modes results in the system clock incrementing its 16-bit register pair TCNT1H:TCNT1L with every cystal oscillation. At 16 MHz (~`2^24`), the count will fill the register pair (`2^16`), ~244.14 times per second and trigger an interrupt if properly configured in code. When the count reaches the top an interrupt overflow (TIMER1_OVF_vect) is generated and the count continues from 0x0000 like an odometer whether you service the request or not. Setting the TOIE1 bit in the TIMSK1 register, together with the enabling of the global interrupt flag in the SREG allows your code to respond to the request.

Review the document, T1NormalOVFISR.docx. In it you'll find an Arduino Register-level implementation of a Timer1 Normal Mode Overflow Interrupt Routine to ease you into the Assembly-level register implemenations required in the Task(s) below. This example employs an ISR to toggle an LED on PB5 at rate of ~244 ovf/s, leading to frequency of 122 Hz.

Task 1.

  1. Open the Arduino IDE, create the project T1NormalOVFISR and drop in the sketch, T1NormalOVFISR.ino. Also, add the header file of predefines, prescalers.h into the same folder.
  2. Review and upload the code to confirm the LED 's behaviour.
  3. The code can be modfied in two convenient ways to reduce the LED;s frequency below POV. One method involves software amd the other is a hardware solution. Test them both.
  4. Open Atmel Studio 7 and use you ASM template to create the project T1NormalOVFISR.asm.
  5. Together in class, we'll code the translation of the Arduino Register-level code provided. Note the addresses of the TIMER1 Registers in choosing which SRAM-related instructions to employ.
  6. Upload the code noting the ~122Hz flash rate of the LED onbard LED or an additional one placed in pin 13. From this concept and code, we have a starting point from which to decrease or increase the frequency of the interrupts.
  7. To decrease the LED freqency, we could use the ISR to maintain a count of the number of interrupts in a register. Only when the count reaches a specifc number would we toggle the state of the LED pin. An interrupt count of 122 would yield something close to 1 Hz.
  8. On the other hand, to increase the frequency of the overflow interrupts, we could set the count to half the maximum count (`2^15`), within the ISR, thereby reducing the time between overflows. For example, if we set the count to `2^15` (half of `2^16`) the frequency of the overflows would double. You may wish employ this useful calculator to explore other frequencies with greater precision.

Task 2. (Prescale)

  1. TBA.

31. SPI: Serial Programming Interface. One of the most established communication protocols within the embedded systems device field is the three-wire, full duplex SPI (AVR151).

Note 1. Since the standard is loosely interpreted, you are encouraged to check a device's datsheet for any variations the manufacturer has adopted.

A fairly reliable starting point is to assume there is one master device that exchanges data bits, serially, with multiple slave devices under strict sequencing of clock pulses provided by the master.

Note 2. An additional wire is required for each additional slave device. A specific slave is selected by the master to communicate with by pulling its respective Slave Select (SS) line low prior to the exchange and reutned to high upon completion.

The common ISP (AVR910) activity used to flash code to your Arduino is one such example of the SPI schema.

Full details of the SPI peripheral can be found in Chapter 19 of the ATmega328p's datasheet and supported by resources linked from our course page as well as countless online tutorials. A summary of the three dedicated SPI Registers appears below. It can be seen that, if properly configured, Interrupt Jump Address 18 (SPI_STC_vect) is triggered upon the Completion of a Serial Transmission from the master. The task below demonstrates an example of this behaviour.

Task A.

  1. To get a strong sense for how an interrupt-based application of the SPI functions migt be set up, assemble a hardware prototype as shown to the right (click to enlarge) with the Morland Bargraph V3 appliance inserted into your UNO as shown.
  2. Create an Arduino project SPIInterrupt from code available at https://github.com/rsgcaces/AVROptimization/blob/master/SPIInterrupt.ino
  3. Review the register use within the code while testing the project.
  4. Open Atmel Studio and create an Assembler project by the same name and try your hand at duplicating the performance of the higher-level sketch. (solution discussed in class on 2019 02 04 in a dcoment entitled AVRASMSTCInterrupt.docx).

Task B.

  1. You were introduced to the Arduino SPI Library last year in the manipulation of the MCP4231 (10kΩ-Dual) Digital Potentiometer.
  2. You'll find detailed information about its SPI Implementaton and Device Command structure in Chapters 6 and 7 respectively, starting on page 41.
  3. This information, together with our in-class review of the MCP4231 IC and high-level SPI sketches, should provde you with the ability to develop an AVR ASM interrupt-based solution similar to MCP4231.ino.
  4. From your Assembly template, create the Atmel Studio project, MCP4231 and implement a solution that will drive the DMM's Voltmeter in a manner depicted to the right.

 


THE STACK (Stack Pointer: SP) ( __SP_H__ : __SP_L__ )

30. Sum of a Series 2. Recursion is a programming strategy in which a function calls itself to solve a simpler version of the problem until the process reaches a trivial base case. Review the sketch below.

In this exercise you develop an assembly language equivalent as a means to understanding the uses of the STACK to pass values and address between callers and receivers.

Note. On second thought, I don't recommend doing this exercise. Few people in their right mind would attempt this. It took me two days to figure this out. Here's the Excel spreadsheet I used to work out the logic if you're interested.


29. Subroutine: Add. The sketch below presents a somewhat formal, high-level solution to adding two bytes with the support of a function (Assembly: Subroutine). What we're interested in, in this exercise, are the details of the passing of the input parameters to the function and the receipt of the return parameter. With this knowledge, you can undertake an assembly language version of this code.

The graphic to the right is intended to show the relationship between the Caller code, the Subroutine and the role played by the Stack in ensuring the communication between the two takes place accurately. I'll be better able to explain how it all works when we're together.

Task. Develop a sketch entitled AddSubroutine.ino that mimics the high-level code above. Incorporate all the best assembly language practices we have highlighted so far.

 

 

 

 

 


28. Swapping 3. (push, pop) Declare and initialize two global variables to different values. Implement assembly language instructions to accomplish the same result as Swapping 1 but makes use of the Stack instead.


I/O PORT ACCESS

27. Motor Control. TBA.


26. Matrix Position. For a final look at input polling, develop MatrixAnimation.ino. The assembly code monitors FOUR push buttons for position data before uploading column and row data to two shift registers connected to your 788BS.

The result is that a single LED dot moves about on your matrix as demonstrated in the adjacent animation.

 

 

 

 

 

 


25b. [17/18] Bargraph Position. Create the AS7 Assembler project, BargraphPosition. Grab a bargraph and wire six consecutive LEDs to PortB (PB0-PB5) of your Arduino. Wire two push buttons to external interrupts 0 and 1 using a hardware debounce strategy that uses (internal or external) pullup resistors.

implements ISRs for both of the external interrupts that are called on the falling edge of the respective button activity. INT0 should result in the active LED position moving to the left and INT1 resulting in the active LED position moving one position to the right. In both cases implement a wraparound. Your assembly code can start with any LED of the six turned on.


25a. Bargraph Position. Using your Arduino, develop the assembly project BargraphPosition.ino that polls TWO push buttons to control left and right movement of a bar on your bargraph. A single shift register is to be employed to provide the byte data to the bargraph.

 


24. Multiple Inputs. In this exercise you are asked to attach three buttons to PORTD pins, one for the red, green, and blue results on an RGB LED. The three buttons are also wired through OR logic (a three-input IC would be great or two cascaded two-input gates) to a single PORTD pin that is polled continuously. Once a state change has been detected, poll the three buttons and light the respective LED for 2s before repeating the exercise.

 

 

 

 

 


23. Input Polling (in,out,sbi,cbi,sbrc,sbrs,sbic,sbis). We'll get to interrupts later in the course, but for now you are asked to attach a hardware-debounced button to a port pin of your ATtiny85 and monitor it for a state change. Once recognized, turn an LED on for 3s before repeating the exercise. AVR Delay Loop Calculator. Note: There are three I/O registers associated with each Port. i.e. DDRB, PORTB, and PINB

 

 

 

 


22. Charlieplexing. The use of shift registers is a hardware solution employed to extend the reach of logic beyond the number of I/O pins on your microcontroller. If the goal is to simply drive more LEDs than the number of microcontroller pins available, a purely software solution is Charlieplexing. The key to successful implementation of this technique is the three-state logic capability of the microcontroller's digital I/O pins meaning the pins can be in one of three states: Output (H)IGH, Output (L)OW and Input (Z) . Placing an I/O pin in the Input (Z) or high impedance state effectively disconnects the pin from the LED network. As can be imagined, given n digital I/O pins, a network of n2-n LEDs can be driven. This is simply the permutation defined by,

`P(n,2)=(n!)/((n-2)!)=n(n-1)=n^2-n`

Task.

  1.   PIN A PIN B PIN C
    L1 L H Z
    L2 H L Z
    L3 Z L H
    L4 Z H L
    L5 L Z H
    L6 H Z L
    Review PCBHeaven's gentle introduction to Charlieplexing.
  2. Review the other research links below for further understanding and inspiration.
  3. Create a project entitled Charlieplex.ino in which 3 pins of your ATtiny85 will be used to drive 6 LEDs in sequence.
  4. Since you know how to condition DDRB and PORTB for I/O use, think about a compact data design that can be used to model the purpose at hand.
  5. Using the three pins of your ATtiny85, develop and test code for this Charlieplexing context.
  6. Note: You should use 6 identical LEDs (or a bargraph) to provide consistent brightness.

Research Links.

  1. Wikipedia: Three-State Logic, Charlieplexing
  2. Charlieplexing LEDs with an Arduino
  3. Kwan's: Precision 3 Charlieplexed Clock
  4. Designing a large Charlieplex Matrix
  5. Charlieplexing Conway's Game of Life
  6. YouTube: Charlieplexing+ATtiny85

ARITHMETIC

21. Multiplication (mul, muls, mulsu).The AVR Instruction Set breaks down the multiplication of 8-bit operands into three cases: mul (unsigned×unsigned), muls (signed×signed), and mulsu (signed×unsigned). Regardless of which registers contain the operands, the 16-bit product is always placed in the r1:r0 register pair. You are asked to develop an example of each.

Note 1. The avr-gcc compiler supports numerous integer types. Use the correct one in each context below.
Note 2. It appears that the Serial library uses r0 and r1 for its own purposes and prevents the assembly code from obtaining correct results. Delay the use of the library until after the results have been obtained.

  1. Unsigned Operands (mul). Create a sketch entitled MultiplyUnsigned.ino and confirm the use of the mul instruction.

  2. Signed Operands (muls). Create a sketch entitled MultiplySigned.ino and confirm the use of the muls instruction. Be sure to use the correct integer types that can support negative values.
  3. Signed and Unsigned Operands (mulsu). Create a sketch entitled MultiplySignedUnsigned.ino and confirm the use of the mulsu instruction. Note. The instruction requires the correct type for each operand so code accordingly.

20. Sum of a Series 1. The sum of the first n natural numbers can be expressed as,

`sum_(i=1)^n i=(n(n+1))/2`

Create a sketch entitled SumSeries.ino that begins in a manner similar to the capture below and uses assembly instructions to accumulate the sum. The value of n can be limited to a byte (uint8_t) but the sum must accommodate a uint16_t total. The output appears in the inset.


19. Addition of uint16_ts. (add, adc, eor). Create a sketch entitled AddInts. Declare and initialize two global variables, opA and opB, of type uint16_t and a global variable sum of type uint32_t. Implement assembly language instructions to add the two operands and store the result in sum. Add Serial functions to confirm the result.


18. Addition of uint8_ts. (add, adc). Create a sketch entitled AddBytes. Declare and initialize two global variables, opA and opB, of type uint8_t and a global variable sum of type uint16_t. Implement assembly language instructions to add the two operands and store the result in sum. Add Serial functions to confirm the result as in,


INDIRECT ADDRESSING MODES

17. ToLowercase 2: char[]. This exercise extends the previous one by asking you to convert all the letters in a char array to their lowercase equivalent. Develop the sketch, ToLowerCaseCharArray.ino that begins as shown below and yields the output in the inset.


16. ArrayCopy : char[]. (ld, st, lo8(), hi8()) In this exercise you will employ Indirect Addressing with Post-Increment to duplicate the characters in an input array.

Task.

  1. Review The Data Addressing Modes sheets you were provided with (AVR Instruction Set Manual, pp. 3-6)
  2. Review the three forms of the ld and st assembly instructions.
  3. The lo8(variable) and hi8(variable) assembler macros split an address into the low and high bytes respectively. This helps populate the X, Y, and Z register pairs that can be used in Indirect Addressing modes.
  4. Develop the sketch, ArrayCopyChar.ino that begins as shown below and yields the output in the inset.


LOGICAL

15. ToLowerCase 1: char. You are likely aware that the ASCII Table defines the numeric value of the lowercase letters to be 32 more than their uppercase equivalents. Develop the sketch ToLowercaseChar.ino that employs assembly language instructions to obtain the value of a global variable of type char that defines an uppercase letter and through an appropriate mask, uses a logic instruction to convert it to its lowercase equivalent. Display the result to confirm.


14. OR Mask (or,ori). To ensure specific bits of a register are set while preserving the state of other bits, the or or ori instruction can be employed with the use of a mask. Consider the byte mask 0x0F. Applying this mask to a register with the ori instruction, will result in bits 0 through 3 being set while bits 4 through 7 remain unaffected. Develop the sketch ORIMask.ino that employs assembly language instructions to set the even-numbered bits, while leaving the odd-numbered bits unaffected. You will accomplish this though the design of an appropriate mask applied with the ori instruction. Display the results to confirm.


13. AND Mask (and,andi). Individual bits of a register can be set or cleared through the use of a 'mask' applied with the and instruction. Consider the byte mask 0x01. Applying this mask to a register with the and instruction, will clear bits 1 through 7 and preserve the value of bit 0 in the register. Develop the sketch ANDMask.ino that employs assembly language instructions to clear the upper nibble of a byte, preserving the original contents of the lower nibble. You will accomplish this through the design of an appropriate mask applied with the andi instruction. Display the results to confirm.


FOUNDATIONS

12. Pattern Matching (and, cp, lsr, mov). Searching for a particular sequence of bits within a longer bit sequence is common task. In this exercise you are asked to count the number of occurrences of a bit pattern within a data byte. In the graphic below the number of occurrences of the bit pattern 11 within the byte 11011011 is determined to be 2. One might think it is 3 but, for this exercise, the search starts at bit 0 and advances through the target byte by an amount equal to the number of bits in the sought after pattern.

Task. Create a sketch entitled PatternMatching.ino, and start it off as shown below. Complete the assembly statements that lead to the results as shown in the inset.


11. Parity. In the early days of computing, communication between computers was particularly vulnerable to noise (static) on the lines resulting in errors (dropped bits) in transmission. A strategy developed to reduce the number of errors was for the transmitting and receiving devices to first agree on whether bytes transferred would ALWAYS contain an EVEN or ODD number of bits. A dropped bit could be detected if the number of bits received did not reflect the agreed-upon protocol. Since the basic ASCII table only required 7 bits of information, the eighth bit (sometimes referred to as the parity bit) would be SET or CLEARED depending on the protocol.

Task.

  1. Create project entitled, Parity.ino.
  2. Define a uint8_t-valued global variable, initialized to a random number on the closed interval [0,127]. Display the random number to the serial monitor using the BIN modifier to make it easier to see the bit count.
  3. Implement assembly instructions to count the number of SET bits in the uint8_t variable. Display to confirm.
  4. Develop a set of assembly language instructions that will support an EVEN parity scheme, essentially SETting bit 7 if required.
  5. When finished, your code will have displayed the original uint8_t value and the adjusted value to the Serial Monitor, again using the BIN modifier.
  6. Finally, display the adjusted value on the 8 rightmost LEDs of your bargraph, similar to what you did for the BargraphFlasher exercise. For example, the letter, C (ASCII 67), under EVEN parity, will be depicted as shown below.


10. Two's Complement. In deciding on a strategy for storing negative numbers (given there's no minus sign), computer scientists decided the highest priority was to ensure a number and it's negative added up to 0. The strategy they came up with for determining the negative of a number, that all computers today have adopted, is known as the Two's Complement algorithm. Look up the neg instruction and it declares it to be Two's Complement right in the title!

Tasks (4).

1. Research the Two's Complement algorithm. What is the One's Complement? Apply the Two's Complement to a binary byte value using pencil and paper. What would result if you applied it twice? Try it.
2. Develop the sketch NegativeByte.ino that uses the neg assembly instruction to produce the negative of a global byte variable. Display to confirm. Observations?
3. Develop the sketch, TwosComplement.ino, along the lines of the code below that uses the assembly language instructions to determine the negative of the value variable without using the neg instruction. Display the result to confirm.


4. The neg instruction only operates on a single register (8-bit). Develop the sketch NegativeInt.ino that employs assembly language instructions to determine the negative of a global uint16_t (exactly 16 bits). Display the results to confirm with the BIN modifier.


9. Swapping 2. Declare and initialize a global variable of type uint8_t to 0xF0. Implement assembly language code to interchange the high and low nibbles before assigning the new value back into the global variable. Display the results to confirm.


8. Swapping 1. Declare and initialize two global variables to different values. Implement assembly language instructions to first, load the value into registers, and then interchange the values of the registers, then store the new values back into the global variables. Display the results to confirm.


9 [1718]. POV. The concept of a packed BCD numeric representation of a 2-digit decimal number involves interpreting each nibble of a byte as a 4-bit binary equivalent of a decimal digit. As such, bytes can only store values from 00 to 99 inclusive, with the latter being defined as 1001 1001. The packed BCD encoding of a 16-bit binary number would require 5 nibbles (3 bytes). The value 65535 would be stored as (LSB first),

0011 0101
0101 0101
0000 0110

 

 

 

 

 

 

 

 


9 [1718]. Double Dabble POV. By now you've surely implemented the Double Dabble algorithm discussed weeks ago, in class. If you have, this challenge will be straightforward. If not, well....

For this practice challenge you are to present the result of the algorithm as a POV display on the three CC 7-segment displays provided. A single 4511 BCD-to-segment decoder will supply the segments as you continuously cycle through the three NPN's to provide the grounds in sequence.

Task.

  1. Prototype your circuit EXACTLY as shown below, completing the wiring as necessary. A photo of the output of ldi r16,0xF3 appears as an inset.
  2. Create the AS7 Assembly Project, DoubleDabblePOV. Combine your existing DoubleDabble code with additional code to present the three-byte packed BCD result of the algorithm on the displays provided.
  3. At the end of the period, submit your DoubleDabblePOV.asm code together with your circuit prototype in an email to handin under the Subject Line: DoubleDabblePOV.


8. [1718]. Hexadecimal Display.

Task.

  1. Create an AS7 Assembly Project entitled, HexDisplay.
  2. In the style of ShiftBar2asm, with it's employment of directives and a table (array) of constant bytes addressed by the X, Y, and Z word-sized pointers, generate the continuous animation of the 16 hexadecimal digits in a manner similar to the adjacent graphic.
  3. Define the bit order of your 16 data bytes in your table according to the segment template: gfabcde., with the decimal place occupying the least significant bit. Turn the decimal point on only for the last hexadecimal digit, F, as in the animation. This format is somewhat arbitrary but if your code is to run correctly on my circuit we should follow a common design.
  4. Wire the 8 pins of PORTD of your Arduino directly to the common cathode 7-segment display.
  5. Insert a 500ms delay between each digit.

At the end of the period, attach your your HexDisplay.asm file to an email to handin under the Subject Line: Hex Display


7. [1718] T. Morland's Shift Register-Bargraph Device. [cbi, sbi, clc, sec] From the timing diagram presented in the datasheet for your shift register you can review how the Data (SER), Clock (SRCLK), and Latch (RCLK) pins can be manipulated to feed your data bits into the internal storage flipflops of the IC before presenting (latching) them on the output pins.

Just as J. Shaffer's handy Traffic Light PCB got us started with AVR assembly, we now turn to T. Morland's compact Shift Register-Bargraph PCB to explore low-level shift out assembly code.

Task.

  1. Solder up your Shift-Bar kit. If you know your soldering skills are lacking you might prefer to solder a 16-pin chip seat without the bargraph and insert it afterwards. You may also prefer straight headers instead of the right-angle ones I've supplied you with if you prefer to have your PCB lie flat on the breadboard.
  2. Since only three signal lines are required to drive the board in addition to the two supply lines, you may wish to consider the use of an ATtiny85 for this task.
  3. Using your burgeoning undertanding of the AVR Instruction Set, create the project file ShiftBar.asm and develop the most efficient assembly routine you can think of that duplicates the animation presented to the right. Click on the image to see an expanded view of the wiring to assist you in undertanding the signals T. Morland designed.
  4. Be sure to comment your code thoroughly giving the effort some deep consideration.
  5. If the LEDs appear a little too bright, consider the 3.3V pin on your Arduino rather than the typical 5V we usually operate at.

7. Bit-Shifting (lsl, lsr, rol, ror, asr, lsr). Examine, and be able to explain the difference between of each of these six bit-shifting functions. Identify which ones can be considered 'destructive' in that the contents of the register they operate on is permanently altered?


6. Branching. The Program Counter, PC, is a two-byte register that always holds the address of the next instruction to be executed. Normally, after an instruction has been fetched from Program Flash' and placed into the Instruction Decoder, the Program Counter is incremented by 1, as in PC←PC+1. However, there are 20 branch instructions that cause the Program Counter to increase by a value other than 1, as in PC←PC+k. Each of the four-letter mnemonics for these instruction begin with the letters BR. Look them over and try to remember their unique purpose.


5. [17/18] Bargraph Flasher 10. Within the BIT and BIT-TEST Instruction group you'll find the highly useful rol and ror commands. Emlpoy one of them in your modification of the previous exercise to include all 10 LEDs in the bargraph. Save the sketch as BargraphFlasher10.ino.

 


4. Bargraph Flasher 8 (ser, com). You're aware that sbi and cbi are convenient for setting and clearing individual bits in an I/O register. The ser instruction can be thought of as the complement of the clr instruciton in that it sets ALL bits in a GP register. Alternatively, to set multiple bits in an I/O register, the out instruction is used. All bits in a register can be inverted through the use of the com instruction.

Task.
1. Wire the rightmost 8 LEDs of your bargraph to the PORTD pins on your Arduino.
2. Develop assembly language instructions within a sketch called BargraphFlasher8.ino that results in the effect displayed in the adjacent animation.


9. Analog-to-Digital (ADC) Converter (Peripheral). Chapter 24 of the ATmega328 datasheet discusses the Analog-to-Digital Converter. You should keep this handy for reference. MaxEmbedded provides a terrific primer on the ADC concept and register set for this stage. An excerpt appears below. We'll go through it together in class.

 

 

 

 

 

 

 

 

I have assembled a summary of the applicable ADC Registers (below) from Chapter 24 of the ATmega328P datasheet. After reviewing the recommended ADC example, implement the code necessary to have your pot, acting as a voltage divider, control the speed of your fan by reading the sweep leg (C) on Analog pin 0. Upload your code and test. The fan's speed should be responsive to your turning of the pot.


8. External Interrupts. Pin 7 of the I2C DS1307 RTC, labeled SQW/OUT, provides a configurable square wave, the frequency of which is scaled from its 32.768kHz crystal. Here's an excerpt from page 6 of the datasheet, as well as flag details of its Control Register,

Square Wave/Output Driver. When enabled, the SQWE bit set to 1, the SQW/OUT pin outputs one of four square-wave frequencies (1Hz, 4kHz, 8kHz, 32kHz). The SQW/OUT pin is open drain and requires an external pullup resistor. SQW/OUT operates with either VCC or VBAT applied. The pull up voltage can be up to 5.5V regardless of the voltage on VCC. If not used, this pin can be left floating.

The square wave from IC pin 7, is conveniently presented at the end of a 5-pin male header of Adafruit's breakout board. (This device would have made for a fantastic ACES Appliance but Adafruit thought of it first). E. McAuliffe (ACES '18) provides us with a trace of the 1 Hz signal from our scope above, right (click to enlarge). This exercise will introduce you a superior way to ensure your assembly code applications remain scheduled without wasting clock cycles as we have typically done through the use of an iterative delay function.

Task. (Instructions: cli, sei, reti).

  1. Prepare your hardware platform as shown in the Fritzing diagram above left. A F/M jumper connects the SQW/OUT pin to INT0 (digital pin 2).
  2. Open the Arduino IDE, creating a new sketch out of setRTC.ino, review the code and then upload it to your platform. Confirm the accurate data appears on your Serial Monitor. Return to the code and note the source of the time delay.
  3. Create another new sketch out of RTCSQWConfig.ino , review the code and then upload it to your platform to set the frequency of RTC's square wave to 1 Hz. Confirm the accurate data is being updated every second on your Serial Monitor. Return to the code and note the source of the time delay in this version. (This is the strategy we want to duplicate in assembly)
  4. Now, here's the problem. Unless your RTC board has right angle headers, the position of board blocks access to the ISP header for our ATMEL-ICE. A solution is to relocate it so that SQW/OUT pin inserts directly into INT0 (digital pin 2) as above right. Note. As long as the 3V battery remains intact, the time, date, and control register contents are preserved. Now we can insert our programmer and begin to implement the software.
  5. Open Atmel Studio 7 and create a new AVR Assembly project from your template, naming it ExternalInterrupt.
  6. We'll develop the code together that will respond to the external interrupt sequence triggered by the RTC's square wave.


7. BlinkSafe. We're now ready to take on the incredibly challenging (and hugely advantageous) study of AVR assembly language programming in Atmel Studio 7.

Task.

  1. Weeks ago, you developed both inline and standalone AVR assembly code within the Arduino IDE to blink an LED on pin 13 on the Arduino. Whereas programming with the USB cable employs the Serial UART feature on digital pins 0 and 1 (RX/TX), programming with the Atmel-ICE uses the ISP/SPI header, so it's best to avoid pins 13, 12 and 11 (SCK,MISO & MOSI). Place an LED in pins digital pins 8 and 9 (PB0&PB1). Using your freshly baked AVR Assembly template, create and implement a new AVR assembly project called BlinkSafe that blinks your LED on PB0.
  2. Replace your LED in digital pins 8 and 9 with a bicolor LED. Create and implement a new AVR assembly project called, BlinkBicolor, that results in the LED alternately flashing between red and green.

6. ExploreBits: Exploring the AVR 8-bit RISC (lds, sts, tst, and, or, brne, sbrc, sbrs, inc). A good way to explore the breadth of the AVR's Instruction Set is with some bit-manipulation algorithms. Create the project ExploreBits with a minimal ExploreBits.ino driver file consisting only of the global variable value of type int8_t initialized to any value you like. Add a C loop() function with empty body (we will not be using the loop() function for the time being but main.cpp expects it to be present). Leave your bicolor LED in PortC from the previous exercise.

  1. Add the file ExploreBits.S to the project with #include <avr/io.h> and a global setup() function.
  2. Add AVR assembly functions, green and red that result in your bicolor LED displaying their respective colours. Add temporary assembly instructions to confirm they perform as expected.
  3. Within your assembly setup function, load the contents of the global int8_t variable value into r16.

For each of below, if the assertion is true, your LED should be green, otherwise it should display red. Implement functions for each assertion and call it from your setup function. Your strategy can be destructive in that the value in r16 need not be preserved. Be sure to comment your code fully (comment the previous one out as your move through the exercises).

  1. value is zero. (Hint: tst)
  2. value is odd.
  3. value is greater than 63, without using a compare instruction
  4. value is a multiple of 8. Is your test applicable for negative values as well?
  5. value is negative, without using tst or compare instructions
  6. value is divisible by 48.
  7. value mod 8 is 7.
  8. The lower and upper nibbles of value are the same.
  9. value has an even number of set bits.
  10. value is a power of 2. [multiple strategies]
  11. value has more set bits than clear bits
  12. value mod 11 is 7.
  13. [Hard] value is binary palindrome (of course there should be 16). Interested ACES are encouraged to try Project Euler: Problem 36

 

 

 

 


4. BicolorBlink (com). Place a bicolor LED in PortC. PortC is typically reserved for ADC support but let's give it a digital treatment in this brief study. Create the standalone assembly project BicolorBlink, placing your entire code within BicoloBlink.S. Review and make use of the One's Complement AVR Instruction (com) in this exercise to have the device flash between red and green with a period of your choosing.


3. RGBLED. Place your CC RGBLED into your Arduino pins D6:3 (PD6:3), with the ground pin in D4.

  1. Create a new Arduino project under the name of RGBLED and save it in your Assembly folder. Remove all the C code from the RGBLED.ino driver except for your opening comments.
  2. Click the pulldown tab at the right edge of the IDE and select New Tab. Name the tab, RGBLED.S (case-sensitive)
  3. We'll write AVR Assembly code together that sequences through the RGB colours with 3s intervals between each transition.
  4. Special Function Register Reference: <avr/sfr_defs.h>
  5. ...

2. Traffic Light (clr, lsl, rcall, lsr, rjmp). Place your Schaffer's Traffic Light into your Arduino pins D11:8 (PB3:0), with the ground pin in D8. Develop inline assembly code (using only a single asm() block within each of the setup() and loop() functions) that runs the device continously with 3s-1s-3s delays between Green-Yellow-Red LEDs. Save the sketch as TrafficLightBasic.ino.

  1. Rather than leaving the inline asm block for your delay needs, replace the Arduino C delay(ms) function with an assembly function that does the same thing (waste clock cycles). Review the Mulvey/Morland utility for determining just how many cycles to waste (use the avr-gcc version for this sketch). This modification introduces you to a few new assembly instructions (dec, brne, lpm, nop, and ret)
  2. Save your successful TrafficLightBasic.ino sketch as TrafficLightEnhanced. In this modification we'll access your external C variables to provide greater pin flexibility. Modify the sketch to allow the TrafficLight to be positioned ANYWHERE within PortB.

1. Blink (ldi, out,eor). Using inline assembly, rewrite the Blink Sketch. Use the Arduino C delay() function.