In the pin definitions below, we have defined the PORT as well.
Everything works the same way as before, except you have to change PORTx with the PORT definition.
Example: LED1_PORT.OUTSET = (1 << LED1);
*/
#define LED1 4 //pin
#define LED2 5 //pin
#define LED3 5 //pin
#define LED1_PORT PORTD //port
#define LED2_PORT PORTD //port
#define LED3_PORT PORTC //port
#define SW1 2 //pin
#define SW2 1 //pin
#define SW3 2 //pin
boolSW1_pressed,SW2_pressed;//Keeping track of button status
#define SW1_PORT PORTC //port
#define SW2_PORT PORTD //port
#define SW3_PORT PORTD //port
#define TOP_PERIOD 0x55
/*
In this exercise, you will use PWM to control the brightness of LEDs
Once we have PWM set up, controlling the brightness is super easy!
*/
intmain(void)
{
//Set LED pins as output
PORTB.DIRSET=(1<<LED1)|(1<<LED2)|(1<<LED3);
//Because the LEDs are active low, we invert the output. This means high PWM value -> bright led
PORTB.PIN0CTRL|=(1<<PORT_INVEN_bp);
PORTB.PIN1CTRL|=(1<<PORT_INVEN_bp);
PORTB.PIN4CTRL|=(1<<PORT_INVEN_bp);
sei();
//Pullups
PORTA.PIN5CTRL|=(1<<PORT_PULLUPEN_bp);
PORTA.PIN6CTRL|=(1<<PORT_PULLUPEN_bp);
PORTA.PIN7CTRL|=(1<<PORT_PULLUPEN_bp);
LED1_PORT.DIRSET=(1<<LED1);//Datasheet say that we should put pins to PWM as outputs.
LED2_PORT.DIRSET=(1<<LED2);//Also, since we put signals out on them, this make sense.
/*We will be using timer 1 in single (not split) mode.
It is highly recommended that you read chapter 20.3.3.4 in the datasheet
on timer compare channels.
There you will find a sub-capter on the single-slope PWM we will be using.
/**
In dataheet: "http://ww1.microchip.com/downloads/en/DeviceDoc/40002016A.pdf", section 4.1, Multiplexed signals, we see that if we want to use TCA0 with PD4 and PD5,
we need to use portmux to change TCA to Port D. We find that the led is at port D, pin 4 and 5 by looking at datasheets and silk text for PCBs.
*/
PORTMUX.TCAROUTEA=(0x3<<0);
//First, enable the timer
TCA0.SINGLE.CTRLA|=(TCA_SINGLE_ENABLE_bm);
TCA0.SPLIT.CTRLD|=(1<<TCA_SPLIT_SPLITM_bp);//Set splitmode to get access to WO4 and WO5
//Set the mode of the timer to single slope PWM
TCA0.SINGLE.CTRLB|=(0x03<<TCA_SINGLE_WGMODE0_bp);
//We have to override the normal pin opperation so the PWM is pushed directly to the pin
//Hint: WO0 and WO1 are connected to leds. We need to override them to get the PWM out.
/*Timer A is a 16 bit timer. This will give us a frequency of 25Hz, we can see it flicker.
By lowering the period, (PER) we get higher frequency at the cost of lower resolution.
*/
TCA0.SPLIT.CTRLA|=(1<<TCA_SPLIT_ENABLE_bp)|(TCA_SPLIT_CLKSEL_DIV1_gc);//Enable the TCA and set prescaler to 1. Try different prescalers and see what happens.
//Set the period to 12 bit. (This results in a frequency of ~400Hz.)
TCA0.SINGLE.PER=0x0fff;//We set our top to have a sufficiently high frequency (Top at 16 bit (0xffff) ~25Hz, 12 bit (0x0fff) ~400Hz)
TCA0.SPLIT.CTRLB|=(1<<TCA_SPLIT_HCMP1EN_bp)|(1<<TCA_SPLIT_HCMP2EN_bp);//Enable High Compare 1 and 2 to get output on WO4 and WO5. See Figure 19-13 in functional description datasheet.
//We can now control the PWM duty cycle by simply writing values to the CMP0 and CMP1 registers.
TCA0.SINGLE.CMP0=0x0000;//We have inverted the pin outputs so this is MIN
TCA0.SINGLE.CMP1=0x0fff;//We have inverted the pin outputs so this is MAX
TCA0.SPLIT.HPER=TOP_PERIOD;// Set period for High compare. See figures in datasheet. But this is the PWM Period.
TCA0.SPLIT.HCMP1=0x00;//See figures in datasheet. This is the PWM duty cycle.
TCA0.SPLIT.HCMP2=0x00;
while(1){
...
...
@@ -74,37 +73,19 @@ int main(void)
Change the brightness based on buttons
*/
//Here we use button pushes to shift the CMP register so we get brightness steps
//adding and subtracting works just as well
//It is largely up to the student to choose how the led brightness is changed.
if(!(PORTA.IN&(1<<SW1))){
if(!SW1_pressed){
TCA0.SINGLE.CMP0=(((TCA0.SINGLE.CMP0<<1)+1)&(0x0fff));//Shift in a 1, and cut off excess to 12 bit
TCA0.SINGLE.CMP1>>=1;
SW1_pressed=true;
}
}
else{
SW1_pressed=false;
}
if(!(PORTA_IN&(1<<SW2))){
if(!SW2_pressed){
TCA0.SINGLE.CMP1=(((TCA0.SINGLE.CMP1<<1)+1)&(0x0fff));//Shift in a 1, and cut off excess to 12 bit