Description
Objective: Implementation of moving average filters with application to discrete-time series.
Consider the periodic, discrete time series, x[n], shown in Figure 1, where the abscissa corresponds to a
discrete-time index, while the ordinate values (in decimal number representation) are given by the marker
atop each stem. Our goal in this assignment will be to implement some elementary moving average filters
using the periodic time series x[n] as input. The specific tasks below have been designed to guide you
through the implementation of the moving average filters.
Figure 1. One period of the time series x[n].
Task 1
1. The first thing for you to do is to compile and simulate the given .asm template called “
template_for_moving_average.” For now, the variable of interest in this template is the register
called value. Run a simulation of this template and use a Watch window to monitor the content of
this register. Note that, as the simulation runs through the code, the values of the value register
correspond exactly to those of the time series x[n] shown in Figure 1. In short, x[n]= value,
where the contents of the value register will evolve in time as the code is executed in the
simulator. Make sure to set the Animation Step Time of the simulator to a setting that is
appropriate for you to be able to see the evolution of the values of x[n], it should be evident to
you that x[n] is indeed periodic..
2. Study the given template code and note that the contents of value are being retrieved, using the
TABLAT pointer, from data stored originally in the PIC’s program memory. This is something
that I did not cover in class, but don’t worry, you do not need to understand the PIC’s TABLAT
mechanism in order to complete this assignment. Thus, by studying the code, I mean that you
should try to get a sense of what the entire code is doing (through simulations, of course) and
make sure that you read all the comments that I wrote there so that you know where to start
writing your code later on.
3. Do not proceed any further in this assignment until you have completed this Task. Please take
step 2 very seriously and email me if something isn’t clear.
Note that Task 1 does not have anything to do with filters, yet.
Task 2
The idea behind a moving average filter may be easily understood via an example. Consider again the
time series x[n] from before, which is now given in Table 1 below, where n is the discrete-time index.
Note that the ellipses (…) in the table mean time continuation, and not missing data.
n … 0 1 2 3 4 5 6 7 8 9 10 11 …
x[n] … 50 0 50 100 150 200 250 200 150 100 50 0 …
Table 1. Periodic discrete-time series x[n].
We want to convolve this time series with the trivial moving average filter given by the following
difference equation.
y[n] = x[n] + x[n − 1]
Note how this simple equation just says that the output, y[n], is simply calculated, for each n, as the
average of two values of x[n] at consecutive time indices, namely, n and n-1. In other words, you take
x[n] (the current data value), add it to x[n-1] (the previous data value), and finally divide the resulting
sum by two. All of this is shown in Table 2 below.
n -1 0 1 2 3 4 5 6 7 8 9 10 11 …
x[n] 100 50 0 50 100 150 200 250 200 150 100 50 0 …
y[n] … 75 25 25 75 125 175 225 225 175 125 75 25 …
Table 2. Periodic time series x[n] and y[n].
Let us illustrate the idea above with another simple example corresponding to the following difference
equation.
z[n] = x[n] + x[n − 2]
Now try to convince yourself that the output z[n] corresponding to this new moving average filter should
be as in Table 3.
n -1 0 1 2 3 4 5 6 7 8 9 10 11 …
x[n] 100 50 0 50 100 150 200 250 200 150 100 50 0 …
z[n] … 100 50 50 50 100 150 200 200 200 150 100 50 …
Table 3. Periodic time series x[n] and z[n].
As an exercise, and to prepare you for what is to come later, please generate the output table for each of
the following difference equations. You may need to extend the tables to allow for higher time indices in
order to show the output correctly. Please include each table in your report. Each table should look similar
to either Table 2 or 3.
a[n] = x[n] + x[n − 3]
b[n] = x[n] + x[n − 4]
c[n] = x[n] + x[n − 5]
Task 3
Now we finally arrive at the part of this assignment where you get to implement the moving average
filters on the PIC using assembly language.
First, consider again the filter y[n] above, and note that in order to calculate the output we need to know
x[n] at time indices t=n and t=n-1, and because of this we call y[n] a first-order filter. This should be
clear from the equation for y[n]. Similarly, for the other filter, z[n], in order to compute the output for
z[n] we need to know x[n] at time indices t=n and t=n-2. Thus z[n] is a second-order filter. It is
important that you understand what is really going on behind the scenes with the filter z[n], so we discuss
it in more detail in the following paragraph.
We have seen that in order to compute the output for z[n], at each time index, we need to know x[n] at
time indices t=n and t=n-2. This means that the following sequential steps are executed as the time index
increases..
1. To compute z[0], we need x[0] and x[-2].
2. To compute z[1], we need x[1] and x[-1].
3. To compute z[2], we need x[2] and x[0].
4. To compute z[3], we need x[3] and x[1].
5. …
6. This algorithm simply continues ad infinitum.
Note how, in step 1 above, we needed x[0] and x[-2] to compute z[0]. This is clear from the equation for
z[n]. But, in doing so, what happened with the data with time index t = -1 which lies between x[0] and x[-
2], namely x[-1]? Did we discard it or throw it away? Well, we cannot simply forget about it or throw it
away because, if you look at step 2 above, we will need x[-1] in order to compute z[1]. (Can you see
that?) If we continue this process, as step 6 indicates, then you will notice that at every time index during
which the filter z[n] is acting upon the data x[n], we need to have saved, somewhere in our computer
memory, the data corresponding to three consecutive time indices. That is, we need to have saved x[n]
at indices t=n, n-1, and n-2. Note, however, that at each time index, the output z[n] only requires two out
of the three consecutive values that we have saved in our memory. Let us call this allocated section in
our computer memory, made up of only three data variables, a memory buffer. As the time series evolves
in time, our memory buffer must be updated with the (three) appropriate consecutive values so that our
filter produces the correct average output at each time index. It may help you to revisit Tables 2 and 3
above with this new understanding that you have to create a memory buffer.
Now, consider the more general formula for an averaging filter.
d[n] = x[n] + x[n − k]
Armed with our understanding of z[n], we can see that in order to be able to implement this more general
filter, d[n], properly, we need to create a memory buffer which contains k variables. Now, there are two
different memory buffers that we can use: a linear buffer and a circular buffer. Since we will be concerned
with moving average filters of low order, it will be sufficient to focus on implementing a linear buffer. In
order to know what the differences between these two buffers are, please take a look at the following
website: https://www.allaboutcircuits.com/technical-articles/circular-buffer-a-critical-element-of-digitalsignal-pro cessors/
You should pay particularly close attention to the discussion surrounding Figure 3 from that reference.
Your task now is to read up on the linear buffer details in the reference provided above and, finally, to
implement the filter y[n] shown above. Below are some guidelines to help you do this. Also. please read
the comments in the .asm provided to you in order to see where the code for this assignment should be
written. The two main parts that you have to figure out for this are as follows:
1. Implementation of the linear buffer.
a. This should be done in a separate subroutine of its own.
b. You will need to create several variables for you to save data into. Hint: for y[n] your
buffer needs to be three variables long.
c. The discrete-time series data, x[n], to be saved in the buffer will be retrieved from the
value register discussed in step 1 of Task 1 above. In other words, the input to your filter,
y[n], will be the data x[n] = value, where the contents of the value register will evolve in
time as the code is executed in the simulator.
2. Arithmetical computations.
a. Note that the data from the value register is eight bits long.
b. y[n] requires the sum of two variables and then a division by two. In this setting of
fixed-point arithmetic, it can be shown that these two operations are not commutative.
Therefore, in order to achieve the full dynamic range of the filter, you need to add the
data values first, and then divide the sum by two at the end.
c. An understanding of the carry bit inside the STATUS register will be crucial in this part.
Take a look at my Lecture 0 slides, the book or the PIC datasheet for more details about
the carry bit and the STATUS register.
3. You must simulate your code and make sure that it produces the data corresponding to y[n]
shown in Table 2 above.
4. The following instructions from the instruction set may be useful when implementing this moving
average filter.
a. movff
b. movf
c. addwf
d. rrcf
e. andlw
Task 4
Having completed Task 3, please write the .asm code to implement the other moving average filters
specified above, namely, z[n], a[b], b[n], and c[n].
1. Please generate separate .asm files for each filter. (Yes, I expect five (5) different .asm files.)
2. Note that once you figure out Task 3, then Task 4 becomes a simple extension of your work. If it
doesn’t seem to you like that is the case, then you should go back to Task 3 and make sure that you
have a thorough understanding of that Task before you attempt this Task 4.