1 – Timer Interrupts [50 pts]
In this problem, you will create a PWM waveform with a specific duty cycle value using the
microcontroller system with timer unit and interrupt description given in file “datasheet_for_q1”:
• The microcontroller is an 8-bit system and its clock runs at 8MHz.
• The timer is also 8-bit.
• The timer uses only the internal clock and there are 3 predefined pre-scale values:
o 1: No pre-scaling
o 8: System clock/8
o 64: System clock/64
• There are two modes of the timer:
o Normal Mode: The timer counts up to the MAX value and then restarts from the
BOTTOM (where MAX=maximum value that an 8-bit counter can go up to and
BOTTOM=0).
o Clear on Compare (CTC) Mode: The timer counts up to the TOP value (defined by
the OCRA register) and then restarts from the BOTTOM (=0).
• The registers in the timer unit are as follows:
o TCNT: Keeps the count value of the timer.
o OCRA: Keeps the output compare value A.
o OCRB: Keeps the output compare value B.
o TCR: Controls the modes and properties of the timer/counter. The layout of the
individual bits and how they can be controlled are shown below:
TCR:
7th bit 6th bit 5th bit 4th bit 3rd bit 2nd bit 1st bit 0th bit
COMPB COMPA OVF OCIA OCIE TM PS1 PS0
• The first two bits, PS1 and PS0 are used for pre-scaling:
o PS1 = 0, PS0 = 0: Timer is off.
o PS1 = 0, PS0 = 1: No pre-scaling (i.e. pre-scale value = 1).
o PS1 = 1, PS0 = 0: Pre-scale value = 8.
o PS1 = 1, PS0 = 1: Pre-scale value = 64.
o And setting a pre-scalar value starts the timer.
• TM bit is used to set the timer mode.
o TM = 0: Normal mode.
o TM = 1: CTC mode.
• OVF bit is used to see if the timer overflows. This bit is set to 1 whenever the TCNT value
goes over MAX and becomes 0. If you do not use interrupts, this bit does not clear
automatically, i.e. it needs to be cleared manually by setting it to 0.
• COMPA bit is used to see if the timer value matches the OCRA register value. If TCNT
becomes equal to OCRA, COMPA value becomes 1. If you do not use interrupts, this bit
does not clear automatically, i.e. it needs to be cleared manually by setting it to 0.
• COMPB bit is used to see if the timer value matches the OCRB register value. If TCNT
becomes equal to OCRB, COMPB value becomes 1. This bit needs to be cleared manually
by setting it to 0.
COMPE375 Exam3 – 3
• OCIE bit, when set to 1, enables the timer overflow interrupt. If enabled, whenever the
TCNT value goes over MAX and becomes 0, the ISR, with the “Timer_OVF_vect”
interrupt vector name is called. Also, this way, the OVF bit is automatically cleared, i.e.
you do not have to manually clear it.
• OCIA bit, when set to 1, enables the OCRA compare interrupt. If enabled, whenever the
TCNT value matches the OCRA register, the ISR, with the “Timer_OCRA_vect” interrupt
vector name is called. Also, this way, the COMPA bit is automatically cleared, i.e. you do
not have to manually clear it.
• You should not make any assumption about the initial values of the bits in the TCR register.
Interrupt-related system details:
• The system has a function called, egi(), that globally enables the interrupts.
• The interrupt service routine (ISR) definition is same as your AVR device, i.e. you can
implement a specific ISR function as:
ISR(ISR vector name){}
Assume that you are creating a PWM waveform with 25% duty cycle, which will be observed
on an LED with the following sequence:
Start LED OFF Compare A occurs LED ON Timer overflows Back to Start
The period of the PWM waveform should be the maximum value you can have for this given
timer unit (i.e. the maximum value you can time with this timer).
Use the following code skeleton to implement this PWM waveform using interrupts. Noninterrupt driven answers will not receive any credit.
i. You should implement the timer_init() function (sets up the timer
parameters).
ii. You can assume that there are predefined variables for registers (TCR, TCNT,
OCRA, OCRB, etc.).
iii. You should use the predefined statements (see the two #define statements) to
turn on and off the LED.
iv. You should use the two given ISR skeletons to implement the relevant
interrupts. These ISRs are where the duty cycle is created. Note that in the ISR
skeletons, the ISR vector names are not written; you need to fill in those parts.
v. The infinite loop in the main function should be empty.
COMPE375 Exam3 – 4
ISR(Timer_OVF_vect){
LEDOFF;
}
ISR(Timer_OCRA_vect){
LEDON;
}
void timer_init(){
/* Here you need to choose the normal mode with pre-scale 64 to get the
maximum possible period with this given timer (counting maximum number of
cycles with the highest pre-scale value). The PWM structure requires the
OFF time until OCRA compare match happens. Thus, the value of OCRA should
correspond to 75% OFF time. Then, since the MAX value for the normal mode
has 256 time units and the value of OCRA corresponds to 75% OFF time, the
OCRA value becomes 256*(1-0.25) – 1 = 191. */
OCRA = 191; //set the OCRA value
TCR &= ~(1<<2); //set the timer mode to normal mode
TCR |= (1<<3) | (1<<4); //enable the OCIE/OCIA interrupts
TCR |= (1<<0) | (1<<1); //set the pre-scaler value to 64
}
int main(){
egi();
timer_init();
while(1){
}
return 0;
}
COMPE375 Exam3 – 5
2 – Analog I/O Programming [30 pts]
Assume that we have the microcontroller system with ADC description given in file
“datasheet_for_q2”:
• The microcontroller is an 8-bit system.
• The ADC unit is a 5-bit Analog to Digital Converter.
• We have an 8-bit control and status register for the ADC unit called ADC_CSR, with
the following bit descriptions:
7th bit 6th bit 5th bit 4th bit 3rd bit 2nd bit 1st bit 0th bit
CH2 CH1 CH0 CC SC – – –
where:
o CH2-CH1-CH0 bits are used for channel selection, i.e. this ADC unit has 8
channels. (111 Channel 7, 110 Channel 6, … , 000 Channel 0)
o SC bit is used to start a conversion. When this bit is set to 1, an ADC conversion
operation starts. This bit is automatically cleared when the conversion ends.
o CC bit is used to inform the system that an ADC conversion has completed. This
bit is set to 1 when conversion ends and automatically cleared when the conversion
value is read.
o The other bits are reserved.
• The ADC conversion result is stored in an 8-bit register, named ADC_DR. This
assignment by default occurs in a right-justified way, i.e. the ADC conversion result is
by default placed in the 5 least significant bits of ADC_DR.
Write a function, using the prototype below, that has the following functionalities:
• The function should take two parameters: a pointer that represents the beginning point
of a character array (arr) and the size of the incoming array (size).
• Each array element, arr[i], should be filled with ADC conversion result from the ADC
channel “i modulo 8”. ADC channel should be set before a conversion starts. For
example, arr[0] is assigned to the conversion result from channel 0, arr[1] is assigned
to the conversion result from channel 1, etc.
• ADC conversions should be handled sequentially, starting with the conversion that
corresponds to the array element with index 0.
• For each conversion, you should wait until the ADC conversion ends to read the
conversion value.
• Each ADC conversion value should be left justified (the ADC result should be in the
5 most significant bits and the 3 least significant bits are zeros) before stored in the
corresponding array location.
COMPE375 Exam3 – 6
void myFunction(char* arr, int size){
int i;
char current_channel;
for(i=0; i<size; i++){
//First, set the ADC channel
current_channel = i & 7; // OR i % 8
ADC_CSR &= ~(0b11100000); //Clear the CH bits
ADC_CSR |= current_channel << 5; //Set the CH bits
/* Here, there are two important observations:
Sample Solution