atsamd: Add support CANbus on ATSAME5x chips
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
parent
0a9becc391
commit
4b77f29c1b
|
@ -4,8 +4,8 @@ This document describes Klipper's CAN bus support.
|
|||
|
||||
## Device Hardware
|
||||
|
||||
Klipper currently supports CAN on stm32 and rp2040 chips. In addition,
|
||||
the micro-controller chip must be on a board that has a CAN
|
||||
Klipper currently supports CAN on stm32, same5x, and rp2040 chips. In
|
||||
addition, the micro-controller chip must be on a board that has a CAN
|
||||
transceiver.
|
||||
|
||||
To compile for CAN, run `make menuconfig` and select "CAN bus" as the
|
||||
|
|
|
@ -68,6 +68,9 @@ config MACH_SAME51
|
|||
config MACH_SAME54
|
||||
bool
|
||||
select MACH_SAMX5
|
||||
config HAVE_SAMD_CANBUS
|
||||
bool
|
||||
default y if MACH_SAME51 || MACH_SAME54
|
||||
|
||||
config MCU
|
||||
string
|
||||
|
@ -159,6 +162,48 @@ choice
|
|||
config ATSAMD_SERIAL
|
||||
bool "Serial"
|
||||
select SERIAL
|
||||
config ATSAMD_MMENU_CANBUS_PA23_PA22
|
||||
bool "CAN bus (on PA23/PA22)"
|
||||
depends on HAVE_SAMD_CANBUS
|
||||
select CANSERIAL
|
||||
config ATSAMD_MMENU_CANBUS_PA25_PA24
|
||||
bool "CAN bus (on PA25/PA24)"
|
||||
depends on HAVE_SAMD_CANBUS
|
||||
select CANSERIAL
|
||||
config ATSAMD_MMENU_CANBUS_PB13_PB12
|
||||
bool "CAN bus (on PB13/PB12)"
|
||||
depends on HAVE_SAMD_CANBUS
|
||||
select CANSERIAL
|
||||
config ATSAMD_MMENU_CANBUS_PB15_PB14
|
||||
bool "CAN bus (on PB15/PB14)"
|
||||
depends on HAVE_SAMD_CANBUS
|
||||
select CANSERIAL
|
||||
config ATSAMD_USBCANBUS
|
||||
bool "USB to CAN bus bridge"
|
||||
depends on HAVE_SAMD_CANBUS
|
||||
select USBCANBUS
|
||||
endchoice
|
||||
choice
|
||||
prompt "CAN bus interface" if USBCANBUS
|
||||
config ATSAMD_CMENU_CANBUS_PA23_PA22
|
||||
bool "CAN bus (on PA23/PA22)"
|
||||
config ATSAMD_CMENU_CANBUS_PB13_PB12
|
||||
bool "CAN bus (on PB13/PB12)"
|
||||
config ATSAMD_CMENU_CANBUS_PB15_PB14
|
||||
bool "CAN bus (on PB15/PB14)"
|
||||
endchoice
|
||||
|
||||
config ATSAMD_CANBUS_PA23_PA22
|
||||
bool
|
||||
default y if ATSAMD_MMENU_CANBUS_PA23_PA22 || ATSAMD_CMENU_CANBUS_PA23_PA22
|
||||
config ATSAMD_CANBUS_PA25_PA24
|
||||
bool
|
||||
default y if ATSAMD_MMENU_CANBUS_PA25_PA24
|
||||
config ATSAMD_CANBUS_PB13_PB12
|
||||
bool
|
||||
default y if ATSAMD_MMENU_CANBUS_PB13_PB12 || ATSAMD_CMENU_CANBUS_PB13_PB12
|
||||
config ATSAMD_CANBUS_PB15_PB14
|
||||
bool
|
||||
default y if ATSAMD_MMENU_CANBUS_PB15_PB14 || ATSAMD_CMENU_CANBUS_PB15_PB14
|
||||
|
||||
endif
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# Setup the toolchain
|
||||
CROSS_PREFIX=arm-none-eabi-
|
||||
|
||||
dirs-y += src/atsamd src/generic
|
||||
dirs-y += src/atsamd src/generic lib/fast-hash
|
||||
|
||||
MCU := $(shell echo $(CONFIG_MCU) | tr a-z A-Z)
|
||||
|
||||
|
@ -11,7 +11,7 @@ CFLAGS-$(CONFIG_MACH_SAMD21) += -mcpu=cortex-m0plus -Ilib/samd21/samd21a/include
|
|||
CFLAGS-$(CONFIG_MACH_SAMD51) += -Ilib/samd51/samd51a/include
|
||||
CFLAGS-$(CONFIG_MACH_SAME51) += -Ilib/same51/include
|
||||
CFLAGS-$(CONFIG_MACH_SAMX5) += -mcpu=cortex-m4 -Ilib/same54/include
|
||||
CFLAGS += $(CFLAGS-y) -D__$(MCU)__ -mthumb -Ilib/cmsis-core
|
||||
CFLAGS += $(CFLAGS-y) -D__$(MCU)__ -mthumb -Ilib/cmsis-core -Ilib/fast-hash
|
||||
|
||||
CFLAGS_klipper.elf += --specs=nano.specs --specs=nosys.specs
|
||||
CFLAGS_klipper.elf += -T $(OUT)src/generic/armcm_link.ld
|
||||
|
@ -22,6 +22,10 @@ src-y += atsamd/main.c atsamd/gpio.c generic/crc16_ccitt.c
|
|||
src-y += generic/armcm_boot.c generic/armcm_irq.c generic/armcm_reset.c
|
||||
src-$(CONFIG_USBSERIAL) += atsamd/usbserial.c atsamd/chipid.c generic/usb_cdc.c
|
||||
src-$(CONFIG_SERIAL) += atsamd/serial.c generic/serial_irq.c
|
||||
canbus-src-y := generic/canserial.c ../lib/fast-hash/fasthash.c
|
||||
canbus-src-y += atsamd/fdcan.c atsamd/chipid.c
|
||||
src-$(CONFIG_USBCANBUS) += $(canbus-src-y) atsamd/usbserial.c generic/usb_canbus.c
|
||||
src-$(CONFIG_CANSERIAL) += $(canbus-src-y) generic/canbus.c
|
||||
src-$(CONFIG_HAVE_GPIO_ADC) += atsamd/adc.c
|
||||
src-$(CONFIG_HAVE_GPIO_I2C) += atsamd/i2c.c
|
||||
src-$(CONFIG_HAVE_GPIO_SPI) += atsamd/spi.c
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
// This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
|
||||
#include "autoconf.h" // CONFIG_USB_SERIAL_NUMBER_CHIPID
|
||||
#include "generic/canserial.h" // canserial_set_uuid
|
||||
#include "generic/usb_cdc.h" // usb_fill_serial
|
||||
#include "generic/usbstd.h" // usb_string_descriptor
|
||||
#include "sched.h" // DECL_INIT
|
||||
|
@ -25,7 +26,7 @@ usbserial_get_serialid(void)
|
|||
void
|
||||
chipid_init(void)
|
||||
{
|
||||
if (!CONFIG_USB_SERIAL_NUMBER_CHIPID)
|
||||
if (!CONFIG_USB_SERIAL_NUMBER_CHIPID && !CONFIG_CANBUS)
|
||||
return;
|
||||
|
||||
uint32_t id[4];
|
||||
|
@ -40,6 +41,11 @@ chipid_init(void)
|
|||
id[2] = *(uint32_t*)0x00806014;
|
||||
id[3] = *(uint32_t*)0x00806018;
|
||||
}
|
||||
|
||||
if (CONFIG_USB_SERIAL_NUMBER_CHIPID)
|
||||
usb_fill_serial(&cdc_chipid.desc, ARRAY_SIZE(cdc_chipid.data), id);
|
||||
|
||||
if (CONFIG_CANBUS)
|
||||
canserial_set_uuid((void*)id, CHIP_UID_LEN);
|
||||
}
|
||||
DECL_INIT(chipid_init);
|
||||
|
|
|
@ -35,12 +35,14 @@ route_pclock(uint32_t pclk_id, uint32_t clkgen_id)
|
|||
|
||||
// Enable a peripheral clock and power to that peripheral
|
||||
void
|
||||
enable_pclock(uint32_t pclk_id, uint32_t pm_id)
|
||||
enable_pclock(uint32_t pclk_id, int32_t pm_id)
|
||||
{
|
||||
route_pclock(pclk_id, CLKGEN_MAIN);
|
||||
if (pm_id >= 0) {
|
||||
uint32_t pm_port = pm_id / 32, pm_bit = 1 << (pm_id % 32);
|
||||
(&PM->APBAMASK.reg)[pm_port] |= pm_bit;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the frequency of the given peripheral clock
|
||||
uint32_t
|
||||
|
|
|
@ -0,0 +1,293 @@
|
|||
// CANbus support on atsame51 chips
|
||||
//
|
||||
// Copyright (C) 2021-2022 Kevin O'Connor <kevin@koconnor.net>
|
||||
// Copyright (C) 2019 Eug Krashtan <eug.krashtan@gmail.com>
|
||||
// Copyright (C) 2020 Pontus Borg <glpontus@gmail.com>
|
||||
//
|
||||
// This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
|
||||
#include "command.h" // DECL_CONSTANT_STR
|
||||
#include "generic/armcm_boot.h" // armcm_enable_irq
|
||||
#include "generic/canbus.h" // canbus_notify_tx
|
||||
#include "generic/canserial.h" // CANBUS_ID_ADMIN
|
||||
#include "internal.h" // enable_pclock
|
||||
#include "sched.h" // DECL_INIT
|
||||
|
||||
|
||||
/****************************************************************
|
||||
* Pin configuration
|
||||
****************************************************************/
|
||||
|
||||
#if CONFIG_ATSAMD_CANBUS_PA23_PA22
|
||||
DECL_CONSTANT_STR("RESERVE_PINS_CAN", "PA23,PA22");
|
||||
#define GPIO_Rx GPIO('A', 23)
|
||||
#define GPIO_Tx GPIO('A', 22)
|
||||
#define CANx_GCLK_ID CAN0_GCLK_ID
|
||||
#elif CONFIG_ATSAMD_CANBUS_PA25_PA24
|
||||
DECL_CONSTANT_STR("RESERVE_PINS_CAN", "PA25,PA24");
|
||||
#define GPIO_Rx GPIO('A', 25)
|
||||
#define GPIO_Tx GPIO('A', 24)
|
||||
#define CANx_GCLK_ID CAN0_GCLK_ID
|
||||
#elif CONFIG_ATSAMD_CANBUS_PB13_PB12
|
||||
DECL_CONSTANT_STR("RESERVE_PINS_CAN", "PB13,PB12");
|
||||
#define GPIO_Rx GPIO('B', 13)
|
||||
#define GPIO_Tx GPIO('B', 12)
|
||||
#define CANx_GCLK_ID CAN1_GCLK_ID
|
||||
#elif CONFIG_ATSAMD_CANBUS_PB15_PB14
|
||||
DECL_CONSTANT_STR("RESERVE_PINS_CAN", "PB15,PB14");
|
||||
#define GPIO_Rx GPIO('B', 15)
|
||||
#define GPIO_Tx GPIO('B', 14)
|
||||
#define CANx_GCLK_ID CAN1_GCLK_ID
|
||||
#endif
|
||||
|
||||
#if CANx_GCLK_ID == CAN0_GCLK_ID
|
||||
#define CAN_FUNCTION 'I'
|
||||
#define CANx CAN0
|
||||
#define CANx_IRQn CAN0_IRQn
|
||||
#else
|
||||
#define CAN_FUNCTION 'H'
|
||||
#define CANx CAN1
|
||||
#define CANx_IRQn CAN1_IRQn
|
||||
#endif
|
||||
|
||||
|
||||
/****************************************************************
|
||||
* Message ram layout
|
||||
****************************************************************/
|
||||
|
||||
struct fdcan_fifo {
|
||||
uint32_t id_section;
|
||||
uint32_t dlc_section;
|
||||
uint32_t data[64 / 4];
|
||||
};
|
||||
|
||||
#define FDCAN_XTD (1<<30)
|
||||
#define FDCAN_RTR (1<<29)
|
||||
|
||||
struct fdcan_msg_ram {
|
||||
uint32_t FLS[28]; // Filter list standard
|
||||
uint32_t FLE[16]; // Filter list extended
|
||||
struct fdcan_fifo RXF0[3];
|
||||
struct fdcan_fifo RXF1[3];
|
||||
uint32_t TEF[6]; // Tx event FIFO
|
||||
struct fdcan_fifo TXFIFO[3];
|
||||
};
|
||||
|
||||
// Message ram is in regular memory
|
||||
static struct fdcan_msg_ram MSG_RAM;
|
||||
|
||||
|
||||
/****************************************************************
|
||||
* CANbus code
|
||||
****************************************************************/
|
||||
|
||||
#define FDCAN_IE_TC (CAN_IE_TCE | CAN_IE_TCFE | CAN_IE_TFEE)
|
||||
|
||||
// Transmit a packet
|
||||
int
|
||||
canbus_send(struct canbus_msg *msg)
|
||||
{
|
||||
uint32_t txfqs = CANx->TXFQS.reg;
|
||||
if (txfqs & CAN_TXFQS_TFQF)
|
||||
// No space in transmit fifo - wait for irq
|
||||
return -1;
|
||||
|
||||
uint32_t w_index = ((txfqs & CAN_TXFQS_TFQPI_Msk) >> CAN_TXFQS_TFQPI_Pos);
|
||||
struct fdcan_fifo *txfifo = &MSG_RAM.TXFIFO[w_index];
|
||||
uint32_t ids;
|
||||
if (msg->id & CANMSG_ID_EFF)
|
||||
ids = (msg->id & 0x1fffffff) | FDCAN_XTD;
|
||||
else
|
||||
ids = (msg->id & 0x7ff) << 18;
|
||||
ids |= msg->id & CANMSG_ID_RTR ? FDCAN_RTR : 0;
|
||||
txfifo->id_section = ids;
|
||||
txfifo->dlc_section = (msg->dlc & 0x0f) << 16;
|
||||
txfifo->data[0] = msg->data32[0];
|
||||
txfifo->data[1] = msg->data32[1];
|
||||
barrier();
|
||||
CANx->TXBAR.reg = ((uint32_t)1 << w_index);
|
||||
return CANMSG_DATA_LEN(msg);
|
||||
}
|
||||
|
||||
static void
|
||||
can_filter(uint32_t index, uint32_t id)
|
||||
{
|
||||
MSG_RAM.FLS[index] = ((0x2 << 30) // Classic filter
|
||||
| (0x1 << 27) // Store in Rx FIFO 0 if filter matches
|
||||
| (id << 16)
|
||||
| 0x7FF); // mask all enabled
|
||||
}
|
||||
|
||||
// Setup the receive packet filter
|
||||
void
|
||||
canbus_set_filter(uint32_t id)
|
||||
{
|
||||
if (!CONFIG_CANBUS_FILTER)
|
||||
return;
|
||||
/* Request initialisation */
|
||||
CANx->CCCR.reg |= CAN_CCCR_INIT;
|
||||
/* Wait the acknowledge */
|
||||
while (!(CANx->CCCR.reg & CAN_CCCR_INIT))
|
||||
;
|
||||
/* Enable configuration change */
|
||||
CANx->CCCR.reg |= CAN_CCCR_CCE;
|
||||
|
||||
// Load filter
|
||||
can_filter(0, CANBUS_ID_ADMIN);
|
||||
can_filter(1, id);
|
||||
can_filter(2, id + 1);
|
||||
|
||||
uint32_t flssa = (uint32_t)MSG_RAM.FLS - CAN0_MSG_RAM_ADDR;
|
||||
CANx->SIDFC.reg = flssa | ((id ? 3 : 1) << CAN_SIDFC_LSS_Pos);
|
||||
CANx->GFC.reg = 0x02 << CAN_GFC_ANFS_Pos;
|
||||
|
||||
/* Leave the initialisation mode for the filter */
|
||||
barrier();
|
||||
CANx->CCCR.reg &= ~CAN_CCCR_CCE;
|
||||
CANx->CCCR.reg &= ~CAN_CCCR_INIT;
|
||||
}
|
||||
|
||||
// This function handles CAN global interrupts
|
||||
void
|
||||
CAN_IRQHandler(void)
|
||||
{
|
||||
uint32_t ir = CANx->IR.reg;
|
||||
|
||||
if (ir & CAN_IE_RF0NE) {
|
||||
CANx->IR.reg = CAN_IE_RF0NE;
|
||||
|
||||
uint32_t rxf0s = CANx->RXF0S.reg;
|
||||
if (rxf0s & CAN_RXF0S_F0FL_Msk) {
|
||||
// Read and ack data packet
|
||||
uint32_t idx = (rxf0s & CAN_RXF0S_F0GI_Msk) >> CAN_RXF0S_F0GI_Pos;
|
||||
struct fdcan_fifo *rxf0 = &MSG_RAM.RXF0[idx];
|
||||
uint32_t ids = rxf0->id_section;
|
||||
struct canbus_msg msg;
|
||||
if (ids & FDCAN_XTD)
|
||||
msg.id = (ids & 0x1fffffff) | CANMSG_ID_EFF;
|
||||
else
|
||||
msg.id = (ids >> 18) & 0x7ff;
|
||||
msg.id |= ids & FDCAN_RTR ? CANMSG_ID_RTR : 0;
|
||||
msg.dlc = (rxf0->dlc_section >> 16) & 0x0f;
|
||||
msg.data32[0] = rxf0->data[0];
|
||||
msg.data32[1] = rxf0->data[1];
|
||||
barrier();
|
||||
CANx->RXF0A.reg = idx;
|
||||
|
||||
// Process packet
|
||||
canbus_process_data(&msg);
|
||||
}
|
||||
}
|
||||
if (ir & FDCAN_IE_TC) {
|
||||
// Tx
|
||||
CANx->IR.reg = FDCAN_IE_TC;
|
||||
canbus_notify_tx();
|
||||
}
|
||||
}
|
||||
|
||||
static inline const uint32_t
|
||||
make_btr(uint32_t sjw, // Sync jump width, ... hmm
|
||||
uint32_t time_seg1, // time segment before sample point, 1 .. 16
|
||||
uint32_t time_seg2, // time segment after sample point, 1 .. 8
|
||||
uint32_t brp) // Baud rate prescaler, 1 .. 1024
|
||||
{
|
||||
return (((uint32_t)(sjw-1)) << CAN_NBTP_NSJW_Pos
|
||||
| ((uint32_t)(time_seg1-1)) << CAN_NBTP_NTSEG1_Pos
|
||||
| ((uint32_t)(time_seg2-1)) << CAN_NBTP_NTSEG2_Pos
|
||||
| ((uint32_t)(brp - 1)) << CAN_NBTP_NBRP_Pos);
|
||||
}
|
||||
|
||||
static inline const uint32_t
|
||||
compute_btr(uint32_t pclock, uint32_t bitrate)
|
||||
{
|
||||
/*
|
||||
Some equations:
|
||||
Tpclock = 1 / pclock
|
||||
Tq = brp * Tpclock
|
||||
Tbs1 = Tq * TS1
|
||||
Tbs2 = Tq * TS2
|
||||
NominalBitTime = Tq + Tbs1 + Tbs2
|
||||
BaudRate = 1/NominalBitTime
|
||||
Bit value sample point is after Tq+Tbs1. Ideal sample point
|
||||
is at 87.5% of NominalBitTime
|
||||
Use the lowest brp where ts1 and ts2 are in valid range
|
||||
*/
|
||||
|
||||
uint32_t bit_clocks = pclock / bitrate; // clock ticks per bit
|
||||
|
||||
uint32_t sjw = 2;
|
||||
uint32_t qs;
|
||||
// Find number of time quantas that gives us the exact wanted bit time
|
||||
for (qs = 18; qs > 9; qs--) {
|
||||
// check that bit_clocks / quantas is an integer
|
||||
uint32_t brp_rem = bit_clocks % qs;
|
||||
if (brp_rem == 0)
|
||||
break;
|
||||
}
|
||||
uint32_t brp = bit_clocks / qs;
|
||||
uint32_t time_seg2 = qs / 8; // sample at ~87.5%
|
||||
uint32_t time_seg1 = qs - (1 + time_seg2);
|
||||
|
||||
return make_btr(sjw, time_seg1, time_seg2, brp);
|
||||
}
|
||||
|
||||
void
|
||||
can_init(void)
|
||||
{
|
||||
if (!CONFIG_USB) {
|
||||
// The FDCAN peripheral only seems to run if at least one
|
||||
// other peripheral is also enabled.
|
||||
enable_pclock(USB_GCLK_ID, ID_USB);
|
||||
USB->DEVICE.CTRLA.reg = USB_CTRLA_ENABLE;
|
||||
}
|
||||
|
||||
enable_pclock(CANx_GCLK_ID, -1);
|
||||
|
||||
gpio_peripheral(GPIO_Rx, CAN_FUNCTION, 1);
|
||||
gpio_peripheral(GPIO_Tx, CAN_FUNCTION, 0);
|
||||
|
||||
uint32_t pclock = get_pclock_frequency(CANx_GCLK_ID);
|
||||
|
||||
uint32_t btr = compute_btr(pclock, CONFIG_CANBUS_FREQUENCY);
|
||||
|
||||
/*##-1- Configure the CAN #######################################*/
|
||||
|
||||
/* Exit from sleep mode */
|
||||
CANx->CCCR.reg &= ~CAN_CCCR_CSR;
|
||||
/* Wait the acknowledge */
|
||||
while (CANx->CCCR.reg & CAN_CCCR_CSA)
|
||||
;
|
||||
/* Request initialisation */
|
||||
CANx->CCCR.reg |= CAN_CCCR_INIT;
|
||||
/* Wait the acknowledge */
|
||||
while (!(CANx->CCCR.reg & CAN_CCCR_INIT))
|
||||
;
|
||||
/* Enable configuration change */
|
||||
CANx->CCCR.reg |= CAN_CCCR_CCE;
|
||||
|
||||
/* Disable protocol exception handling */
|
||||
CANx->CCCR.reg |= CAN_CCCR_PXHD;
|
||||
|
||||
CANx->NBTP.reg = btr;
|
||||
|
||||
/* Setup message RAM addresses */
|
||||
uint32_t f0sa = (uint32_t)MSG_RAM.RXF0 - CAN0_MSG_RAM_ADDR;
|
||||
CANx->RXF0C.reg = f0sa | (ARRAY_SIZE(MSG_RAM.RXF0) << CAN_RXF0C_F0S_Pos);
|
||||
CANx->RXESC.reg = (7 << CAN_RXESC_F1DS_Pos) | (7 << CAN_RXESC_F0DS_Pos);
|
||||
uint32_t tbsa = (uint32_t)MSG_RAM.TXFIFO - CAN0_MSG_RAM_ADDR;
|
||||
CANx->TXBC.reg = tbsa | (ARRAY_SIZE(MSG_RAM.TXFIFO) << CAN_TXBC_TFQS_Pos);
|
||||
CANx->TXESC.reg = 7 << CAN_TXESC_TBDS_Pos;
|
||||
|
||||
/* Leave the initialisation mode */
|
||||
CANx->CCCR.reg &= ~CAN_CCCR_CCE;
|
||||
CANx->CCCR.reg &= ~CAN_CCCR_INIT;
|
||||
|
||||
/*##-2- Configure the CAN Filter #######################################*/
|
||||
canbus_set_filter(0);
|
||||
|
||||
/*##-3- Configure Interrupts #################################*/
|
||||
armcm_enable_irq(CAN_IRQHandler, CANx_IRQn, 1);
|
||||
CANx->ILE.reg = CAN_ILE_EINT0;
|
||||
CANx->IE.reg = CAN_IE_RF0NE | FDCAN_IE_TC;
|
||||
}
|
||||
DECL_INIT(can_init);
|
|
@ -22,7 +22,7 @@
|
|||
#define GET_FUSE(REG) \
|
||||
((*((uint32_t*)(REG##_ADDR)) & (REG##_Msk)) >> (REG##_Pos))
|
||||
|
||||
void enable_pclock(uint32_t pclk_id, uint32_t pm_id);
|
||||
void enable_pclock(uint32_t pclk_id, int32_t pm_id);
|
||||
uint32_t get_pclock_frequency(uint32_t pclk_id);
|
||||
void gpio_peripheral(uint32_t gpio, char ptype, int32_t pull_up);
|
||||
|
||||
|
|
|
@ -39,12 +39,14 @@ route_pclock(uint32_t pclk_id, uint32_t clkgen_id)
|
|||
|
||||
// Enable a peripheral clock and power to that peripheral
|
||||
void
|
||||
enable_pclock(uint32_t pclk_id, uint32_t pm_id)
|
||||
enable_pclock(uint32_t pclk_id, int32_t pm_id)
|
||||
{
|
||||
route_pclock(pclk_id, CLKGEN_48M);
|
||||
if (pm_id >= 0) {
|
||||
uint32_t pm_port = pm_id / 32, pm_bit = 1 << (pm_id % 32);
|
||||
(&MCLK->APBAMASK.reg)[pm_port] |= pm_bit;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the frequency of the given peripheral clock
|
||||
uint32_t
|
||||
|
|
Loading…
Reference in New Issue