/* * Copyright © Matthew Wozniak * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include "intdefs.h" #include "stm32f1xx.h" #define DOER /* Our CAN recv queue */ #define CAN_FRAME_QUEUE_DEPTH 128 struct { int front; int back; CAN_FIFOMailBox_TypeDef data[CAN_FRAME_QUEUE_DEPTH]; } can_frame_queue = {}; /* * Retrieves the oldest current frame from our CAN received frame FIFO queue. * Returns false if we have read all received frames, otherwise returns true. * * The CAN filters should be set up in a way such that this function is called * at least as often as the hardware receives a frame, as any CAN frames that * the hardware receives after our queue is full will just be dropped. */ bool can_recv(CAN_FIFOMailBox_TypeDef *frame) { if (can_frame_queue.front == can_frame_queue.back) return false; *frame = can_frame_queue.data[can_frame_queue.front]; can_frame_queue.front = (can_frame_queue.front + 1) % CAN_FRAME_QUEUE_DEPTH; return true; } // This will get called when we get a new frame in FIFO1 void canrx1_handler() { // if the queue is full just drop the frame int next_back = (can_frame_queue.back + 1) % CAN_FRAME_QUEUE_DEPTH; if (next_back == can_frame_queue.front) { CAN1->RF1R |= CAN_RF1R_RFOM1; return; } can_frame_queue.data[can_frame_queue.back] = CAN1->sFIFOMailBox[1]; can_frame_queue.back = next_back; // tell the FIFO we are done with this frame CAN1->RF1R |= CAN_RF1R_RFOM1; } /* * Sends a CAN frame over CAN1. If id is longer than 11 bits, sends it with the * extended format. Otherwise uses the standard format. * * Returns false if there is no available mailbox (couldn't send the frame) */ bool can_send_frame(u32 id, u8 data[8], int len) { // find the first open mailbox for (int i = 0; i < 3; i++) { // is this mailbox empty? if (CAN1->sTxMailBox[i].TIR & CAN_TI0R_TXRQ_Msk) continue; // it is, we will put our new message here CAN_TxMailBox_TypeDef *mb = CAN1->sTxMailBox + i; // set data length mb->TDTR &= ~CAN_TDT0R_DLC_Msk; mb->TDTR |= (len & 0xf) << CAN_TDT0R_DLC_Pos; // set data mb->TDHR = data[7] << 24 | data[6] << 16 | data[5] << 8 | data[4]; mb->TDLR = data[3] << 24 | data[2] << 16 | data[1] << 8 | data[0]; // set CAN id int tir = 0; if (id > 0x7ff) { tir |= id << CAN_TI0R_EXID_Pos; tir |= CAN_TI0R_IDE; } else { tir |= id << CAN_TI0R_STID_Pos; } // send it off mb->TIR |= tir | CAN_TI0R_TXRQ; return true; } // no empty mailbox! return false; } volatile u32 ticks = 0; void systick_handler() { ticks++; } void delay_ms(u32 ms) { u32 start = ticks; u32 end = ticks + ms; while (ticks < end); } int main() { // enable the HSE RCC->CR |= RCC_CR_HSEON; while (!(RCC->CR & RCC_CR_HSERDY_Msk)); // configure PLL to use HSE clock RCC->CFGR &= ~RCC_CFGR_PLLSRC_Msk; RCC->CFGR |= 1 << RCC_CFGR_PLLSRC_Pos; // configure PLL to 9x clock scaling (8MHz * 9 = 72MHz) RCC->CFGR &= ~RCC_CFGR_PLLMULL_Msk; RCC->CFGR |= RCC_CFGR_PLLMULL9_Msk; // enable the PLL RCC->CR |= RCC_CR_PLLON; while (!(RCC->CR & RCC_CR_PLLRDY)); // divide ABP1 by 2 (max frequency is 36MHz) RCC->CFGR &= ~RCC_CFGR_PPRE1_Msk; RCC->CFGR |= RCC_CFGR_PPRE1_DIV2; // 2 waitstate for 72 MHz clock FLASH->ACR |= FLASH_ACR_LATENCY_2; // finally, set SYSCLK to use PLL output (72MHz) RCC->CFGR &= ~RCC_CFGR_SW_Msk; RCC->CFGR |= RCC_CFGR_SW_PLL; // 72M cycles/sec / 1000 cycles = 0.001 // 1 ms SysTick interrupt timer SystemCoreClock = 72000000; SysTick_Config(SystemCoreClock / 1000); __enable_irq(); // GPIO ports C and A and AFIO RCC->APB2ENR |= RCC_APB2ENR_IOPCEN | RCC_APB2ENR_IOPAEN | RCC_APB2ENR_AFIOEN; // CAN1 RCC->APB1ENR |= RCC_APB1ENR_CAN1EN; // do two dummy reads after enabling the peripheral clocks, // as per the errata volatile u32 dummy; dummy = RCC->APB2ENR; dummy = RCC->APB2ENR; dummy = RCC->APB1ENR; dummy = RCC->APB1ENR; // Push pull output GPIOC->CRH |= 1 << GPIO_CRH_MODE13_Pos | 0 << GPIO_CRH_CNF13_Pos; // CAN1 remap 0 (PA11 and PA12) AFIO->MAPR &= ~AFIO_MAPR_CAN_REMAP_Msk; // Pin 11 will be initialized to an input // Set GPIO A pin 12 to be the alternate function GPIOA->CRH |= 3 /* 50MHz output */ << GPIO_CRH_MODE12_Pos | 2 /* Alternate function push-pull */ << GPIO_CRH_CNF12_Pos; // Set to init mode CAN1->MCR |= CAN_MCR_INRQ; while (!(CAN1->MSR & CAN_MSR_INAK)); // No silent mode CAN1->BTR &= ~CAN_BTR_SILM; // Enable loopback for now CAN1->BTR |= CAN_BTR_LBKM; // Set it to some value CAN1->BTR &= ~CAN_BTR_SJW_Msk; CAN1->BTR |= CAN_BTR_SJW_1; /* http://www.bittiming.can-wiki.info * we want the bitrate to be 1,000,000 * * One bit: * sync (1 tq) * time seg 1 (15 tq) * time seg 2 (2 tq) * = total 18 time quanta * * So to make the bitrate 1 Mbps we must set the CAN clock prescaler to * send 18 tq per 1 / 1,000,000 * Since it sends 1 tq for clock cycle we will set it to 18 MHz * So scale down 36 MHz by 2. So we set the BRP to 1 because it adds 1 for * us. */ CAN1->BTR &= ~(CAN_BTR_BRP_Msk | CAN_BTR_TS1_Msk | CAN_BTR_TS2_Msk); CAN1->BTR |= 15 << CAN_BTR_TS1_Pos | 2 << CAN_BTR_TS2_Pos | 7 << CAN_BTR_BRP_Pos; // Leave sleep (synchronize on the bus) // Keep CAN working while being debugged // Automatically wakeup when we see something // Recover from any bus off events // Don't resend messages if they aren't acknowledged CAN1->MCR &= ~(CAN_MCR_SLEEP | CAN_MCR_DBF | CAN_MCR_RESET | CAN_MCR_AWUM | CAN_MCR_ABOM | CAN_MCR_NART); // Put every received frame in FIFO1 for now CAN1->FMR |= CAN_FMR_FINIT; // Single 32 bit mode CAN1->FS1R |= 1 << CAN_FS1R_FSC0_Pos; // Set to mask mode (should already be unset but just make sure) CAN1->FM1R &= ~(1 << CAN_FM1R_FBM0_Pos); // Send to FIFO1 CAN1->FFA1R |= 1 << CAN_FFA1R_FFA0_Pos; // Set filter 0 to active CAN1->FA1R |= 1 << CAN_FA1R_FACT0_Pos; // Match everything CAN1->sFilterRegister[0].FR1 = 0; CAN1->sFilterRegister[0].FR2 = 0; // Activate filters CAN1->FMR &= ~CAN_FMR_FINIT; // Interrupt on CAN frame receive so we can move the frame to our own queue CAN1->IER |= CAN_IER_FMPIE1; NVIC_SetPriority(CAN1_RX1_IRQn, 10); NVIC_EnableIRQ(CAN1_RX1_IRQn); // exit init mode CAN1->MCR &= ~CAN_MCR_INRQ; while (CAN1->MSR & CAN_MSR_INAK); while (1) { #ifdef TELLER u8 test_frame_data[8] = { 0, 1, 2, 3, 4, 5, 6, 7 }; GPIOC->ODR ^= (1 << 13); if (!can_send_frame(1, test_frame_data, 8)) { for (int i = 0; i < 10; i++) { GPIOC->ODR ^= (1 << 13); delay_ms(50); } } delay_ms(100); #endif #ifdef DOER CAN_FIFOMailBox_TypeDef frame; if (can_recv(&frame)) { GPIOC->ODR ^= 1 << 13; } delay_ms(10); #endif } } // vi: sw=4 ts=4 noet tw=80 cc=80