关键词 > ECE-013

ECE-013: COMPUTER SYSTEMS AND “C” PROGRAMMING Lab 7 - Toaster Oven

发布时间:2024-05-28

Hello, dear friend, you can consult us at any time if you have any questions, add WeChat: daixieit

ECE-013: COMPUTER SYSTEMS AND “C” PROGRAMMING

Lab 7 - Toaster Oven

27 Points

Warning

This lab is much more involved than the previous ones, and will take   you more time. You will need to start early and read this lab carefully. Failure to plan is planning to fail.

Introduction

This  lab  introduces  finite  state  machines  as  a  tool  for  programming reactive  systems.  FSMs  are  commonly  used,  though  they  are  rarely explicitly   described   with  those words.  They   are   a  very   powerful technique for organizing the behavior of complex systems (and just as importantly  debugging  them).  FSMs  are  used,  for  example,  in  your microwave, dish washer, thermostat, car, and many others. Mastering software state machines is one of the fundamental skills you will learn in  this  class—and  a  skill  that  will  be  quite  valuable  to  you  in  your programming careers.

For this lab you will  implement a toaster oven  by following the state machine  diagram  we  have  provided.  This  lab  builds  on  the  event- driven  programming used  in the  Bounce lab. A proper state machine uses events to trigger state transitions with an idealized instantaneous transition. This is a key idea in embedded systems.

You will need to able to understand and reason about state machines to do well in this lab. Make sure you do the reading before attempting the  lab.  Use the example  bounce  FSM  provided  here to  rewrite your Bounce code using a state machine implementation as a test of your understanding.  Ask  a  TA/tutor  for  help  in  working  through  this  and you'll understand much better how to implement this lab.

Concepts

.    const variables

.    Timer interrupts

.    Free Running Counters

.    Event-driven programming

.    Finite state machines

Reading

.    This  lab  manual  (you  need  to   read  this  carefully,   probably several times)

. CKO Chapters 5.8– 5.11

.    Wikipedia      on       FSMs: https://en.wikipedia.org/wiki/Finite- state_machine

Required Files:

.    Lab07_main.c

.    Leds.h

.    README.pdf

Lab Files:

. DO NOT EDIT these files:

o BOARD.c/.h – Standard hardware library for CSE13E.

o Oled.h, Ascii.h – These files provides the interface for

manipulating the oled Oled.    No .c files are needed here, as Lab7SupportLib contains compiled definitions for the   functions in Oled.h.

o Buttons.h , ADC.h – Same as the files from the previous

lab.    However, the definitions in these functions are now implemented in Lab7SupportLib.a.

o Lab7SupportLib.a – This is a “static library” file.  It’s similar to a *.o file.  It contains the executable code for the

functions in Buttons.h and ADC.h.  This allows you to use

the ADC and Buttons libraries, without having direct access to our code.

.   To use Lab7SupportLib.a, right-clicking on the

“Libraries” folder in your MPLAB X project panel and

selecting "Add Library/Object File.. .".

.    Do not create a Buttons.c or ADC.c file!

. Edit these files (these are provided as *_template.c/h files, you must rename them):

o Leds.h – a simple macro file.  It serves the same purpose as the Leds_Lab06 library from the previous lab, but

implements all “functions”using macros.

.    Do not create a Leds.c file!

o  Lab07_main.c – This file will contain all of the executable code that you submit for this lab.

Assignment requirements:

In  order  to   master  finite   state  machines,  you   are  going  to implement  a  toaster  over  on  the  microcontroller.  In  this  case, you are going to display the state of the toaster oven graphically on  the  OLED   rather  than   have  you   interact  with   actual   heating elements. This lab requires you to implement all the required toaster oven functionality in Lab07_main.c utilizing the provided libraries.

Toaster Oven Functionality:

.    The system displays (on the OLED) the heating elements state in a little graphical toaster oven, the cooking  mode, the current time (set time or remaining time when on), and the current temperature (except for when in toast mode). Additionally a greater-than sign (>) is used in Bake mode to    indicate   whether    time    or   temp    is    configurable   through    the potentiometer.

o A  function   called   updateOvenOLED()   should   contain   all   OLED- altering  code.    Note  that  the  file Ascii.h  contains  several  special characters that are useful for drawing the oven.

.    First,  the  user  will  select  a  mode  and  configure  a  cook  time  and/or temperature:

o The  toaster  oven   has  3  cooking   modes,  which  can   be  rotated through by pressing BTN3 for < 1s. They are, in order: bake, toast, and broil.

. Bake  mode: Both  temperature  and  time  are  configurable, with  temperature  defaulting  to  350  degrees  F  and  time  to 0:01.  Switching  between  temp  and  time  can  be  done  by holding   BTN3   for    >    1s    (defined   as   a    LONG_PRESS).

Whichever  is selected  has an  indicator  beside  its  label  (the selector  should  always  default  to  time  when  entering  this mode). Both top and bottom heating elements are used when cooking in bake mode.

. Toast mode: Only the time can be configured in this mode, and the temperature  is  not  displayed. There  is  no  selector indicator on the display. Only the  bottom  heating elements come on in toast mode.

. Broil mode: The temperature is fixed at 500 degrees F and only  time  is  configurable  in this  mode. The temperature  is displayed in broil mode. Again, the input selector indicator is not displayed. Only the top heating elements come on in broil mode.

o While   in   this   phase   of   operation,   the   user   can   rotate   the potentiometer to adjust the time or temperature.  There is a 2-state variable,  called  the  “settings  selector”,  which  determines  which setting the pot controls.

.    The  settings  selector  is  switched  by  holding  BTN3  for  >1 second.

.    The  cooking  time  is  derived  from  the  ADC  value  obtained from the Adc library by using only the top 8 bits of the ADC reading and adding  1.   This results in a range from 0:01 to 4:16 minutes.

.   The cooking temperature is obtained from the potentiometer by using only the top 8 bits of the ADC value and adding 300 to it. This allows for temperatures between 300 and 555.

.    Once a  mode, time, and  (if appropriate) temperature are selected, then cooking  is started by pressing down on  BTN4. This turns on the  heating elements  on  the  display  (as  they're  otherwise  off)  and  the  LEDs  (see below).

o Cooking can be ended early by holding down BTN4 for >1 second. This should reset the toaster to the same cooking mode that it was in before the button press.

.    Additionally, if the time/temp selector should return to their settings  when  baking  started.    So,  if the  user  selects  1:00 minute, then cooks for 30 seconds, then cancels cooking, the timer should now say 1:00 minute.

o When  the  toaster  oven  is  on,  the  8  LEDs  indicate  the  remaining cook time in a horizontal “progress bar” to complement the text on the OLED. At the start of cooking, all LEDs should be on.  After 1/8

of the total time has passed, LD1 will turn off. After another 1/8 of the original cook time, LD2 will turn off, and so on until all LEDs are off at the end.

o After  cooking  is  complete,  the  system  will  return  to  the  current mode with the last used settings; the heating elements should be off, the time and temperature reset to the pot value, and the input selector displayed if in bake mode.

. Extra  credit: After  cooking  is  complete,  the  toaster  oven  enters  an “alert” mode, blinking its screen at intervals.

o You will  need to  use the Oled  library’s  invert function, and add a state to the state machine.

o Describe your implementation in your README.pdf.

Code requirements:

When implementing the toaster oven functionality, your code should adhere to the following restrictions:

.    The template code has two timer ISRs.  Keep them very short and simple!  The idea is to get back to your main code as soon as possible.

o The 100Hz timer should be used exclusively to check for button events  and  ADC  events.     This  ensures  the   system  is  very responsive to button presses.

o The 5Hz timer:

.    Sets a TIMER_TICK event flag

.    Increment  the  freerunning  timer.    This  timer  is  never reset, but is instead used as a “global” timer.  This timer is used to determine whether a button  press was a long press or a short press, and is used to determine cooking progress.   This  is  a  useful  technique  when you  need to time   multiple   events   using   a  single  timer   (discussed below).

.    No  floating   point  numbers  are  allowed   in  your  code.  You  will   be performing integer math to get all required values.

o Floating   point   operations   are   much   slower   than   standard arithmetic!     While   it  doesn’t   matter   much   in  this   lab,   it’s important to build good habits now

.    Implement  all  of  your  Toaster  Oven’s  behavioral  logic  with  a  single

state machine, which you should keep in the runOvenSM() function.

o Your  state  machine  should  use  a  single  switch  statement  to check   the   state   variable,   and   each   case   should   use   if()

statements to handle particular events. Don’t forget to break or return!

o You  may  (of  course)  use  helper  functions  to  keep  your  state machine clean, but the overall logic must still follow these rules.

.    Your toaster should be an entirely event-driven system.

o Your state machine should be called from your main loop (ie, in a while(1) inside of your main()), but it should only run when an event occurs.

o Your main loop should not do anything besides polling the event

flag and calling the state machine when appropriate.

.    It should  DEFINITELY not call updateOvenOLED() directly.

This function is very slow, and should only be called when necessary.

o This  state  machine  code  should NOT be  called  directly  by  an interrupt. Interrupts should set event flags that are checked by the state machine loop and passed into runOvenSM().

o Don’t forget to clear your event flags once your state machine is done with them!

.    Create a single struct that holds all toaster oven data: oven state (what state  the  oven  state  machine  is  in),  temperature,  cooking   mode, button press time, cooking start time, and input Selector (whether the pot affects time or temp).

o Create a single instance of this struct as a module-level variable.

o Make sure each member variable has comments indicating what they hold and their units, if any.

.    Used named constants and types:

o Create  a  single typedef’d  enum to  name your  states.   Use  an instance of this type in your module-level struct.

o Use the same technique to name your cook modes and Selector settings.

o All   constants   must   be   declared   as   constants   using   either #define,  enum  or  const variable.   This  includes  constants  like 300 (degrees) or 5 (ticks per second).

.    Use safe data practices:

o Any variables created outside of main must be declared static so that they exist only as module level variables.

o Any strings  used to specify formatting for  (s)printf() should  be declared as const to allow for compiler optimizations.

General:

.    Format    your    code    to     match    the    style     guidelines    in    the

ECE013E_StyleGuidelines document.

.    Make   sure  that  your   code  triggers   no   errors   or   warnings   when compiling. Compilation errors in libraries will result in no credit for that section, which is nearly all of the points available in this lab!

o Commit often and submit early!   That way you can  be sure to

have a compiling version that you can submit.

.    Compilation  errors   in  the   main  file  will   result  in  NO  CREDIT.  Any compilation warnings will result in two lost points.

Lab Writeup:

Your README.pdf must contain the following items. Spelling and grammar

count   as   part   of  your   grade   so  you'll   want  to   proof-read  this   before submitting.  This  will  follow  the  same  rough  outline  as  a  lab  report  for  a regular  science  class.   It  should  be  on  the  order  of three  paragraphs  with several sentences in each paragraph.

o First you should list your name & the names of colleagues who you have collaborated with.

o In the next section you should provide a summary of the lab in your own words. Highlight what you thought were the important aspects of the lab. If these differ from how the lab manual presents things, make a note of that.

o The subsequent section should describe your approach to the  lab. What was your general approach to the  lab?  Did you  read the  manual first or what were your first steps? What went wrong as you worked through it? What  worked  well?  How  would  you  approach  this  lab  differently  if  you were to do it again? How did you work with other students in the class and what did you find helpful/unhelpful?

o The final section should describe the results of you implementing the lab. How did it end up finally? How many hours did you end up spending on it? What did you like about it? What did you dislike? Was this a worthwhile lab? Do you have any suggestions for altering it to make it better? What were the  hardest  parts of  it?  Did the  points distribution for the grading seem appropriate? Did the lab manual cover the material in enough detail to  start  you  off?  Did  examples  or  discussions  during  class  help  your understanding this lab or would more teaching on the concepts in this lab help?

Grading

This assignment consists of 27 points, with a 1-point “cushion” .

Note that all components are human-graded (there’s just not  much we can automate in this lab!)

.    9 points:  Toaster oven functionality

o 3 points:  Oven cycles through three selectable modes of operation, and settings can be selected and adjusted as appropriate.

o 2 points:  Oven counts down, and resets when complete.

o 3 points:   Oven correctly displays the  required  information on the OLED and LEDs.

o 1 point:  Oven cannot get “stuck,” so that the board must be reset.

.    10  points – Followed the specs for code organization, interrupt handling, and output.

o 5 points:  Oven is fully event-driven

o 3 points:  State machine is implemented correctly

o 1 point: Oven timing is correct.

o 1 point: All data is appropriately scoped.

. 1 point extra credit – Invert the display at 2Hz after cooking completes. .    2 points: Code style

.    6 points: Writeup

.    1 point: Leds.h macros get, set, and initialize LEDs correctly. .    -X points:

o NO CREDIT for code with compilation errors

o -2 points: at least 1 compilation warning

o -2 points: for using gotos, extern, or global variables

o Additional deductions at grader discretion

o Additional bonuses at grader discretion, particularly for README.pdf explanations of features that didn’t quite work.

Free Running Counters

In an embedded system, it is often a requirement to know how much time  has elapsed  between two events. You can see this  in everyday use  in  many  everyday  electronic  items.  There  are  several  ways  to accomplish this. One is to dedicate a hardware timer to be started on the  first  event,  and  stopped  when  the  second  occurs.  This  is  very precise,  assuming  you  can  spare  the  hardware  timer,  and  that  the longest time elapsed between events is within the range of the timer. However,  if you  are  going  to  do  this for  more  than  a  single  pair  of events, then you will be using up all of your hardware timers on this task alone.

Another method is to use what is called a free running counter: a timer is   set  to   periodically   interrupt,   and   increments  the  free   running counter.7 To  find  out  the  time  elapsed,  copy  the  value  of  the  free running counter to a variable, startTime, when the first event occurs, and on the second event:

Elapsed Time = current FreeRunning Time – start Time;

The elapsed time will be in units of timer event ticks. Note that you can do this for as many things you want elapsed time for without using any additional  hardware.  Because  this  is  all   happening  in  integer  2’s- complement math, a single rollover on the free running counter does not alter the results of the calculation. Two rollovers of the free running counter between first and second event would be required to give you the wrong elapsed time. If your timer is ticking at 5Hz, and you use a 16-bit integer, then you have over 3 ½ hours before you get a wrong elapsed time.

Integer Math

Most embedded systems or microcontrollers do not have a full floating point unit built into their hardware. As such, floating point math must be emulated and is very processor intensive. We can use floats if we must, but try to minimize their use to absolute necessities.

The larger truth is that you don’t often need floating point, but can get by using integer math (or fixed-point math) most of the time. However, using integer math does require some care in the order of operations to make sure you don’t get the wrong results.

For  example,  let’s  say you  wanted to  convert your ADC  reading  (an unsigned 10 bit integer) to a percentage. The mathematical formula is straightforward:

ADC % = (AD1(C)023(Rea)ding ) × 100

ADCpercent = (ADCGetValue() / 1023) * 100;

However  if you  implement  it that  way, you  will  always  get  0%  as  a result because the integer divide occurs first (and the result will always be 0). Thus the correct way to implement it would be:

ADCpercent = (ADCGetValue() * 100) / 1023;

You  must  take  care  that  the  initial  multiplication  can  fit  into  the variable  size  without  overflowing.  If  it  might  overflow,  then  use  a “cast” to temporarily increase the size for the calculation:

ADCpercent = ( (uint32_t) ADCGetValue() * 100) / 1023;

Lastly, integer math additions and subtractions work when using 2s

complement math such that you get the right result even when going   past the number boundaries. If you are going to divide or multiply by a power of 2, use shifts instead. They are much faster and typically

directly supported by underlying hardware.

Macros

Macros  are  a  very   powerful  tool  that  can   make  code  simpler  to implement, easier to understand, and faster (not necessarily all three every  time!).  But  with  great  power,  comes  great  responsibility.  The macro system in C (unlike some more modern languages) does not do any input checking and mostly does straight textual substitution. This makes it very easy to run into problems.

The most basic usage for macros are for declaring constants using the #define preprocessor directive like:

#define TRUE 1

Using a constant like this makes your code easier to read, so instead of a  number  which  you  may  not  understand,  you  have  a  name  that hopefully  hints  to  its  function.  The  other  advantage  to  this  is  that, unlike using the const modifier, no memory space is used to store this number; it is merely substituted in for the name wherever it appears in the source code.

Using  macro  constants  is  the  most  basic  usage  of  macros.  A  more advanced  use  is  for  writing  multiple-statement  blocks.  These  aren't functions in the regular sense, they don't return anything, but can take arguments. What they are is really a smarter way to do a straight text replacement, basically templated textual replacement.

For example, here's a macro that just divides the PR2 by a number:

#define DIVIDE_IT(num) (PR2 = PR2 / (num))

So if you call it like follows:

DIVIDE_IT(5);

Everything will compile properly and the resulting code would be:

(PR2 = PR2 / (5));

So whatever text is passed in as the argument is substituted for "num" in the output text. Also note that we included parenthesis around num. This  is  to  make  sure  the  following  works  correctly:   DIVIDE_IT(5 + 12*x); If we didn't include the parenthesis, the text output would be:

(PR2 = PR2 / 5 + 12*x);

, which is not what we intended.  We also didn't include a semicolon in the macro because we want the user to add one as they normally do after regular statements in C and for the user to understand that they can treat the macro as a single statement, much like a conventional C function call. But with multiple statements in a macro we have to do something a little extra to keep that functionality. We use curly braces to ensure that the compiler treats this code as a single block (the "\" are to continue the macro on the next line):

#define INIT_ECAN(bsize) { \

CB_Init(&ecan1TxCBuffer, txDataArray, bsize); \

CB_Init(&ecan1RxCBuffer, rxDataArray, bsize); \

}

So with that macro written using a {} block, we can call it like a single function call:

INIT_ECAN(10);

And now the semi-colon works properly and the code is grouped into a single statement.

To   help  debug   macro-related   issues,   MPLAB  X  gives  you  a  very powerful tool.  Right click anywhere on your code and select Navigate -

> View Macro Expansion. The window that opens at the bottom shows the same portion of the code that you have shown in the main coding window, but with all the macros expanded and inlined.