commit
						1b232a6c18
					
				
				 25 changed files with 476 additions and 456 deletions
			
			
		@ -1 +0,0 @@ | 
				
			|||||||
obj/* | 
					 | 
				
			||||||
@ -1,58 +0,0 @@ | 
				
			|||||||
# :set noet
 | 
					 | 
				
			||||||
PROJ_NAME = comma
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
CFLAGS  = -O2 -Wall -std=gnu11 -DPEDAL
 | 
					 | 
				
			||||||
CFLAGS += -mlittle-endian -mthumb -mcpu=cortex-m3
 | 
					 | 
				
			||||||
CFLAGS += -msoft-float -DSTM32F2 -DSTM32F205xx
 | 
					 | 
				
			||||||
CFLAGS += -I ../inc -I ../ -I ../../ -nostdlib
 | 
					 | 
				
			||||||
CFLAGS += -T../stm32_flash.ld
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
STARTUP_FILE = startup_stm32f205xx
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
CC = arm-none-eabi-gcc
 | 
					 | 
				
			||||||
OBJCOPY = arm-none-eabi-objcopy
 | 
					 | 
				
			||||||
OBJDUMP = arm-none-eabi-objdump
 | 
					 | 
				
			||||||
DFU_UTIL = "dfu-util"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# pedal only uses the debug cert
 | 
					 | 
				
			||||||
CERT = ../../certs/debug
 | 
					 | 
				
			||||||
CFLAGS += "-DALLOW_DEBUG"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
canflash: obj/$(PROJ_NAME).bin | 
					 | 
				
			||||||
	../../tests/pedal/enter_canloader.py $<
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
usbflash: obj/$(PROJ_NAME).bin | 
					 | 
				
			||||||
	../../tests/pedal/enter_canloader.py; sleep 0.5
 | 
					 | 
				
			||||||
	PYTHONPATH=../../ python -c "from python import Panda; p = [x for x in [Panda(x) for x in Panda.list()] if x.bootstub]; assert(len(p)==1); p[0].flash('obj/$(PROJ_NAME).bin', reconnect=False)"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
recover: obj/bootstub.bin obj/$(PROJ_NAME).bin | 
					 | 
				
			||||||
	../../tests/pedal/enter_canloader.py --recover; sleep 0.5
 | 
					 | 
				
			||||||
	$(DFU_UTIL) -d 0483:df11 -a 0 -s 0x08004000 -D obj/$(PROJ_NAME).bin
 | 
					 | 
				
			||||||
	$(DFU_UTIL) -d 0483:df11 -a 0 -s 0x08000000:leave -D obj/bootstub.bin
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
obj/main.o: main.c ../*.h | 
					 | 
				
			||||||
	mkdir -p obj
 | 
					 | 
				
			||||||
	$(CC) $(CFLAGS) -o $@ -c $<
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
obj/bootstub.o: ../bootstub.c ../*.h | 
					 | 
				
			||||||
	mkdir -p obj
 | 
					 | 
				
			||||||
	$(CC) $(CFLAGS) -o $@ -c $<
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
obj/$(STARTUP_FILE).o: ../$(STARTUP_FILE).s | 
					 | 
				
			||||||
	$(CC) $(CFLAGS) -o $@ -c $<
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
obj/%.o: ../../crypto/%.c | 
					 | 
				
			||||||
	$(CC) $(CFLAGS) -o $@ -c $<
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
obj/$(PROJ_NAME).bin: obj/$(STARTUP_FILE).o obj/main.o | 
					 | 
				
			||||||
  # hack
 | 
					 | 
				
			||||||
	$(CC) -Wl,--section-start,.isr_vector=0x8004000 $(CFLAGS) -o obj/$(PROJ_NAME).elf $^
 | 
					 | 
				
			||||||
	$(OBJCOPY) -v -O binary obj/$(PROJ_NAME).elf obj/code.bin
 | 
					 | 
				
			||||||
	SETLEN=1 ../../crypto/sign.py obj/code.bin $@ $(CERT)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
obj/bootstub.bin: obj/$(STARTUP_FILE).o obj/bootstub.o obj/sha.o obj/rsa.o | 
					 | 
				
			||||||
	$(CC) $(CFLAGS) -o obj/bootstub.$(PROJ_NAME).elf $^
 | 
					 | 
				
			||||||
	$(OBJCOPY) -v -O binary obj/bootstub.$(PROJ_NAME).elf $@
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
clean: | 
					 | 
				
			||||||
	rm -f obj/*
 | 
					 | 
				
			||||||
@ -1,30 +0,0 @@ | 
				
			|||||||
MOVE ALL FILES TO board/pedal TO FLASH | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This is the firmware for the comma pedal. It borrows a lot from panda. | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
The comma pedal is a gas pedal interceptor for Honda/Acura. It allows you to "virtually" press the pedal. | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
This is the open source software. Note that it is not ready to use yet. | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
== Test Plan == | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
* Startup | 
					 | 
				
			||||||
** Confirm STATE_FAULT_STARTUP | 
					 | 
				
			||||||
* Timeout | 
					 | 
				
			||||||
** Send value | 
					 | 
				
			||||||
** Confirm value is output | 
					 | 
				
			||||||
** Stop sending messages | 
					 | 
				
			||||||
** Confirm value is passthru after 100ms | 
					 | 
				
			||||||
** Confirm STATE_FAULT_TIMEOUT | 
					 | 
				
			||||||
* Random values | 
					 | 
				
			||||||
** Send random 6 byte messages | 
					 | 
				
			||||||
** Confirm random values cause passthru | 
					 | 
				
			||||||
** Confirm STATE_FAULT_BAD_CHECKSUM | 
					 | 
				
			||||||
* Same message lockout | 
					 | 
				
			||||||
** Send same message repeated | 
					 | 
				
			||||||
** Confirm timeout behavior | 
					 | 
				
			||||||
* Don't set enable | 
					 | 
				
			||||||
** Confirm no output | 
					 | 
				
			||||||
* Set enable and values | 
					 | 
				
			||||||
** Confirm output | 
					 | 
				
			||||||
@ -1,291 +0,0 @@ | 
				
			|||||||
//#define DEBUG
 | 
					 | 
				
			||||||
//#define CAN_LOOPBACK_MODE
 | 
					 | 
				
			||||||
//#define USE_INTERNAL_OSC
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "../config.h" | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "drivers/drivers.h" | 
					 | 
				
			||||||
#include "drivers/llgpio.h" | 
					 | 
				
			||||||
#include "gpio.h" | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define CUSTOM_CAN_INTERRUPTS | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "libc.h" | 
					 | 
				
			||||||
#include "safety.h" | 
					 | 
				
			||||||
#include "drivers/adc.h" | 
					 | 
				
			||||||
#include "drivers/uart.h" | 
					 | 
				
			||||||
#include "drivers/dac.h" | 
					 | 
				
			||||||
#include "drivers/can.h" | 
					 | 
				
			||||||
#include "drivers/timer.h" | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define CAN CAN1 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//#define PEDAL_USB
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef PEDAL_USB | 
					 | 
				
			||||||
  #include "drivers/usb.h" | 
					 | 
				
			||||||
#endif | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define ENTER_BOOTLOADER_MAGIC 0xdeadbeef | 
					 | 
				
			||||||
uint32_t enter_bootloader_mode; | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void __initialize_hardware_early() { | 
					 | 
				
			||||||
  early(); | 
					 | 
				
			||||||
} | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ********************* serial debugging *********************
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void debug_ring_callback(uart_ring *ring) { | 
					 | 
				
			||||||
  char rcv; | 
					 | 
				
			||||||
  while (getc(ring, &rcv)) { | 
					 | 
				
			||||||
    putc(ring, rcv); | 
					 | 
				
			||||||
  } | 
					 | 
				
			||||||
} | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef PEDAL_USB | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int usb_cb_ep1_in(uint8_t *usbdata, int len, int hardwired) { return 0; } | 
					 | 
				
			||||||
void usb_cb_ep2_out(uint8_t *usbdata, int len, int hardwired) {} | 
					 | 
				
			||||||
void usb_cb_ep3_out(uint8_t *usbdata, int len, int hardwired) {} | 
					 | 
				
			||||||
void usb_cb_enumeration_complete() {} | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int usb_cb_control_msg(USB_Setup_TypeDef *setup, uint8_t *resp, int hardwired) { | 
					 | 
				
			||||||
  int resp_len = 0; | 
					 | 
				
			||||||
  uart_ring *ur = NULL; | 
					 | 
				
			||||||
  switch (setup->b.bRequest) { | 
					 | 
				
			||||||
    // **** 0xe0: uart read
 | 
					 | 
				
			||||||
    case 0xe0: | 
					 | 
				
			||||||
      ur = get_ring_by_number(setup->b.wValue.w); | 
					 | 
				
			||||||
      if (!ur) break; | 
					 | 
				
			||||||
      if (ur == &esp_ring) uart_dma_drain(); | 
					 | 
				
			||||||
      // read
 | 
					 | 
				
			||||||
      while ((resp_len < min(setup->b.wLength.w, MAX_RESP_LEN)) && | 
					 | 
				
			||||||
                         getc(ur, (char*)&resp[resp_len])) { | 
					 | 
				
			||||||
        ++resp_len; | 
					 | 
				
			||||||
      } | 
					 | 
				
			||||||
      break; | 
					 | 
				
			||||||
  } | 
					 | 
				
			||||||
  return resp_len; | 
					 | 
				
			||||||
} | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ***************************** toyota can checksum ****************************
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int can_cksum(uint8_t *dat, uint8_t len, uint16_t addr) | 
					 | 
				
			||||||
{ | 
					 | 
				
			||||||
	uint8_t checksum = 0; | 
					 | 
				
			||||||
	checksum =((addr & 0xFF00) >> 8) + (addr & 0x00FF) + len + 1; | 
					 | 
				
			||||||
	//uint16_t temp_msg = msg;
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	for (int ii = 0; ii < len; ii++) | 
					 | 
				
			||||||
	{ | 
					 | 
				
			||||||
		checksum += (dat[ii]); | 
					 | 
				
			||||||
		//temp_msg = temp_msg >> 8;
 | 
					 | 
				
			||||||
	} | 
					 | 
				
			||||||
	//return ((msg & ~0xFF) & (checksum & 0xFF));
 | 
					 | 
				
			||||||
	return checksum; | 
					 | 
				
			||||||
} | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ***************************** can port *****************************
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// addresses to be used on CAN
 | 
					 | 
				
			||||||
#define CAN_GAS_INPUT  0x200 | 
					 | 
				
			||||||
#define CAN_GAS_OUTPUT 0x201 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void CAN1_TX_IRQHandler() { | 
					 | 
				
			||||||
  // clear interrupt
 | 
					 | 
				
			||||||
  CAN->TSR |= CAN_TSR_RQCP0; | 
					 | 
				
			||||||
} | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// two independent values
 | 
					 | 
				
			||||||
uint16_t gas_set_0 = 0; | 
					 | 
				
			||||||
uint16_t gas_set_1 = 0; | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define MAX_TIMEOUT 10 | 
					 | 
				
			||||||
uint32_t timeout = 0; | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define NO_FAULT 0 | 
					 | 
				
			||||||
#define FAULT_BAD_CHECKSUM 1 | 
					 | 
				
			||||||
#define FAULT_SEND 2 | 
					 | 
				
			||||||
#define FAULT_SCE 3 | 
					 | 
				
			||||||
#define FAULT_STARTUP 4 | 
					 | 
				
			||||||
#define FAULT_TIMEOUT 5 | 
					 | 
				
			||||||
#define FAULT_INVALID 6 | 
					 | 
				
			||||||
uint8_t state = FAULT_STARTUP; | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void CAN1_RX0_IRQHandler() { | 
					 | 
				
			||||||
  while (CAN->RF0R & CAN_RF0R_FMP0) { | 
					 | 
				
			||||||
    #ifdef DEBUG | 
					 | 
				
			||||||
      puts("CAN RX\n"); | 
					 | 
				
			||||||
    #endif | 
					 | 
				
			||||||
    uint32_t address = CAN->sFIFOMailBox[0].RIR>>21; | 
					 | 
				
			||||||
    if (address == CAN_GAS_INPUT) { | 
					 | 
				
			||||||
      // softloader entry
 | 
					 | 
				
			||||||
      if (CAN->sFIFOMailBox[0].RDLR == 0xdeadface) { | 
					 | 
				
			||||||
        if (CAN->sFIFOMailBox[0].RDHR == 0x0ab00b1e) { | 
					 | 
				
			||||||
          enter_bootloader_mode = ENTER_SOFTLOADER_MAGIC; | 
					 | 
				
			||||||
          NVIC_SystemReset(); | 
					 | 
				
			||||||
        } else if (CAN->sFIFOMailBox[0].RDHR == 0x02b00b1e) { | 
					 | 
				
			||||||
          enter_bootloader_mode = ENTER_BOOTLOADER_MAGIC; | 
					 | 
				
			||||||
          NVIC_SystemReset(); | 
					 | 
				
			||||||
        } | 
					 | 
				
			||||||
      } | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      // normal packet
 | 
					 | 
				
			||||||
      uint8_t *dat = (uint8_t *)&CAN->sFIFOMailBox[0].RDLR; | 
					 | 
				
			||||||
      uint8_t *dat2 = (uint8_t *)&CAN->sFIFOMailBox[0].RDHR; | 
					 | 
				
			||||||
      uint16_t value_0 = (dat[0] << 8) | dat[1]; | 
					 | 
				
			||||||
      uint16_t value_1 = (dat[2] << 8) | dat[3]; | 
					 | 
				
			||||||
      uint8_t enable = (dat2[0] >> 7) & 1; | 
					 | 
				
			||||||
      uint8_t index = 0; | 
					 | 
				
			||||||
      if (can_cksum(dat, 5, CAN_GAS_INPUT) == dat2[1]) { | 
					 | 
				
			||||||
        if (index == 0) { | 
					 | 
				
			||||||
          #ifdef DEBUG | 
					 | 
				
			||||||
            puts("setting gas "); | 
					 | 
				
			||||||
            puth(value); | 
					 | 
				
			||||||
            puts("\n"); | 
					 | 
				
			||||||
          #endif | 
					 | 
				
			||||||
          if (enable) { | 
					 | 
				
			||||||
            gas_set_0 = value_0; | 
					 | 
				
			||||||
            gas_set_1 = value_1; | 
					 | 
				
			||||||
          } else { | 
					 | 
				
			||||||
            // clear the fault state if values are 0
 | 
					 | 
				
			||||||
            if (value_0 == 0 && value_1 == 0) { | 
					 | 
				
			||||||
              state = NO_FAULT; | 
					 | 
				
			||||||
            } else { | 
					 | 
				
			||||||
              state = FAULT_INVALID; | 
					 | 
				
			||||||
            } | 
					 | 
				
			||||||
            gas_set_0 = gas_set_1 = 0; | 
					 | 
				
			||||||
          } | 
					 | 
				
			||||||
          // clear the timeout
 | 
					 | 
				
			||||||
          timeout = 0; | 
					 | 
				
			||||||
        } | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      } else { | 
					 | 
				
			||||||
        // wrong checksum = fault
 | 
					 | 
				
			||||||
        state = FAULT_BAD_CHECKSUM; | 
					 | 
				
			||||||
      } | 
					 | 
				
			||||||
    } | 
					 | 
				
			||||||
    // next
 | 
					 | 
				
			||||||
    CAN->RF0R |= CAN_RF0R_RFOM0; | 
					 | 
				
			||||||
  } | 
					 | 
				
			||||||
} | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void CAN1_SCE_IRQHandler() { | 
					 | 
				
			||||||
  state = FAULT_SCE; | 
					 | 
				
			||||||
  can_sce(CAN); | 
					 | 
				
			||||||
} | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int pdl0 = 0, pdl1 = 0; | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int led_value = 0; | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void TIM3_IRQHandler() { | 
					 | 
				
			||||||
  #ifdef DEBUG | 
					 | 
				
			||||||
    puth(TIM3->CNT); | 
					 | 
				
			||||||
    puts(" "); | 
					 | 
				
			||||||
    puth(pdl0); | 
					 | 
				
			||||||
    puts(" "); | 
					 | 
				
			||||||
    puth(pdl1); | 
					 | 
				
			||||||
    puts("\n"); | 
					 | 
				
			||||||
  #endif | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // check timer for sending the user pedal and clearing the CAN
 | 
					 | 
				
			||||||
  if ((CAN->TSR & CAN_TSR_TME0) == CAN_TSR_TME0) { | 
					 | 
				
			||||||
    uint8_t dat[8]; | 
					 | 
				
			||||||
    dat[0] = (pdl0>>8)&0xFF; | 
					 | 
				
			||||||
    dat[1] = (pdl0>>0)&0xFF; | 
					 | 
				
			||||||
    dat[2] = (pdl1>>8)&0xFF; | 
					 | 
				
			||||||
    dat[3] = (pdl1>>0)&0xFF; | 
					 | 
				
			||||||
    dat[4] = state; | 
					 | 
				
			||||||
    dat[5] = can_cksum(dat, 5, CAN_GAS_OUTPUT); | 
					 | 
				
			||||||
    CAN->sTxMailBox[0].TDLR = dat[0] | (dat[1]<<8) | (dat[2]<<16) | (dat[3]<<24); | 
					 | 
				
			||||||
    CAN->sTxMailBox[0].TDHR = dat[4] | (dat[5]<<8); | 
					 | 
				
			||||||
    CAN->sTxMailBox[0].TDTR = 6;  // len of packet is 5
 | 
					 | 
				
			||||||
    CAN->sTxMailBox[0].TIR = (CAN_GAS_OUTPUT << 21) | 1; | 
					 | 
				
			||||||
  } else { | 
					 | 
				
			||||||
    // old can packet hasn't sent!
 | 
					 | 
				
			||||||
    state = FAULT_SEND; | 
					 | 
				
			||||||
    #ifdef DEBUG | 
					 | 
				
			||||||
      puts("CAN MISS\n"); | 
					 | 
				
			||||||
    #endif | 
					 | 
				
			||||||
  } | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // blink the LED
 | 
					 | 
				
			||||||
  set_led(LED_GREEN, led_value); | 
					 | 
				
			||||||
  led_value = !led_value; | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  TIM3->SR = 0; | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // up timeout for gas set
 | 
					 | 
				
			||||||
  if (timeout == MAX_TIMEOUT) { | 
					 | 
				
			||||||
    state = FAULT_TIMEOUT; | 
					 | 
				
			||||||
  } else { | 
					 | 
				
			||||||
    timeout += 1; | 
					 | 
				
			||||||
  } | 
					 | 
				
			||||||
} | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ***************************** main code *****************************
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void pedal() { | 
					 | 
				
			||||||
  // read/write
 | 
					 | 
				
			||||||
  pdl0 = adc_get(ADCCHAN_ACCEL0); | 
					 | 
				
			||||||
  pdl1 = adc_get(ADCCHAN_ACCEL1); | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // write the pedal to the DAC
 | 
					 | 
				
			||||||
  if (state == NO_FAULT) { | 
					 | 
				
			||||||
    dac_set(0, max(gas_set_0, pdl0)); | 
					 | 
				
			||||||
    dac_set(1, max(gas_set_1, pdl1)); | 
					 | 
				
			||||||
  } else { | 
					 | 
				
			||||||
    dac_set(0, pdl0); | 
					 | 
				
			||||||
    dac_set(1, pdl1); | 
					 | 
				
			||||||
  } | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // feed the watchdog
 | 
					 | 
				
			||||||
  IWDG->KR = 0xAAAA; | 
					 | 
				
			||||||
} | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int main() { | 
					 | 
				
			||||||
  __disable_irq(); | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // init devices
 | 
					 | 
				
			||||||
  clock_init(); | 
					 | 
				
			||||||
  periph_init(); | 
					 | 
				
			||||||
  gpio_init(); | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef PEDAL_USB | 
					 | 
				
			||||||
  // enable USB
 | 
					 | 
				
			||||||
  usb_init(); | 
					 | 
				
			||||||
#endif | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // pedal stuff
 | 
					 | 
				
			||||||
  dac_init(); | 
					 | 
				
			||||||
  adc_init(); | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // init can
 | 
					 | 
				
			||||||
  can_silent = ALL_CAN_LIVE; | 
					 | 
				
			||||||
  can_init(0); | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // 48mhz / 65536 ~= 732
 | 
					 | 
				
			||||||
  timer_init(TIM3, 15); | 
					 | 
				
			||||||
  NVIC_EnableIRQ(TIM3_IRQn); | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // setup watchdog
 | 
					 | 
				
			||||||
  IWDG->KR = 0x5555; | 
					 | 
				
			||||||
  IWDG->PR = 0;          // divider /4
 | 
					 | 
				
			||||||
  // 0 = 0.125 ms, let's have a 50ms watchdog
 | 
					 | 
				
			||||||
  IWDG->RLR = 400 - 1; | 
					 | 
				
			||||||
  IWDG->KR = 0xCCCC; | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  puts("**** INTERRUPTS ON ****\n"); | 
					 | 
				
			||||||
  __enable_irq(); | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // main pedal loop
 | 
					 | 
				
			||||||
  while (1) { | 
					 | 
				
			||||||
    pedal(); | 
					 | 
				
			||||||
  } | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return 0; | 
					 | 
				
			||||||
} | 
					 | 
				
			||||||
@ -0,0 +1,170 @@ | 
				
			|||||||
 | 
					#!/usr/bin/env python2 | 
				
			||||||
 | 
					import unittest | 
				
			||||||
 | 
					import numpy as np | 
				
			||||||
 | 
					import libpandasafety_py | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MAX_RATE_UP = 50 | 
				
			||||||
 | 
					MAX_RATE_DOWN = 70 | 
				
			||||||
 | 
					MAX_STEER = 2047 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MAX_RT_DELTA = 940 | 
				
			||||||
 | 
					RT_INTERVAL = 250000 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DRIVER_TORQUE_ALLOWANCE = 60; | 
				
			||||||
 | 
					DRIVER_TORQUE_FACTOR = 10; | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def twos_comp(val, bits): | 
				
			||||||
 | 
					  if val >= 0: | 
				
			||||||
 | 
					    return val | 
				
			||||||
 | 
					  else: | 
				
			||||||
 | 
					    return (2**bits) + val | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def sign(a): | 
				
			||||||
 | 
					  if a > 0: | 
				
			||||||
 | 
					    return 1 | 
				
			||||||
 | 
					  else: | 
				
			||||||
 | 
					    return -1 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestSubaruSafety(unittest.TestCase): | 
				
			||||||
 | 
					  @classmethod | 
				
			||||||
 | 
					  def setUp(cls): | 
				
			||||||
 | 
					    cls.safety = libpandasafety_py.libpandasafety | 
				
			||||||
 | 
					    cls.safety.nooutput_init(0) | 
				
			||||||
 | 
					    cls.safety.init_tests_subaru() | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def _set_prev_torque(self, t): | 
				
			||||||
 | 
					    self.safety.set_subaru_desired_torque_last(t) | 
				
			||||||
 | 
					    self.safety.set_subaru_rt_torque_last(t) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def _torque_driver_msg(self, torque): | 
				
			||||||
 | 
					    to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') | 
				
			||||||
 | 
					    to_send[0].RIR = 0x119 << 21 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    t = twos_comp(torque, 11) | 
				
			||||||
 | 
					    to_send[0].RDLR = ((t & 0x7FF) << 16) | 
				
			||||||
 | 
					    return to_send | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def _torque_msg(self, torque): | 
				
			||||||
 | 
					    to_send = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') | 
				
			||||||
 | 
					    to_send[0].RIR = 0x122 << 21 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    t = twos_comp(torque, 13) | 
				
			||||||
 | 
					    to_send[0].RDLR = (t << 16) | 
				
			||||||
 | 
					    return to_send | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def test_default_controls_not_allowed(self): | 
				
			||||||
 | 
					    self.assertFalse(self.safety.get_controls_allowed()) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def test_enable_control_allowed_from_cruise(self): | 
				
			||||||
 | 
					    to_push = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') | 
				
			||||||
 | 
					    to_push[0].RIR = 0x240 << 21 | 
				
			||||||
 | 
					    to_push[0].RDHR = 1 << 9 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    self.safety.subaru_rx_hook(to_push) | 
				
			||||||
 | 
					    self.assertTrue(self.safety.get_controls_allowed()) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def test_disable_control_allowed_from_cruise(self): | 
				
			||||||
 | 
					    to_push = libpandasafety_py.ffi.new('CAN_FIFOMailBox_TypeDef *') | 
				
			||||||
 | 
					    to_push[0].RIR = 0x240 << 21 | 
				
			||||||
 | 
					    to_push[0].RDHR = 0 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    self.safety.set_controls_allowed(1) | 
				
			||||||
 | 
					    self.safety.subaru_rx_hook(to_push) | 
				
			||||||
 | 
					    self.assertFalse(self.safety.get_controls_allowed()) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def test_steer_safety_check(self): | 
				
			||||||
 | 
					    for enabled in [0, 1]: | 
				
			||||||
 | 
					      for t in range(-3000, 3000): | 
				
			||||||
 | 
					        self.safety.set_controls_allowed(enabled) | 
				
			||||||
 | 
					        self._set_prev_torque(t) | 
				
			||||||
 | 
					        if abs(t) > MAX_STEER or (not enabled and abs(t) > 0): | 
				
			||||||
 | 
					          self.assertFalse(self.safety.subaru_tx_hook(self._torque_msg(t))) | 
				
			||||||
 | 
					        else: | 
				
			||||||
 | 
					          self.assertTrue(self.safety.subaru_tx_hook(self._torque_msg(t))) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def test_manually_enable_controls_allowed(self): | 
				
			||||||
 | 
					    self.safety.set_controls_allowed(1) | 
				
			||||||
 | 
					    self.assertTrue(self.safety.get_controls_allowed()) | 
				
			||||||
 | 
					    self.safety.set_controls_allowed(0) | 
				
			||||||
 | 
					    self.assertFalse(self.safety.get_controls_allowed()) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def test_non_realtime_limit_up(self): | 
				
			||||||
 | 
					    self.safety.set_subaru_torque_driver(0, 0) | 
				
			||||||
 | 
					    self.safety.set_controls_allowed(True) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    self._set_prev_torque(0) | 
				
			||||||
 | 
					    self.assertTrue(self.safety.subaru_tx_hook(self._torque_msg(MAX_RATE_UP))) | 
				
			||||||
 | 
					    self._set_prev_torque(0) | 
				
			||||||
 | 
					    self.assertTrue(self.safety.subaru_tx_hook(self._torque_msg(-MAX_RATE_UP))) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    self._set_prev_torque(0) | 
				
			||||||
 | 
					    self.assertFalse(self.safety.subaru_tx_hook(self._torque_msg(MAX_RATE_UP + 1))) | 
				
			||||||
 | 
					    self.safety.set_controls_allowed(True) | 
				
			||||||
 | 
					    self._set_prev_torque(0) | 
				
			||||||
 | 
					    self.assertFalse(self.safety.subaru_tx_hook(self._torque_msg(-MAX_RATE_UP - 1))) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def test_non_realtime_limit_down(self): | 
				
			||||||
 | 
					    self.safety.set_subaru_torque_driver(0, 0) | 
				
			||||||
 | 
					    self.safety.set_controls_allowed(True) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def test_against_torque_driver(self): | 
				
			||||||
 | 
					    self.safety.set_controls_allowed(True) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for sign in [-1, 1]: | 
				
			||||||
 | 
					      for t in np.arange(0, DRIVER_TORQUE_ALLOWANCE + 1, 1): | 
				
			||||||
 | 
					        t *= -sign | 
				
			||||||
 | 
					        self.safety.set_subaru_torque_driver(t, t) | 
				
			||||||
 | 
					        self._set_prev_torque(MAX_STEER * sign) | 
				
			||||||
 | 
					        self.assertTrue(self.safety.subaru_tx_hook(self._torque_msg(MAX_STEER * sign))) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      self.safety.set_subaru_torque_driver(DRIVER_TORQUE_ALLOWANCE + 1, DRIVER_TORQUE_ALLOWANCE + 1) | 
				
			||||||
 | 
					      self.assertFalse(self.safety.subaru_tx_hook(self._torque_msg(-MAX_STEER))) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # spot check some individual cases | 
				
			||||||
 | 
					    for sign in [-1, 1]: | 
				
			||||||
 | 
					      driver_torque = (DRIVER_TORQUE_ALLOWANCE + 10) * sign | 
				
			||||||
 | 
					      torque_desired = (MAX_STEER - 10 * DRIVER_TORQUE_FACTOR) * sign | 
				
			||||||
 | 
					      delta = 1 * sign | 
				
			||||||
 | 
					      self._set_prev_torque(torque_desired) | 
				
			||||||
 | 
					      self.safety.set_subaru_torque_driver(-driver_torque, -driver_torque) | 
				
			||||||
 | 
					      self.assertTrue(self.safety.subaru_tx_hook(self._torque_msg(torque_desired))) | 
				
			||||||
 | 
					      self._set_prev_torque(torque_desired + delta) | 
				
			||||||
 | 
					      self.safety.set_subaru_torque_driver(-driver_torque, -driver_torque) | 
				
			||||||
 | 
					      self.assertFalse(self.safety.subaru_tx_hook(self._torque_msg(torque_desired + delta))) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      self._set_prev_torque(MAX_STEER * sign) | 
				
			||||||
 | 
					      self.safety.set_subaru_torque_driver(-MAX_STEER * sign, -MAX_STEER * sign) | 
				
			||||||
 | 
					      self.assertTrue(self.safety.subaru_tx_hook(self._torque_msg((MAX_STEER - MAX_RATE_DOWN) * sign))) | 
				
			||||||
 | 
					      self._set_prev_torque(MAX_STEER * sign) | 
				
			||||||
 | 
					      self.safety.set_subaru_torque_driver(-MAX_STEER * sign, -MAX_STEER * sign) | 
				
			||||||
 | 
					      self.assertTrue(self.safety.subaru_tx_hook(self._torque_msg(0))) | 
				
			||||||
 | 
					      self._set_prev_torque(MAX_STEER * sign) | 
				
			||||||
 | 
					      self.safety.set_subaru_torque_driver(-MAX_STEER * sign, -MAX_STEER * sign) | 
				
			||||||
 | 
					      self.assertFalse(self.safety.subaru_tx_hook(self._torque_msg((MAX_STEER - MAX_RATE_DOWN + 1) * sign))) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def test_realtime_limits(self): | 
				
			||||||
 | 
					    self.safety.set_controls_allowed(True) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for sign in [-1, 1]: | 
				
			||||||
 | 
					      self.safety.init_tests_subaru() | 
				
			||||||
 | 
					      self._set_prev_torque(0) | 
				
			||||||
 | 
					      self.safety.set_subaru_torque_driver(0, 0) | 
				
			||||||
 | 
					      for t in np.arange(0, MAX_RT_DELTA, 1): | 
				
			||||||
 | 
					        t *= sign | 
				
			||||||
 | 
					        self.assertTrue(self.safety.subaru_tx_hook(self._torque_msg(t))) | 
				
			||||||
 | 
					      self.assertFalse(self.safety.subaru_tx_hook(self._torque_msg(sign * (MAX_RT_DELTA + 1)))) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      self._set_prev_torque(0) | 
				
			||||||
 | 
					      for t in np.arange(0, MAX_RT_DELTA, 1): | 
				
			||||||
 | 
					        t *= sign | 
				
			||||||
 | 
					        self.assertTrue(self.safety.subaru_tx_hook(self._torque_msg(t))) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      # Increase timer to update rt_torque_last | 
				
			||||||
 | 
					      self.safety.set_timer(RT_INTERVAL + 1) | 
				
			||||||
 | 
					      self.assertTrue(self.safety.subaru_tx_hook(self._torque_msg(sign * (MAX_RT_DELTA - 1)))) | 
				
			||||||
 | 
					      self.assertTrue(self.safety.subaru_tx_hook(self._torque_msg(sign * (MAX_RT_DELTA + 1)))) | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == "__main__": | 
				
			||||||
 | 
					  unittest.main() | 
				
			||||||
@ -0,0 +1,21 @@ | 
				
			|||||||
 | 
					#!/usr/bin/env python2 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# This trims CAN message CSV files to just the messages relevant for Panda testing. | 
				
			||||||
 | 
					# Usage: | 
				
			||||||
 | 
					# cat input.csv | ./trim_csv.py > output.csv | 
				
			||||||
 | 
					import fileinput | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					addr_to_keep = [544, 0x1f4, 0x292]  # For Chrysler, update to the addresses that matter for you. | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					for line in fileinput.input(): | 
				
			||||||
 | 
					  line = line.strip() | 
				
			||||||
 | 
					  cols = line.split(',') | 
				
			||||||
 | 
					  if len(cols) != 4: | 
				
			||||||
 | 
					    continue  # malformed, such as at the end or every 60s. | 
				
			||||||
 | 
					  (_, addr, bus, _) = cols | 
				
			||||||
 | 
					  if (addr == 'addr'): | 
				
			||||||
 | 
					    continue | 
				
			||||||
 | 
					  if (int(bus) == 128):  # Keep all messages sent by OpenPilot. | 
				
			||||||
 | 
					    print line | 
				
			||||||
 | 
					  elif (int(addr) in addr_to_keep): | 
				
			||||||
 | 
					    print line | 
				
			||||||
					Loading…
					
					
				
		Reference in new issue