diff options
| author | 2025-12-11 23:58:51 -0500 | |
|---|---|---|
| committer | 2025-12-11 23:58:51 -0500 | |
| commit | 5356bc886d53291a9a812f2aba35e4e165d96fba (patch) | |
| tree | 55fbe7e4f3cb54a5b8c43b40aba4771dc0465d8a /main.c | |
| download | stm32_can_demo-5356bc886d53291a9a812f2aba35e4e165d96fba.tar.gz stm32_can_demo-5356bc886d53291a9a812f2aba35e4e165d96fba.zip | |
initial commit
Diffstat (limited to 'main.c')
| -rw-r--r-- | main.c | 246 |
1 files changed, 246 insertions, 0 deletions
@@ -0,0 +1,246 @@ +/* + * Copyright © Matthew Wozniak <me@woz.blue> + * + * 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_frames = {}; + +bool can_recv(CAN_FIFOMailBox_TypeDef *frame) { + if (can_frames.front == can_frames.back) return false; + *frame = can_frames.data[can_frames.front]; + can_frames.front = (can_frames.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 + if ((can_frames.back + 1) % CAN_FRAME_QUEUE_DEPTH == can_frames.front) { + CAN1->RF1R |= CAN_RF1R_RFOM1; + return; + } + can_frames.data[can_frames.back] = CAN1->sFIFOMailBox[1]; + can_frames.back = (can_frames.back + 1) % CAN_FRAME_QUEUE_DEPTH; + // 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 |
