Skip to content

Instantly share code, notes, and snippets.

@bamsbamx
Last active March 29, 2023 18:56
Show Gist options
  • Save bamsbamx/6187b6d3efa70d5beda5103d79e9807c to your computer and use it in GitHub Desktop.
Save bamsbamx/6187b6d3efa70d5beda5103d79e9807c to your computer and use it in GitHub Desktop.
AVR - Wake-from-sleep receiving CAN_INT_vect RX interrupt
/*
* mega32m1_sleep.c
*
* Created: 17/05/2018 14:01:51
* Author : Aner Torre
*
* Sleep modes test. The main purpose of this test is to demonstrate which sleep modes
* can the CAN message RX interrupts wake the MCU from. Blinks a LED for 4 times
* and enters sleep_mode until the MCU is waken by receiving CAN message RX interrupt
*
* The results show that CAN message received interrupts ( ISR(CAN_INT_vect) ) can ONLY
* wake the MCU from SLEEP_MODE_IDLE.
* Power consumptions (INCLUDING MCP2561 CAN transceiver):
* - NORMAL FUNCTION: about 10 mA
* - SLEEP_MODE_IDLE: about 9 mA
* - SLEEP_MODE_PWR_DOWN: about 4.5 mA
*/
#ifndef F_CPU
#define F_CPU 8000000UL
#endif
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/sleep.h>
#include <stdbool.h>
#include <stdint.h>
#include <util/delay.h>
volatile bool msg_rcv = 0;
volatile uint16_t id = 0;
volatile uint8_t data[8];
ISR (CAN_INT_vect) {
int8_t length, savecanpage;
savecanpage = CANPAGE; // Save current MOB
CANPAGE = CANHPMOB & 0xF0; // Selects MOB with highest priority interrupt
if (CANSTMOB & (1 << RXOK)) {
// Interrupt caused by receive finished
id = ((uint16_t)CANIDT2 >> 5) | ((uint16_t)CANIDT1 << 3); // considering 11 bit ID (CAN v2.0A)
length = (CANCDMOB & 0x0F); // DLC, number of bytes to be received
for (int8_t i = 0; i < length; i++) data[i] = CANMSG; // Get data, INDX auto increments CANMSG
CANCDMOB = ((1 << CONMOB1) | (0 << IDE) | (8 << DLC0)); // Enable Reception 11 bit IDE DLC8
msg_rcv = 1;
}
CANSTMOB = 0x00; // Reset reason on selected channel
CANPAGE = savecanpage; // Restore original MOB
}
void chip_init(void) {
// defining all pins as OUTPUT reduces power consumption
DDRB = 0xFF;
DDRC = 0xFF;
DDRD = 0xFF;
DDRE = 0xFF;
PORTB = 0xFE;
PORTC = 0x00;
PORTD = 0x00;
PORTE = 0x00;
PRR = 0x00; // Individual peripheral clocks enabled
}
void can_init(void) {
CANGCON = ( 1 << SWRES ); // Software reset
CANTCON = 0x00; // CAN timing prescaler set to 0;
// Set baud rate to 500 kbit/s (with 8Mhz internal osc)
CANBT1 = 0x02;
CANBT2 = 0x04;
CANBT3 = 0x13;
for (int8_t mob = 0; mob < 6; mob++) {
CANPAGE = (mob << 4); // Selects Message Object 0-5
CANCDMOB = 0x00; // Disable mob
CANSTMOB = 0x00; // Clear mob status register;
}
CANPAGE = ( 1 << MOBNB0 ); // Select MOB1
CANIE2 = ( 1 << IEMOB1 ); // Enable interrupts on MOB1 for reception and transmission
CANGIE = ( 1 << ENIT ) | ( 1 << ENRX ) /*| ( 1 << ENTX )*/; // Enable interrupts ONLY on receive
// Filter all IDs not matching (all except 0x201)
CANIDT1 = (uint8_t) (0x201 >> 3);
CANIDT2 = (uint8_t) (0x201 << 5);
CANIDT3 = 0x00;
CANIDT4 = 0x00;
// mask = 0b11111111111
CANIDM1 = 0xFF >> 3;
CANIDM2 = 0xFF << 5;
CANIDM3 = 0x00;
CANIDM4 = (1 << RTRMSK) | (1 << IDEMSK);
//CANCDMOB = ( 1 << CONMOB1) | ( 1 << IDE ) | ( 8 << DLC0); // Use this to enable reception 29 bit IDE DLC8 (CAN v2.0B)
CANCDMOB = ( 1 << CONMOB1) | ( 0 << IDE ) | ( 8 << DLC0); // Use this to enable reception 11 bit IDE DLC8 (CAN v2.0A)
CANGCON |= (1 << LISTEN); // Put CAN in LISTEN mode (no ACK)
CANGCON |= ( 1 << ENASTB ); // Enable mode. CAN channel enters in enable mode once 11 recessive bits have been read
sei();
}
int main(void) {
chip_init();
can_init();
DDRB |= (1 << PB7);
while (1) {
// Test different sleep modes
// set_sleep_mode(SLEEP_MODE_IDLE);
// sleep_mode();
// interrupt_wait();
//
// set_sleep_mode(SLEEP_MODE_STANDBY);
// sleep_mode();
// interrupt_wait();
//
// /* Three times power down to differentiate */
// set_sleep_mode(SLEEP_MODE_PWR_DOWN);
// sleep_mode();
// set_sleep_mode(SLEEP_MODE_PWR_DOWN);
// sleep_mode();
// set_sleep_mode(SLEEP_MODE_PWR_DOWN);
// sleep_mode();
// interrupt_wait();
// Blink LED for (3 + 1) times and enter SLEEP_MODE_IDLE until CAN RX interrupt. Then, continue...
for (uint8_t i = 0; i < 6; i++) {
PORTB &= ~(1 << PB7);
_delay_ms(1000);
PORTB |= (1 << PB7);
_delay_ms(1000);
if (i == 3) {
set_sleep_mode(SLEEP_MODE_IDLE);
sleep_mode();
}
}
_delay_ms(9);
}
return(0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment