feat(can-sensor): add STM32F1 bxCAN port
This commit is contained in:
@@ -28,7 +28,7 @@ templates/
|
|||||||
| [`c/keypad`](c/keypad) | шесть кнопок: антидребезг, автоповтор, удержание, очередь событий | `stdint.h` | чтение уровня кнопки, время в мс |
|
| [`c/keypad`](c/keypad) | шесть кнопок: антидребезг, автоповтор, удержание, очередь событий | `stdint.h` | чтение уровня кнопки, время в мс |
|
||||||
| [`c/menu`](c/menu) | экранное меню: стек экранов, курсор, прокрутка, тема | `stdint.h` | заливка прямоугольника, вывод строки |
|
| [`c/menu`](c/menu) | экранное меню: стек экранов, курсор, прокрутка, тема | `stdint.h` | заливка прямоугольника, вывод строки |
|
||||||
| [`c/eeprom-ft24c256`](c/eeprom-ft24c256) | EEPROM 24Cxx по I²C с нарезкой записи по страницам | `stdint.h` | две I²C-транзакции, задержка |
|
| [`c/eeprom-ft24c256`](c/eeprom-ft24c256) | EEPROM 24Cxx по I²C с нарезкой записи по страницам | `stdint.h` | две I²C-транзакции, задержка |
|
||||||
| [`c/can-sensor`](c/can-sensor) | передача 64-битных ROM датчиков парой CAN-кадров | `stdint.h` | отправка и приём CAN-кадра |
|
| [`c/can-sensor`](c/can-sensor) | однокадровые SETCAN SETTINGS для 64-битных ROM | ядро: `stdint.h`; порт F1: CMSIS | callbacks либо готовый bxCAN STM32F1 |
|
||||||
| [`c/ds18b20`](c/ds18b20) | термометры DS18B20 поверх программной 1-Wire | `stdint.h` | Init, DelayUs, Reset, WriteBit, ReadBit — **порты STM32F103, STM32G431 и STM32G474 в комплекте** |
|
| [`c/ds18b20`](c/ds18b20) | термометры DS18B20 поверх программной 1-Wire | `stdint.h` | Init, DelayUs, Reset, WriteBit, ReadBit — **порты STM32F103, STM32G431 и STM32G474 в комплекте** |
|
||||||
| [`c/protocan-transport`](c/protocan-transport) | транспорт ProtoCAN: кадр, канал, CRC, общее адресное пространство, каталог GUI | `stdint.h` | запись в поток и запрос свободного места — **порт STM32F4 в комплекте** |
|
| [`c/protocan-transport`](c/protocan-transport) | транспорт ProtoCAN: кадр, канал, CRC, общее адресное пространство, каталог GUI | `stdint.h` | запись в поток и запрос свободного места — **порт STM32F4 в комплекте** |
|
||||||
| [`c/protocan-boot`](c/protocan-boot) | адресная прошивка по ProtoCAN: A/B-слоты, сессия, CRC32, verify и rollback-контракт | C99 | CAN TX, erase/write Flash, boot metadata, проверка образа и reboot |
|
| [`c/protocan-boot`](c/protocan-boot) | адресная прошивка по ProtoCAN: A/B-слоты, сессия, CRC32, verify и rollback-контракт | C99 | CAN TX, erase/write Flash, boot metadata, проверка образа и reboot |
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
| Файл | Что делает | Зависимости |
|
| Файл | Что делает | Зависимости |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `can_sensor.h`, `can_sensor.c` | сборка и разбор SETTINGS, повторы передачи, счётчики обмена | `stdint.h` |
|
| `can_sensor.h`, `can_sensor.c` | сборка и разбор SETTINGS, повторы передачи, счётчики обмена | `stdint.h` |
|
||||||
|
| `ports/stm32f1/` | опросный порт CAN1: GPIO, BTR, фильтр, mailbox/FIFO, тайм-аут ACK | CMSIS `stm32f10x.h` |
|
||||||
|
|
||||||
`ZZ` — номер сборки, `YY` — позиция. Нулевой ROM с DLC=8 очищает локацию.
|
`ZZ` — номер сборки, `YY` — позиция. Нулевой ROM с DLC=8 очищает локацию.
|
||||||
|
|
||||||
@@ -47,6 +48,30 @@ CanSensor_Message message;
|
|||||||
if (CanSensor_Poll(&link, &message)) { /* принят идентификатор */ }
|
if (CanSensor_Poll(&link, &message)) { /* принят идентификатор */ }
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Порт STM32F1
|
||||||
|
|
||||||
|
Порт не использует STM32 HAL и не занимает прерывания. Скопируйте
|
||||||
|
`ports/stm32f1/can_sensor_stm32f1_config.f103.template.h` в приложение под
|
||||||
|
именем `can_sensor_stm32f1_config.h`, добавьте `ports/stm32f1` в include path и
|
||||||
|
соберите `can_sensor_stm32f1.c` вместе с ядром:
|
||||||
|
|
||||||
|
```c
|
||||||
|
CanSensorStm32F1_Port bxcan = {0};
|
||||||
|
CanSensor_Io io;
|
||||||
|
|
||||||
|
CanSensorStm32F1_Start(&bxcan, 500000U, 0x17F00000UL, 0x1FF00000UL, 0U);
|
||||||
|
io = CanSensorStm32F1_MakeIo(&bxcan);
|
||||||
|
CanSensor_Init(&link, &io, &config);
|
||||||
|
|
||||||
|
/* Из главного цикла: */
|
||||||
|
CanSensorStm32F1_Task(&bxcan, now_ms, 20U);
|
||||||
|
```
|
||||||
|
|
||||||
|
Фильтр порта принимает только Extended data-кадры. Маска задаётся приложением:
|
||||||
|
можно пропустить только SETTINGS либо оставить открытым `MsgType` для соседних
|
||||||
|
команд того же узла. `CanSensorStm32F1_Receive()` доступна отдельно, если перед
|
||||||
|
`CanSensor_HandleFrame()` приложению нужно разобрать такие команды самому.
|
||||||
|
|
||||||
## Проверено в проектах
|
## Проверено в проектах
|
||||||
|
|
||||||
`KONOR_ds18b20` — bxCAN на STM32F103C8T6. Естественная пара — [`ds18b20`](../ds18b20):
|
`KONOR_ds18b20` — bxCAN на STM32F103C8T6. Естественная пара — [`ds18b20`](../ds18b20):
|
||||||
|
|||||||
330
c/can-sensor/ports/stm32f1/can_sensor_stm32f1.c
Normal file
330
c/can-sensor/ports/stm32f1/can_sensor_stm32f1.c
Normal file
@@ -0,0 +1,330 @@
|
|||||||
|
/**
|
||||||
|
* @file can_sensor_stm32f1.c
|
||||||
|
* @brief Опросный порт can-sensor на bxCAN STM32F1 без STM32 HAL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "can_sensor_stm32f1.h"
|
||||||
|
|
||||||
|
#include "can_sensor_stm32f1_config.h"
|
||||||
|
|
||||||
|
#if !defined(CAN_SENSOR_STM32F1_GPIO) \
|
||||||
|
|| !defined(CAN_SENSOR_STM32F1_GPIO_CLOCK) \
|
||||||
|
|| !defined(CAN_SENSOR_STM32F1_RX_PIN) \
|
||||||
|
|| !defined(CAN_SENSOR_STM32F1_TX_PIN)
|
||||||
|
#error "can_sensor_stm32f1_config.h должен задать GPIO, GPIO_CLOCK, RX_PIN и TX_PIN"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define CAN_STM32F1_TQ_MAX 18U
|
||||||
|
#define CAN_STM32F1_TQ_MIN 8U
|
||||||
|
#define CAN_STM32F1_TIMEOUT_CYCLES 1000000U
|
||||||
|
#define CAN_STM32F1_TSR_ALL_EMPTY (CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2)
|
||||||
|
#define CAN_STM32F1_TSR_ALL_ABORT (CAN_TSR_ABRQ0 | CAN_TSR_ABRQ1 | CAN_TSR_ABRQ2)
|
||||||
|
#define CAN_STM32F1_TSR_ANY_TXOK (CAN_TSR_TXOK0 | CAN_TSR_TXOK1 | CAN_TSR_TXOK2)
|
||||||
|
#define CAN_STM32F1_TSR_DONE_FLAGS \
|
||||||
|
(CAN_TSR_RQCP0 | CAN_TSR_TXOK0 | CAN_TSR_ALST0 | CAN_TSR_TERR0 \
|
||||||
|
| CAN_TSR_RQCP1 | CAN_TSR_TXOK1 | CAN_TSR_ALST1 | CAN_TSR_TERR1 \
|
||||||
|
| CAN_TSR_RQCP2 | CAN_TSR_TXOK2 | CAN_TSR_ALST2 | CAN_TSR_TERR2)
|
||||||
|
|
||||||
|
static uint32_t can_stm32f1_pclk1(void)
|
||||||
|
{
|
||||||
|
static const uint8_t shift[8] = { 0U, 0U, 0U, 0U, 1U, 2U, 3U, 4U };
|
||||||
|
const uint32_t bits = (RCC->CFGR & RCC_CFGR_PPRE1) >> 8U;
|
||||||
|
|
||||||
|
return SystemCoreClock >> shift[bits & 0x7U];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void can_stm32f1_configure_pin(GPIO_TypeDef *gpio, uint8_t pin,
|
||||||
|
uint32_t mode)
|
||||||
|
{
|
||||||
|
uint32_t config;
|
||||||
|
uint32_t shift;
|
||||||
|
|
||||||
|
if (pin < 8U) {
|
||||||
|
shift = (uint32_t)pin * 4U;
|
||||||
|
config = gpio->CRL;
|
||||||
|
config = (config & ~(0xFUL << shift)) | (mode << shift);
|
||||||
|
gpio->CRL = config;
|
||||||
|
} else {
|
||||||
|
shift = ((uint32_t)pin - 8U) * 4U;
|
||||||
|
config = gpio->CRH;
|
||||||
|
config = (config & ~(0xFUL << shift)) | (mode << shift);
|
||||||
|
gpio->CRH = config;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void can_stm32f1_configure_pins(void)
|
||||||
|
{
|
||||||
|
RCC->APB2ENR |= CAN_SENSOR_STM32F1_GPIO_CLOCK | RCC_APB2ENR_AFIOEN;
|
||||||
|
/* RX: input pull-up; TX: alternate function push-pull, 50 MHz. */
|
||||||
|
can_stm32f1_configure_pin(CAN_SENSOR_STM32F1_GPIO,
|
||||||
|
CAN_SENSOR_STM32F1_RX_PIN, 0x8UL);
|
||||||
|
can_stm32f1_configure_pin(CAN_SENSOR_STM32F1_GPIO,
|
||||||
|
CAN_SENSOR_STM32F1_TX_PIN, 0xBUL);
|
||||||
|
CAN_SENSOR_STM32F1_GPIO->BSRR =
|
||||||
|
(uint32_t)(1UL << CAN_SENSOR_STM32F1_RX_PIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t can_stm32f1_calc_timing(uint32_t bitrate, uint32_t *btr,
|
||||||
|
CanSensorStm32F1_Timing *timing)
|
||||||
|
{
|
||||||
|
const uint32_t pclk = can_stm32f1_pclk1();
|
||||||
|
uint32_t total;
|
||||||
|
uint32_t tq;
|
||||||
|
|
||||||
|
if ((bitrate == 0U) || (btr == 0) || (pclk == 0U)
|
||||||
|
|| ((pclk % bitrate) != 0U)) {
|
||||||
|
return 0U;
|
||||||
|
}
|
||||||
|
total = pclk / bitrate;
|
||||||
|
for (tq = CAN_STM32F1_TQ_MAX; tq >= CAN_STM32F1_TQ_MIN; tq--) {
|
||||||
|
const uint32_t prescaler = total / tq;
|
||||||
|
|
||||||
|
if (((total % tq) == 0U) && (prescaler >= 1U)
|
||||||
|
&& (prescaler <= 1024U)) {
|
||||||
|
const uint32_t ts2 = tq / 5U;
|
||||||
|
const uint32_t ts1 = tq - 1U - ts2;
|
||||||
|
|
||||||
|
*btr = ((ts2 - 1U) << 20U) | ((ts1 - 1U) << 16U)
|
||||||
|
| (prescaler - 1U);
|
||||||
|
if (timing != 0) {
|
||||||
|
timing->pclk_hz = pclk;
|
||||||
|
timing->prescaler = (uint16_t)prescaler;
|
||||||
|
timing->ts1 = (uint8_t)ts1;
|
||||||
|
timing->ts2 = (uint8_t)ts2;
|
||||||
|
timing->bitrate = pclk / (prescaler * tq);
|
||||||
|
timing->sample_point =
|
||||||
|
(uint8_t)(((1U + ts1) * 100U) / tq);
|
||||||
|
}
|
||||||
|
return 1U;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t CanSensorStm32F1_CalcTiming(uint32_t bitrate,
|
||||||
|
CanSensorStm32F1_Timing *timing)
|
||||||
|
{
|
||||||
|
uint32_t btr;
|
||||||
|
|
||||||
|
return can_stm32f1_calc_timing(bitrate, &btr, timing);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void can_stm32f1_configure_filter(uint32_t id, uint32_t mask)
|
||||||
|
{
|
||||||
|
const uint32_t filter_id = ((id & mask) << 3U) | CAN_RI0R_IDE;
|
||||||
|
const uint32_t filter_mask =
|
||||||
|
(mask << 3U) | CAN_RI0R_IDE | CAN_RI0R_RTR;
|
||||||
|
|
||||||
|
CAN1->FMR |= CAN_FMR_FINIT;
|
||||||
|
CAN1->FA1R &= ~1UL;
|
||||||
|
CAN1->FS1R |= 1UL;
|
||||||
|
CAN1->FM1R &= ~1UL;
|
||||||
|
CAN1->sFilterRegister[0].FR1 = filter_id;
|
||||||
|
CAN1->sFilterRegister[0].FR2 = filter_mask;
|
||||||
|
CAN1->FFA1R &= ~1UL;
|
||||||
|
CAN1->FA1R |= 1UL;
|
||||||
|
CAN1->FMR &= ~CAN_FMR_FINIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void can_stm32f1_load_mailbox(uint32_t mailbox,
|
||||||
|
const CanSensor_Frame *frame)
|
||||||
|
{
|
||||||
|
uint32_t identifier;
|
||||||
|
uint8_t index;
|
||||||
|
|
||||||
|
if (frame->extended != 0U) {
|
||||||
|
identifier = ((frame->id & 0x1FFFFFFFUL) << 3U) | CAN_TI0R_IDE;
|
||||||
|
} else {
|
||||||
|
identifier = (frame->id & 0x7FFUL) << 21U;
|
||||||
|
}
|
||||||
|
CAN1->sTxMailBox[mailbox].TDTR = (uint32_t)(frame->length & 0x0FU);
|
||||||
|
CAN1->sTxMailBox[mailbox].TDLR = 0U;
|
||||||
|
CAN1->sTxMailBox[mailbox].TDHR = 0U;
|
||||||
|
for (index = 0U; index < frame->length; index++) {
|
||||||
|
if (index < 4U) {
|
||||||
|
CAN1->sTxMailBox[mailbox].TDLR |=
|
||||||
|
(uint32_t)frame->data[index] << (index * 8U);
|
||||||
|
} else {
|
||||||
|
CAN1->sTxMailBox[mailbox].TDHR |=
|
||||||
|
(uint32_t)frame->data[index] << ((index - 4U) * 8U);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CAN1->sTxMailBox[mailbox].TIR = identifier | CAN_TI0R_TXRQ;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t CanSensorStm32F1_Start(CanSensorStm32F1_Port *port,
|
||||||
|
uint32_t bitrate, uint32_t filter_id,
|
||||||
|
uint32_t filter_mask, uint8_t loopback)
|
||||||
|
{
|
||||||
|
uint32_t btr;
|
||||||
|
uint32_t guard;
|
||||||
|
uint32_t mode_bits;
|
||||||
|
|
||||||
|
if ((port == 0) || (filter_id > 0x1FFFFFFFUL)
|
||||||
|
|| (filter_mask > 0x1FFFFFFFUL)
|
||||||
|
|| (can_stm32f1_calc_timing(bitrate, &btr, &port->timing) == 0U)) {
|
||||||
|
return 0U;
|
||||||
|
}
|
||||||
|
CanSensorStm32F1_Stop(port);
|
||||||
|
can_stm32f1_configure_pins();
|
||||||
|
RCC->APB1ENR |= RCC_APB1ENR_CAN1EN;
|
||||||
|
|
||||||
|
CAN1->MCR &= ~CAN_MCR_SLEEP;
|
||||||
|
CAN1->MCR |= CAN_MCR_INRQ;
|
||||||
|
for (guard = 0U; guard < CAN_STM32F1_TIMEOUT_CYCLES; guard++) {
|
||||||
|
if ((CAN1->MSR & CAN_MSR_INAK) != 0U) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((CAN1->MSR & CAN_MSR_INAK) == 0U) {
|
||||||
|
return 0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
mode_bits = (loopback != 0U) ? (CAN_BTR_LBKM | CAN_BTR_SILM) : 0U;
|
||||||
|
CAN1->MCR = CAN_MCR_INRQ | CAN_MCR_ABOM | CAN_MCR_TXFP;
|
||||||
|
CAN1->BTR = btr | mode_bits;
|
||||||
|
can_stm32f1_configure_filter(filter_id, filter_mask);
|
||||||
|
CAN1->MCR &= ~CAN_MCR_INRQ;
|
||||||
|
for (guard = 0U; guard < CAN_STM32F1_TIMEOUT_CYCLES; guard++) {
|
||||||
|
if ((CAN1->MSR & CAN_MSR_INAK) == 0U) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((CAN1->MSR & CAN_MSR_INAK) != 0U) {
|
||||||
|
return 0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
port->bitrate = bitrate;
|
||||||
|
port->loopback = (uint8_t)(loopback != 0U);
|
||||||
|
port->no_transceiver = 0U;
|
||||||
|
port->tx_waiting = 0U;
|
||||||
|
port->ready = 1U;
|
||||||
|
return 1U;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanSensorStm32F1_Stop(CanSensorStm32F1_Port *port)
|
||||||
|
{
|
||||||
|
if (port == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
RCC->APB1ENR |= RCC_APB1ENR_CAN1EN;
|
||||||
|
RCC->APB1RSTR |= RCC_APB1RSTR_CAN1RST;
|
||||||
|
RCC->APB1RSTR &= ~RCC_APB1RSTR_CAN1RST;
|
||||||
|
port->ready = 0U;
|
||||||
|
port->bitrate = 0U;
|
||||||
|
port->loopback = 0U;
|
||||||
|
port->no_transceiver = 0U;
|
||||||
|
port->tx_waiting = 0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t CanSensorStm32F1_Send(void *context, const CanSensor_Frame *frame)
|
||||||
|
{
|
||||||
|
CanSensorStm32F1_Port *port = (CanSensorStm32F1_Port *)context;
|
||||||
|
uint32_t mailbox;
|
||||||
|
|
||||||
|
if ((port == 0) || (frame == 0) || (port->ready == 0U)
|
||||||
|
|| (frame->length > CAN_SENSOR_MAX_DATA)) {
|
||||||
|
return 0U;
|
||||||
|
}
|
||||||
|
if ((CAN1->TSR & CAN_TSR_TME0) != 0U) {
|
||||||
|
mailbox = 0U;
|
||||||
|
} else if ((CAN1->TSR & CAN_TSR_TME1) != 0U) {
|
||||||
|
mailbox = 1U;
|
||||||
|
} else if ((CAN1->TSR & CAN_TSR_TME2) != 0U) {
|
||||||
|
mailbox = 2U;
|
||||||
|
} else {
|
||||||
|
return 0U;
|
||||||
|
}
|
||||||
|
can_stm32f1_load_mailbox(mailbox, frame);
|
||||||
|
return 1U;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t CanSensorStm32F1_Receive(void *context, CanSensor_Frame *frame)
|
||||||
|
{
|
||||||
|
CanSensorStm32F1_Port *port = (CanSensorStm32F1_Port *)context;
|
||||||
|
uint32_t identifier;
|
||||||
|
uint32_t low;
|
||||||
|
uint32_t high;
|
||||||
|
uint8_t index;
|
||||||
|
|
||||||
|
if ((port == 0) || (frame == 0) || (port->ready == 0U)
|
||||||
|
|| ((CAN1->RF0R & CAN_RF0R_FMP0) == 0U)) {
|
||||||
|
return 0U;
|
||||||
|
}
|
||||||
|
identifier = CAN1->sFIFOMailBox[0].RIR;
|
||||||
|
if ((identifier & CAN_RI0R_IDE) != 0U) {
|
||||||
|
frame->extended = 1U;
|
||||||
|
frame->id = (identifier >> 3U) & 0x1FFFFFFFUL;
|
||||||
|
} else {
|
||||||
|
frame->extended = 0U;
|
||||||
|
frame->id = (identifier >> 21U) & 0x7FFUL;
|
||||||
|
}
|
||||||
|
frame->length = (uint8_t)(CAN1->sFIFOMailBox[0].RDTR & 0x0FU);
|
||||||
|
if (frame->length > CAN_SENSOR_MAX_DATA) {
|
||||||
|
frame->length = CAN_SENSOR_MAX_DATA;
|
||||||
|
}
|
||||||
|
low = CAN1->sFIFOMailBox[0].RDLR;
|
||||||
|
high = CAN1->sFIFOMailBox[0].RDHR;
|
||||||
|
for (index = 0U; index < CAN_SENSOR_MAX_DATA; index++) {
|
||||||
|
if (index < 4U) {
|
||||||
|
frame->data[index] =
|
||||||
|
(uint8_t)((low >> (index * 8U)) & 0xFFU);
|
||||||
|
} else {
|
||||||
|
frame->data[index] =
|
||||||
|
(uint8_t)((high >> ((index - 4U) * 8U)) & 0xFFU);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CAN1->RF0R |= CAN_RF0R_RFOM0;
|
||||||
|
return 1U;
|
||||||
|
}
|
||||||
|
|
||||||
|
CanSensor_Io CanSensorStm32F1_MakeIo(CanSensorStm32F1_Port *port)
|
||||||
|
{
|
||||||
|
CanSensor_Io io;
|
||||||
|
|
||||||
|
io.send = CanSensorStm32F1_Send;
|
||||||
|
io.receive = CanSensorStm32F1_Receive;
|
||||||
|
io.context = port;
|
||||||
|
return io;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanSensorStm32F1_Task(CanSensorStm32F1_Port *port, uint32_t now_ms,
|
||||||
|
uint32_t timeout_ms)
|
||||||
|
{
|
||||||
|
uint32_t status;
|
||||||
|
|
||||||
|
if ((port == 0) || (port->ready == 0U)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
status = CAN1->TSR;
|
||||||
|
if ((status & CAN_STM32F1_TSR_ALL_EMPTY) == CAN_STM32F1_TSR_ALL_EMPTY) {
|
||||||
|
if ((status & CAN_STM32F1_TSR_ANY_TXOK) != 0U) {
|
||||||
|
port->no_transceiver = 0U;
|
||||||
|
}
|
||||||
|
CAN1->TSR = status & CAN_STM32F1_TSR_DONE_FLAGS;
|
||||||
|
port->tx_waiting = 0U;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((timeout_ms == 0U) || (port->tx_waiting == 0U)) {
|
||||||
|
port->tx_waiting = (uint8_t)(timeout_ms != 0U);
|
||||||
|
port->tx_started_ms = now_ms;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((now_ms - port->tx_started_ms) < timeout_ms) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CAN1->TSR = CAN_STM32F1_TSR_ALL_ABORT;
|
||||||
|
CAN1->TSR = CAN_STM32F1_TSR_DONE_FLAGS;
|
||||||
|
port->tx_waiting = 0U;
|
||||||
|
port->tx_timeouts++;
|
||||||
|
port->no_transceiver = 1U;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t CanSensorStm32F1_BusError(const CanSensorStm32F1_Port *port)
|
||||||
|
{
|
||||||
|
if ((port == 0) || (port->ready == 0U)) {
|
||||||
|
return 0U;
|
||||||
|
}
|
||||||
|
return (uint8_t)(((CAN1->ESR & (CAN_ESR_BOFF | CAN_ESR_EPVF)) != 0U)
|
||||||
|
? 1U : 0U);
|
||||||
|
}
|
||||||
77
c/can-sensor/ports/stm32f1/can_sensor_stm32f1.h
Normal file
77
c/can-sensor/ports/stm32f1/can_sensor_stm32f1.h
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
/**
|
||||||
|
* @file can_sensor_stm32f1.h
|
||||||
|
* @brief Опросный порт can-sensor на bxCAN микроконтроллеров STM32F1.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CAN_SENSOR_STM32F1_H
|
||||||
|
#define CAN_SENSOR_STM32F1_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "can_sensor.h"
|
||||||
|
|
||||||
|
/** Разрядность бита, записанная портом в регистр BTR. */
|
||||||
|
typedef struct {
|
||||||
|
uint32_t pclk_hz;
|
||||||
|
uint32_t bitrate;
|
||||||
|
uint16_t prescaler;
|
||||||
|
uint8_t ts1;
|
||||||
|
uint8_t ts2;
|
||||||
|
uint8_t sample_point;
|
||||||
|
} CanSensorStm32F1_Timing;
|
||||||
|
|
||||||
|
/** Состояние одного контроллера bxCAN. Поля доступны только для диагностики. */
|
||||||
|
typedef struct {
|
||||||
|
uint32_t bitrate;
|
||||||
|
uint32_t tx_timeouts;
|
||||||
|
uint32_t tx_started_ms;
|
||||||
|
CanSensorStm32F1_Timing timing;
|
||||||
|
uint8_t ready;
|
||||||
|
uint8_t loopback;
|
||||||
|
uint8_t no_transceiver;
|
||||||
|
uint8_t tx_waiting;
|
||||||
|
} CanSensorStm32F1_Port;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Проверяет достижимость скорости и рассчитывает поля BTR.
|
||||||
|
* @param bitrate Требуемая скорость, бит/с.
|
||||||
|
* @param timing Приёмник результата либо 0.
|
||||||
|
* @return 1, если скорость получается из текущей частоты APB1 без ошибки.
|
||||||
|
*/
|
||||||
|
uint8_t CanSensorStm32F1_CalcTiming(uint32_t bitrate,
|
||||||
|
CanSensorStm32F1_Timing *timing);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Запускает CAN1, настраивает выводы и один 32-битный фильтр FIFO 0.
|
||||||
|
*
|
||||||
|
* Фильтр сравнивает Extended ID по формуле `(id & filter_mask) ==
|
||||||
|
* (filter_id & filter_mask)` и отбрасывает Standard и Remote кадры.
|
||||||
|
*/
|
||||||
|
uint8_t CanSensorStm32F1_Start(CanSensorStm32F1_Port *port,
|
||||||
|
uint32_t bitrate, uint32_t filter_id,
|
||||||
|
uint32_t filter_mask, uint8_t loopback);
|
||||||
|
|
||||||
|
/** Останавливает CAN1 программным сбросом. */
|
||||||
|
void CanSensorStm32F1_Stop(CanSensorStm32F1_Port *port);
|
||||||
|
|
||||||
|
/** Помещает data-кадр в свободный mailbox. Совместима с CanSensor_Io.send. */
|
||||||
|
uint8_t CanSensorStm32F1_Send(void *context, const CanSensor_Frame *frame);
|
||||||
|
|
||||||
|
/** Забирает один data-кадр из FIFO 0. Совместима с CanSensor_Io.receive. */
|
||||||
|
uint8_t CanSensorStm32F1_Receive(void *context, CanSensor_Frame *frame);
|
||||||
|
|
||||||
|
/** Собирает таблицу callback-функций для CanSensor_Init(). */
|
||||||
|
CanSensor_Io CanSensorStm32F1_MakeIo(CanSensorStm32F1_Port *port);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Обслуживает завершение и тайм-аут передачи без прерываний.
|
||||||
|
* @param now_ms Монотонное время приложения, мс.
|
||||||
|
* @param timeout_ms Предел ожидания ACK; 0 отключает принудительную отмену.
|
||||||
|
*/
|
||||||
|
void CanSensorStm32F1_Task(CanSensorStm32F1_Port *port, uint32_t now_ms,
|
||||||
|
uint32_t timeout_ms);
|
||||||
|
|
||||||
|
/** Возвращает 1 при bus-off или error-passive. */
|
||||||
|
uint8_t CanSensorStm32F1_BusError(const CanSensorStm32F1_Port *port);
|
||||||
|
|
||||||
|
#endif /* CAN_SENSOR_STM32F1_H */
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* @file can_sensor_stm32f1_config.f103.template.h
|
||||||
|
* @brief Шаблон выводов CAN1 для STM32F103 без ремапа.
|
||||||
|
*
|
||||||
|
* Скопируйте файл в include-каталог приложения под именем
|
||||||
|
* can_sensor_stm32f1_config.h. Скорость и фильтр задаются при запуске порта.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CAN_SENSOR_STM32F1_CONFIG_H
|
||||||
|
#define CAN_SENSOR_STM32F1_CONFIG_H
|
||||||
|
|
||||||
|
#include "stm32f10x.h"
|
||||||
|
|
||||||
|
#define CAN_SENSOR_STM32F1_GPIO GPIOA
|
||||||
|
#define CAN_SENSOR_STM32F1_GPIO_CLOCK RCC_APB2ENR_IOPAEN
|
||||||
|
#define CAN_SENSOR_STM32F1_RX_PIN 11U
|
||||||
|
#define CAN_SENSOR_STM32F1_TX_PIN 12U
|
||||||
|
|
||||||
|
#endif /* CAN_SENSOR_STM32F1_CONFIG_H */
|
||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user