Skip to content

Commit c46cf85

Browse files
committed
canio: implement for stm32f405
The has successfully run my loopback self-test program for CAN, which tests transmission, reception, and filtering. The 1M baud rate setting was also verified on saleae to be accurate.
1 parent 9535edb commit c46cf85

File tree

13 files changed

+792
-2
lines changed

13 files changed

+792
-2
lines changed

lib/tinyusb

Submodule tinyusb updated 84 files

ports/stm/Makefile

Lines changed: 4 additions & 0 deletions
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/mpconfigboard.mk

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,5 @@ LD_DEFAULT = boards/STM32F405_default.ld
1616
# UF2 boot option
1717
LD_BOOT = boards/STM32F405_boot.ld
1818
UF2_OFFSET = 0x8010000
19+
20+
CIRCUITPY_CANIO = 1

ports/stm/boards/feather_stm32f405_express/pins.c

Lines changed: 3 additions & 0 deletions
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

Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
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+
__attribute__((optimize("O0")))
54+
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)
55+
{
56+
#define DIV_ROUND(a, b) (((a) + (b)/2) / (b))
57+
#define DIV_ROUND_UP(a, b) (((a) + (b) - 1) / (b))
58+
59+
const uint8_t can_tx_len = MP_ARRAY_SIZE(mcu_can_tx_list);
60+
const uint8_t can_rx_len = MP_ARRAY_SIZE(mcu_can_rx_list);
61+
62+
const mcu_periph_obj_t *mcu_tx = find_pin_function(mcu_can_tx_list, can_tx_len, tx, -1);
63+
if (!mcu_tx) {
64+
mp_raise_ValueError_varg(translate("Invalid %q pin selection"), MP_QSTR_tx);
65+
}
66+
int periph_index = mcu_tx->periph_index;
67+
68+
const mcu_periph_obj_t *mcu_rx = find_pin_function(mcu_can_rx_list, can_rx_len, rx, periph_index);
69+
if (!mcu_rx) {
70+
mp_raise_ValueError_varg(translate("Invalid %q pin selection"), MP_QSTR_rx);
71+
}
72+
73+
if (reserved_can[periph_index]) {
74+
mp_raise_ValueError(translate("Hardware busy, try alternative pins"));
75+
}
76+
77+
const uint32_t can_frequency = 42000000;
78+
uint32_t clocks_per_bit = DIV_ROUND(can_frequency, baudrate);
79+
uint32_t clocks_to_sample = DIV_ROUND(clocks_per_bit * 7, 8);
80+
uint32_t clocks_after_sample = clocks_per_bit - clocks_to_sample;
81+
uint32_t divisor = MAX(DIV_ROUND_UP(clocks_to_sample, 16), DIV_ROUND_UP(clocks_after_sample, 8));
82+
const uint32_t sjw = 3;
83+
84+
uint32_t tq_per_bit = DIV_ROUND(clocks_per_bit, divisor);
85+
uint32_t tq_to_sample = DIV_ROUND(clocks_to_sample, divisor);
86+
uint32_t tq_after_sample = tq_per_bit - tq_to_sample;
87+
88+
if (divisor > 1023) {
89+
mp_raise_OSError(MP_EINVAL); // baudrate cannot be attained (16kHz or something is lower bound, should never happen)
90+
}
91+
92+
{
93+
GPIO_InitTypeDef GPIO_InitStruct = {
94+
.Pin = pin_mask(tx->number),
95+
.Speed = GPIO_SPEED_FREQ_VERY_HIGH,
96+
.Mode = GPIO_MODE_AF_PP,
97+
.Pull = GPIO_PULLUP,
98+
.Alternate = mcu_tx->altfn_index,
99+
};
100+
HAL_GPIO_Init(pin_port(tx->port), &GPIO_InitStruct);
101+
102+
GPIO_InitStruct.Pin = pin_mask(rx->number);
103+
GPIO_InitStruct.Alternate = mcu_rx->altfn_index;
104+
HAL_GPIO_Init(pin_port(rx->port), &GPIO_InitStruct);
105+
}
106+
107+
CAN_TypeDef *hw = mcu_can_banks[periph_index - 1];
108+
109+
// CAN2 shares resources with CAN1. So we always enable CAN1, then split
110+
// the filter banks equally between them.
111+
112+
__HAL_RCC_CAN1_CLK_ENABLE();
113+
114+
if(hw == CAN2) {
115+
__HAL_RCC_CAN2_CLK_ENABLE();
116+
self->start_filter_bank = 14;
117+
self->end_filter_bank = 28;
118+
self->filter_hw = CAN1;
119+
} else {
120+
self->start_filter_bank = 0;
121+
self->end_filter_bank = 14;
122+
self->filter_hw = hw;
123+
}
124+
125+
CAN_InitTypeDef init = {
126+
.AutoRetransmission = ENABLE,
127+
.AutoBusOff = ENABLE,
128+
.Prescaler = divisor,
129+
.Mode = (loopback ? CAN_MODE_LOOPBACK : 0) | (silent ? CAN_MODE_SILENT_LOOPBACK : 0),
130+
.SyncJumpWidth = (sjw-1) << CAN_BTR_SJW_Pos,
131+
.TimeSeg1 = (tq_to_sample-2) << CAN_BTR_TS1_Pos,
132+
.TimeSeg2 = (tq_after_sample-1) << CAN_BTR_TS2_Pos,
133+
};
134+
135+
self->periph_index = periph_index;
136+
self->silent = silent;
137+
self->loopback = loopback;
138+
self->baudrate = baudrate;
139+
140+
self->handle.Instance = hw;
141+
self->handle.Init = init;
142+
self->handle.State = HAL_CAN_STATE_RESET;
143+
144+
HAL_CAN_Init(&self->handle);
145+
146+
// Set the filter split as 14:14
147+
// COULDDO(@jepler): Dynamically allocate filter banks between CAN1/2
148+
self->filter_hw->FMR |= CAN_FMR_FINIT;
149+
self->filter_hw->FMR = CAN_FMR_FINIT | (14 << CAN_FMR_CAN2SB_Pos);
150+
151+
// Clear every filter enable bit for this can HW
152+
uint32_t fa1r = self->filter_hw->FA1R;
153+
for (int i = self->start_filter_bank; i<self->end_filter_bank; i++) {
154+
fa1r &= ~(1 << i);
155+
}
156+
self->filter_hw->FA1R = fa1r;
157+
CLEAR_BIT(self->filter_hw->FMR, CAN_FMR_FINIT);
158+
159+
HAL_CAN_Start(&self->handle);
160+
161+
reserved_can[periph_index] = true;
162+
}
163+
164+
bool common_hal_canio_can_loopback_get(canio_can_obj_t *self)
165+
{
166+
return self->loopback;
167+
}
168+
169+
int common_hal_canio_can_baudrate_get(canio_can_obj_t *self)
170+
{
171+
return self->baudrate;
172+
}
173+
174+
int common_hal_canio_can_transmit_error_count_get(canio_can_obj_t *self)
175+
{
176+
return (self->handle.Instance->ESR & CAN_ESR_TEC) >> CAN_ESR_TEC_Pos;
177+
}
178+
179+
int common_hal_canio_can_receive_error_count_get(canio_can_obj_t *self)
180+
{
181+
return (self->handle.Instance->ESR & CAN_ESR_REC) >> CAN_ESR_REC_Pos;
182+
}
183+
184+
int common_hal_canio_can_error_warning_state_count_get(canio_can_obj_t *self)
185+
{
186+
return self->error_warning_state_count;
187+
}
188+
189+
int common_hal_canio_can_error_passive_state_count_get(canio_can_obj_t *self)
190+
{
191+
return self->error_passive_state_count;
192+
}
193+
194+
int common_hal_canio_can_bus_off_state_count_get(canio_can_obj_t *self)
195+
{
196+
return self->bus_off_state_count;
197+
}
198+
199+
canio_bus_state_t common_hal_canio_can_state_get(canio_can_obj_t *self) {
200+
uint32_t esr = self->handle.Instance->ESR;
201+
if (READ_BIT(esr, CAN_ESR_BOFF)) {
202+
return BUS_STATE_OFF;
203+
}
204+
if (READ_BIT(esr, CAN_ESR_EPVF)) {
205+
return BUS_STATE_ERROR_PASSIVE;
206+
}
207+
if (READ_BIT(esr, CAN_ESR_EWGF)) {
208+
return BUS_STATE_ERROR_WARNING;
209+
}
210+
return BUS_STATE_ERROR_ACTIVE;
211+
}
212+
213+
void common_hal_canio_can_restart(canio_can_obj_t *self) {
214+
if (!common_hal_canio_can_auto_restart_get(self)) {
215+
HAL_CAN_Start(&self->handle);
216+
}
217+
}
218+
219+
bool common_hal_canio_can_auto_restart_get(canio_can_obj_t *self) {
220+
return READ_BIT(self->handle.Instance->MCR, CAN_MCR_ABOM);
221+
}
222+
223+
void common_hal_canio_can_auto_restart_set(canio_can_obj_t *self, bool value) {
224+
if(value) {
225+
SET_BIT(self->handle.Instance->MCR, CAN_MCR_ABOM);
226+
} else {
227+
CLEAR_BIT(self->handle.Instance->MCR, CAN_MCR_ABOM);
228+
}
229+
}
230+
231+
void common_hal_canio_can_send(canio_can_obj_t *self, mp_obj_t message_in)
232+
{
233+
canio_message_obj_t *message = message_in;
234+
uint32_t mailbox;
235+
bool rtr = message->base.type == &canio_remote_transmission_request_type;
236+
CAN_TxHeaderTypeDef header = {
237+
.StdId = message->id,
238+
.ExtId = message->id,
239+
.IDE = message->extended ? CAN_ID_EXT : CAN_ID_STD,
240+
.RTR = rtr ? CAN_RTR_REMOTE : CAN_RTR_DATA,
241+
.DLC = message->size,
242+
};
243+
HAL_StatusTypeDef status = HAL_CAN_AddTxMessage(&self->handle, &header, message->data, &mailbox);
244+
if (status != HAL_OK) {
245+
mp_raise_OSError(MP_ENOMEM);
246+
}
247+
}
248+
249+
bool common_hal_canio_can_silent_get(canio_can_obj_t *self) {
250+
return self->silent;
251+
}
252+
253+
bool common_hal_canio_can_deinited(canio_can_obj_t *self) {
254+
return !self->handle.Instance;
255+
}
256+
257+
void common_hal_canio_can_check_for_deinit(canio_can_obj_t *self) {
258+
if (common_hal_canio_can_deinited(self)) {
259+
raise_deinited_error();
260+
}
261+
}
262+
263+
void common_hal_canio_can_deinit(canio_can_obj_t *self)
264+
{
265+
if (self->handle.Instance) {
266+
SET_BIT(self->handle.Instance->MCR, CAN_MCR_RESET);
267+
while (READ_BIT(self->handle.Instance->MCR, CAN_MCR_RESET)) {
268+
}
269+
reserved_can[self->periph_index] = 0;
270+
}
271+
self->handle.Instance = NULL;
272+
}
273+
274+
void common_hal_canio_reset(void) {
275+
for (size_t i=0; i<MP_ARRAY_SIZE(mcu_can_banks); i++) {
276+
SET_BIT(mcu_can_banks[i]->MCR, CAN_MCR_RESET);
277+
reserved_can[i] = 0;
278+
}
279+
}

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

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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+
volatile uint32_t error_warning_state_count;
46+
volatile uint32_t error_passive_state_count;
47+
volatile uint32_t bus_off_state_count;
48+
int baudrate;
49+
const mcu_pin_obj_t *rx_pin;
50+
const mcu_pin_obj_t *tx_pin;
51+
bool loopback:1;
52+
bool silent:1;
53+
bool auto_restart:1;
54+
bool fifo0_in_use:1;
55+
bool fifo1_in_use:1;
56+
uint8_t periph_index:2;
57+
uint8_t start_filter_bank;
58+
uint8_t end_filter_bank;
59+
long filter_in_use; // bitmask for the 28 filter banks
60+
} canio_can_obj_t;

0 commit comments

Comments
 (0)