Last active
March 29, 2023 18:56
-
-
Save bamsbamx/6187b6d3efa70d5beda5103d79e9807c to your computer and use it in GitHub Desktop.
AVR - Wake-from-sleep receiving CAN_INT_vect RX interrupt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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