summaryrefslogtreecommitdiff
path: root/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'main.c')
-rw-r--r--main.c246
1 files changed, 246 insertions, 0 deletions
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..89f96b7
--- /dev/null
+++ b/main.c
@@ -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