Skip to content

Commit 0775e2b

Browse files
authored
Merge pull request #3505 from jepler/canbus-stm
canio: implement for stm32f405
2 parents 8c428a3 + 16ed875 commit 0775e2b

File tree

13 files changed

+799
-4
lines changed

13 files changed

+799
-4
lines changed

ports/atmel-samd/common-hal/canio/CAN.h

-3
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,6 @@ typedef struct canio_can_obj {
4141
mp_obj_base_t base;
4242
Can *hw;
4343
canio_can_state_t *state;
44-
volatile uint32_t error_warning_state_count;
45-
volatile uint32_t error_passive_state_count;
46-
volatile uint32_t bus_off_state_count;
4744
int baudrate;
4845
uint8_t rx_pin_number:8;
4946
uint8_t tx_pin_number:8;

ports/stm/Makefile

+4
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,10 @@ SRC_STM32 = $(addprefix $(HAL_DIR)/Src/stm32$(MCU_SERIES_LOWER)xx_,\
198198
ll_utils.c \
199199
)
200200

201+
ifeq ($(CIRCUITPY_CANIO),1)
202+
SRC_STM32 += $(HAL_DIR)/Src/stm32$(MCU_SERIES_LOWER)xx_hal_can.c
203+
endif
204+
201205
# Need this to avoid UART linker problems. TODO: rewrite to use registered callbacks.
202206
# Does not exist for F4 and lower
203207
ifeq ($(MCU_VARIANT),$(filter $(MCU_VARIANT),STM32F765xx STM32F767xx STM32F769xx STM32H743xx))

ports/stm/boards/feather_stm32f405_express/pins.c

+3
Original file line numberDiff line numberDiff line change
@@ -47,5 +47,8 @@ STATIC const mp_rom_map_elem_t board_module_globals_table[] = {
4747
{ MP_ROM_QSTR(MP_QSTR_SDIO_CLOCK), MP_ROM_PTR(&pin_PC12) },
4848
{ MP_ROM_QSTR(MP_QSTR_SDIO_COMMAND), MP_ROM_PTR(&pin_PD02) },
4949
{ MP_ROM_QSTR(MP_QSTR_SDIO_DATA), MP_ROM_PTR(&sdio_data_tuple) },
50+
51+
{ MP_OBJ_NEW_QSTR(MP_QSTR_CAN_RX), MP_ROM_PTR(&pin_PB09) },
52+
{ MP_OBJ_NEW_QSTR(MP_QSTR_CAN_TX), MP_ROM_PTR(&pin_PB08) },
5053
};
5154
MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table);

ports/stm/common-hal/canio/CAN.c

+297
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2020 Jeff Epler for Adafruit Industries
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*/
26+
27+
#include <string.h>
28+
29+
#include "py/runtime.h"
30+
#include "py/mperrno.h"
31+
32+
#include "common-hal/canio/CAN.h"
33+
#include "peripherals/periph.h"
34+
#include "shared-bindings/microcontroller/Pin.h"
35+
#include "shared-bindings/util.h"
36+
#include "supervisor/port.h"
37+
38+
STATIC bool reserved_can[MP_ARRAY_SIZE(mcu_can_banks)];
39+
40+
STATIC const mcu_periph_obj_t *find_pin_function(const mcu_periph_obj_t *table, size_t sz, const mcu_pin_obj_t *pin, int periph_index) {
41+
for(size_t i = 0; i<sz; i++, table++) {
42+
if (periph_index != -1 && periph_index != table->periph_index) {
43+
continue;
44+
}
45+
if (pin == table->pin) {
46+
return table;
47+
}
48+
}
49+
return NULL;
50+
}
51+
52+
53+
void common_hal_canio_can_construct(canio_can_obj_t *self, mcu_pin_obj_t *tx, mcu_pin_obj_t *rx, int baudrate, bool loopback, bool silent)
54+
{
55+
#define DIV_ROUND(a, b) (((a) + (b)/2) / (b))
56+
#define DIV_ROUND_UP(a, b) (((a) + (b) - 1) / (b))
57+
58+
const uint8_t can_tx_len = MP_ARRAY_SIZE(mcu_can_tx_list);
59+
const uint8_t can_rx_len = MP_ARRAY_SIZE(mcu_can_rx_list);
60+
61+
const mcu_periph_obj_t *mcu_tx = find_pin_function(mcu_can_tx_list, can_tx_len, tx, -1);
62+
if (!mcu_tx) {
63+
mp_raise_ValueError_varg(translate("Invalid %q pin selection"), MP_QSTR_tx);
64+
}
65+
int periph_index = mcu_tx->periph_index;
66+
67+
const mcu_periph_obj_t *mcu_rx = find_pin_function(mcu_can_rx_list, can_rx_len, rx, periph_index);
68+
if (!mcu_rx) {
69+
mp_raise_ValueError_varg(translate("Invalid %q pin selection"), MP_QSTR_rx);
70+
}
71+
72+
if (reserved_can[periph_index]) {
73+
mp_raise_ValueError(translate("Hardware busy, try alternative pins"));
74+
}
75+
76+
const uint32_t can_frequency = 42000000;
77+
uint32_t clocks_per_bit = DIV_ROUND(can_frequency, baudrate);
78+
uint32_t clocks_to_sample = DIV_ROUND(clocks_per_bit * 7, 8);
79+
uint32_t clocks_after_sample = clocks_per_bit - clocks_to_sample;
80+
uint32_t divisor = MAX(DIV_ROUND_UP(clocks_to_sample, 16), DIV_ROUND_UP(clocks_after_sample, 8));
81+
const uint32_t sjw = 3;
82+
83+
uint32_t tq_per_bit = DIV_ROUND(clocks_per_bit, divisor);
84+
uint32_t tq_to_sample = DIV_ROUND(clocks_to_sample, divisor);
85+
uint32_t tq_after_sample = tq_per_bit - tq_to_sample;
86+
87+
if (divisor > 1023) {
88+
mp_raise_OSError(MP_EINVAL); // baudrate cannot be attained (16kHz or something is lower bound, should never happen)
89+
}
90+
91+
{
92+
GPIO_InitTypeDef GPIO_InitStruct = {
93+
.Pin = pin_mask(tx->number),
94+
.Speed = GPIO_SPEED_FREQ_VERY_HIGH,
95+
.Mode = GPIO_MODE_AF_PP,
96+
.Pull = GPIO_PULLUP,
97+
.Alternate = mcu_tx->altfn_index,
98+
};
99+
HAL_GPIO_Init(pin_port(tx->port), &GPIO_InitStruct);
100+
101+
GPIO_InitStruct.Pin = pin_mask(rx->number);
102+
GPIO_InitStruct.Alternate = mcu_rx->altfn_index;
103+
HAL_GPIO_Init(pin_port(rx->port), &GPIO_InitStruct);
104+
}
105+
106+
CAN_TypeDef *hw = mcu_can_banks[periph_index - 1];
107+
108+
// CAN2 shares resources with CAN1. So we always enable CAN1, then split
109+
// the filter banks equally between them.
110+
111+
__HAL_RCC_CAN1_CLK_ENABLE();
112+
113+
if(hw == CAN2) {
114+
__HAL_RCC_CAN2_CLK_ENABLE();
115+
self->start_filter_bank = 14;
116+
self->end_filter_bank = 28;
117+
self->filter_hw = CAN1;
118+
} else {
119+
self->start_filter_bank = 0;
120+
self->end_filter_bank = 14;
121+
self->filter_hw = hw;
122+
}
123+
124+
CAN_InitTypeDef init = {
125+
.AutoRetransmission = ENABLE,
126+
.AutoBusOff = ENABLE,
127+
.Prescaler = divisor,
128+
.Mode = (loopback ? CAN_MODE_LOOPBACK : 0) | (silent ? CAN_MODE_SILENT_LOOPBACK : 0),
129+
.SyncJumpWidth = (sjw-1) << CAN_BTR_SJW_Pos,
130+
.TimeSeg1 = (tq_to_sample-2) << CAN_BTR_TS1_Pos,
131+
.TimeSeg2 = (tq_after_sample-1) << CAN_BTR_TS2_Pos,
132+
};
133+
134+
self->periph_index = periph_index;
135+
self->silent = silent;
136+
self->loopback = loopback;
137+
self->baudrate = baudrate;
138+
139+
self->handle.Instance = hw;
140+
self->handle.Init = init;
141+
self->handle.State = HAL_CAN_STATE_RESET;
142+
143+
HAL_CAN_Init(&self->handle);
144+
145+
// Set the filter split as 14:14
146+
// COULDDO(@jepler): Dynamically allocate filter banks between CAN1/2
147+
self->filter_hw->FMR |= CAN_FMR_FINIT;
148+
self->filter_hw->FMR = CAN_FMR_FINIT | (14 << CAN_FMR_CAN2SB_Pos);
149+
150+
// Clear every filter enable bit for this can HW
151+
uint32_t fa1r = self->filter_hw->FA1R;
152+
for (int i = self->start_filter_bank; i<self->end_filter_bank; i++) {
153+
fa1r &= ~(1 << i);
154+
}
155+
self->filter_hw->FA1R = fa1r;
156+
CLEAR_BIT(self->filter_hw->FMR, CAN_FMR_FINIT);
157+
158+
HAL_CAN_Start(&self->handle);
159+
160+
reserved_can[periph_index] = true;
161+
}
162+
163+
bool common_hal_canio_can_loopback_get(canio_can_obj_t *self)
164+
{
165+
return self->loopback;
166+
}
167+
168+
int common_hal_canio_can_baudrate_get(canio_can_obj_t *self)
169+
{
170+
return self->baudrate;
171+
}
172+
173+
int common_hal_canio_can_transmit_error_count_get(canio_can_obj_t *self)
174+
{
175+
return (self->handle.Instance->ESR & CAN_ESR_TEC) >> CAN_ESR_TEC_Pos;
176+
}
177+
178+
int common_hal_canio_can_receive_error_count_get(canio_can_obj_t *self)
179+
{
180+
return (self->handle.Instance->ESR & CAN_ESR_REC) >> CAN_ESR_REC_Pos;
181+
}
182+
183+
canio_bus_state_t common_hal_canio_can_state_get(canio_can_obj_t *self) {
184+
uint32_t esr = self->handle.Instance->ESR;
185+
if (READ_BIT(esr, CAN_ESR_BOFF)) {
186+
return BUS_STATE_OFF;
187+
}
188+
if (READ_BIT(esr, CAN_ESR_EPVF)) {
189+
return BUS_STATE_ERROR_PASSIVE;
190+
}
191+
if (READ_BIT(esr, CAN_ESR_EWGF)) {
192+
return BUS_STATE_ERROR_WARNING;
193+
}
194+
return BUS_STATE_ERROR_ACTIVE;
195+
}
196+
197+
void common_hal_canio_can_restart(canio_can_obj_t *self) {
198+
if (!common_hal_canio_can_auto_restart_get(self)) {
199+
// "If ABOM is cleared, the software must initiate the recovering
200+
// sequence by requesting bxCAN to enter and to leave initialization
201+
// mode."
202+
self->handle.Instance->MCR |= CAN_MCR_INRQ;
203+
while ((self->handle.Instance->MSR & CAN_MSR_INAK) == 0) {
204+
}
205+
self->handle.Instance->MCR &= ~CAN_MCR_INRQ;
206+
while ((self->handle.Instance->MSR & CAN_MSR_INAK)) {
207+
}
208+
}
209+
}
210+
211+
bool common_hal_canio_can_auto_restart_get(canio_can_obj_t *self) {
212+
return READ_BIT(self->handle.Instance->MCR, CAN_MCR_ABOM);
213+
}
214+
215+
void common_hal_canio_can_auto_restart_set(canio_can_obj_t *self, bool value) {
216+
if(value) {
217+
SET_BIT(self->handle.Instance->MCR, CAN_MCR_ABOM);
218+
} else {
219+
CLEAR_BIT(self->handle.Instance->MCR, CAN_MCR_ABOM);
220+
}
221+
}
222+
223+
void common_hal_canio_can_send(canio_can_obj_t *self, mp_obj_t message_in)
224+
{
225+
canio_message_obj_t *message = message_in;
226+
uint32_t mailbox;
227+
bool rtr = message->base.type == &canio_remote_transmission_request_type;
228+
CAN_TxHeaderTypeDef header = {
229+
.StdId = message->id,
230+
.ExtId = message->id,
231+
.IDE = message->extended ? CAN_ID_EXT : CAN_ID_STD,
232+
.RTR = rtr ? CAN_RTR_REMOTE : CAN_RTR_DATA,
233+
.DLC = message->size,
234+
};
235+
uint32_t free_level = HAL_CAN_GetTxMailboxesFreeLevel(&self->handle);
236+
if (free_level == 0) {
237+
// There's no free Tx mailbox. We need to cancel some message without
238+
// transmitting it, because once the bus returns to active state it's
239+
// preferable to transmit the newest messages instead of older messages.
240+
//
241+
// We don't strictly guarantee that we abort the oldest Tx request,
242+
// rather we just abort a different index each time. This permits us
243+
// to just track a single cancel index
244+
HAL_CAN_AbortTxRequest(&self->handle, 1 << (self->cancel_mailbox));
245+
self->cancel_mailbox = (self->cancel_mailbox + 1) % 3;
246+
// The abort request may not have completed immediately, so wait for
247+
// the Tx mailbox to become free
248+
do {
249+
free_level = HAL_CAN_GetTxMailboxesFreeLevel(&self->handle);
250+
} while (!free_level);
251+
}
252+
HAL_StatusTypeDef status = HAL_CAN_AddTxMessage(&self->handle, &header, message->data, &mailbox);
253+
if (status != HAL_OK) {
254+
// this is a "shouldn't happen" condition. we don't throw because the
255+
// contract of send() is that it queues the packet to be sent if
256+
// possible and does not signal success or failure to actually send.
257+
return;
258+
}
259+
260+
// wait 8ms (hard coded for now) for TX to occur
261+
uint64_t deadline = port_get_raw_ticks(NULL) + 8;
262+
while (port_get_raw_ticks(NULL) < deadline && HAL_CAN_IsTxMessagePending(&self->handle, 1 << mailbox)) {
263+
RUN_BACKGROUND_TASKS;
264+
}
265+
}
266+
267+
bool common_hal_canio_can_silent_get(canio_can_obj_t *self) {
268+
return self->silent;
269+
}
270+
271+
bool common_hal_canio_can_deinited(canio_can_obj_t *self) {
272+
return !self->handle.Instance;
273+
}
274+
275+
void common_hal_canio_can_check_for_deinit(canio_can_obj_t *self) {
276+
if (common_hal_canio_can_deinited(self)) {
277+
raise_deinited_error();
278+
}
279+
}
280+
281+
void common_hal_canio_can_deinit(canio_can_obj_t *self)
282+
{
283+
if (self->handle.Instance) {
284+
SET_BIT(self->handle.Instance->MCR, CAN_MCR_RESET);
285+
while (READ_BIT(self->handle.Instance->MCR, CAN_MCR_RESET)) {
286+
}
287+
reserved_can[self->periph_index] = 0;
288+
}
289+
self->handle.Instance = NULL;
290+
}
291+
292+
void common_hal_canio_reset(void) {
293+
for (size_t i=0; i<MP_ARRAY_SIZE(mcu_can_banks); i++) {
294+
SET_BIT(mcu_can_banks[i]->MCR, CAN_MCR_RESET);
295+
reserved_can[i] = 0;
296+
}
297+
}

ports/stm/common-hal/canio/CAN.h

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2020 Jeff Epler for Adafruit Industries
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*/
26+
27+
#pragma once
28+
29+
#include "py/obj.h"
30+
#include "shared-bindings/canio/__init__.h"
31+
#include "shared-bindings/canio/CAN.h"
32+
#include "common-hal/microcontroller/Pin.h"
33+
#include "common-hal/canio/__init__.h"
34+
#include "shared-module/canio/Message.h"
35+
36+
#include "stm32f4xx_hal.h"
37+
#include "stm32f4xx_hal_can.h"
38+
39+
#define FILTER_BANK_COUNT (28)
40+
41+
typedef struct canio_can_obj {
42+
mp_obj_base_t base;
43+
CAN_HandleTypeDef handle;
44+
CAN_TypeDef *filter_hw;
45+
int baudrate;
46+
const mcu_pin_obj_t *rx_pin;
47+
const mcu_pin_obj_t *tx_pin;
48+
bool loopback:1;
49+
bool silent:1;
50+
bool auto_restart:1;
51+
bool fifo0_in_use:1;
52+
bool fifo1_in_use:1;
53+
uint8_t periph_index:2;
54+
uint8_t cancel_mailbox;
55+
uint8_t start_filter_bank;
56+
uint8_t end_filter_bank;
57+
long filter_in_use; // bitmask for the 28 filter banks
58+
} canio_can_obj_t;

0 commit comments

Comments
 (0)