1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
|
/*
* 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_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
if ((can_frame_queue.back + 1) % CAN_FRAME_QUEUE_DEPTH == 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 = (can_frame_queue.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
|