git subrepo clone https://github.com/libopencm3/libopencm3
subrepo: subdir: "libopencm3" merged: "f5813a54" upstream: origin: "https://github.com/libopencm3/libopencm3" branch: "master" commit: "f5813a54" git-subrepo: version: "0.4.3" origin: "???" commit: "???"
This commit is contained in:
47
libopencm3/lib/pac55xx/Makefile
Normal file
47
libopencm3/lib/pac55xx/Makefile
Normal file
@@ -0,0 +1,47 @@
|
||||
##
|
||||
## This file is part of the libopencm3 project.
|
||||
##
|
||||
## Copyright (C) 2019 Brian Viele <vielster@allocor.tech>
|
||||
##
|
||||
## This library is free software: you can redistribute it and/or modify
|
||||
## it under the terms of the GNU Lesser General Public License as published by
|
||||
## the Free Software Foundation, either version 3 of the License, or
|
||||
## (at your option) any later version.
|
||||
##
|
||||
## This library is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU Lesser General Public License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU Lesser General Public License
|
||||
## along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
##
|
||||
## The PAC55xx by Qorvo (formerly ActiveSemi) is a Cortex-M4F based device
|
||||
## which is specialized for motor control and PSC applications.
|
||||
|
||||
LIBNAME = libopencm3_pac55xx
|
||||
SRCLIBDIR ?= ..
|
||||
FP_FLAGS ?= -mfloat-abi=hard -mfpu=fpv4-sp-d16
|
||||
CC = $(PREFIX)gcc
|
||||
AR = $(PREFIX)ar
|
||||
TGT_CFLAGS = -Os \
|
||||
-Wall -Wextra -Wimplicit-function-declaration \
|
||||
-Wredundant-decls -Wmissing-prototypes -Wstrict-prototypes \
|
||||
-Wundef -Wshadow \
|
||||
-I../../include -fno-common \
|
||||
-mcpu=cortex-m4 -mthumb $(FP_FLAGS) -Wstrict-prototypes \
|
||||
-ffunction-sections -fdata-sections -MD -DPAC55XX
|
||||
TGT_CFLAGS += $(DEBUG_FLAGS)
|
||||
TGT_CFLAGS += $(STANDARD_FLAGS)
|
||||
ARFLAGS = rcs
|
||||
|
||||
OBJS += can.o
|
||||
OBJS += ccs.o
|
||||
OBJS += gpio.o
|
||||
OBJS += memctl.o
|
||||
OBJS += usart.o
|
||||
|
||||
VPATH += ../cm3
|
||||
|
||||
|
||||
include ../Makefile.include
|
||||
460
libopencm3/lib/pac55xx/can.c
Normal file
460
libopencm3/lib/pac55xx/can.c
Normal file
@@ -0,0 +1,460 @@
|
||||
/**
|
||||
* @addtogroup can_api CAN Peripheral API
|
||||
* @ingroup peripheral_apis
|
||||
* @brief <b>PAC55xxxx CAN Driver</b>
|
||||
* @author @htmlonly © @endhtmlonly 2020 Kevin Stefanik <kevin@allocor.tech>
|
||||
* @date February 13, 2020
|
||||
*
|
||||
* This library supports the CAN module in the PAC55xx SoC from Qorvo.
|
||||
*
|
||||
* Note: Acceptance Code Mask Register values of 1 indicate the filter is to
|
||||
* ignore the bit. However, standard CAN driver APIs use a positive logic for the
|
||||
* mask. The implementations in this file inverts masks as appropriate to
|
||||
* the mask to make this more portable/intuitive.
|
||||
*
|
||||
* LGPL License Terms @ref lgpl_license
|
||||
*/
|
||||
/*
|
||||
* This file is part of the libopencm3 project.
|
||||
*
|
||||
* This library is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <libopencm3/pac55xx/can.h>
|
||||
#include <libopencm3/cm3/common.h>
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/** @brief CAN Enable
|
||||
Enable the CAN peripheral and its associated FIFOs/counters/interrupts.
|
||||
@param[in] canport Unsigned int32. CAN block register base address.
|
||||
*/
|
||||
void can_enable(uint32_t canport) {
|
||||
CAN_ISR_SR_CMR_MR_CLEAR(canport, CAN_MR_RM);
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/** @brief CAN Disable
|
||||
Disable the CAN peripheral and all associated FIFOs/counters/interrupts.
|
||||
@param[in] canport Unsigned int32. CAN block register base address.
|
||||
*/
|
||||
void can_disable(uint32_t canport) {
|
||||
CAN_ISR_SR_CMR_MR_SET(canport, CAN_MR_RM);
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/** @brief CAN Init
|
||||
Initialize the selected CAN peripheral block.
|
||||
@param[in] canport Unsigned int32. CAN block register base address.
|
||||
@param[in] listen_only bool. Enable listen only mode.
|
||||
@param[in] sjw Unsigned int32. Resynchronization time quanta jump width.
|
||||
@param[in] tseg1 Unsigned int32. Time segment 1 time quanta width.
|
||||
@param[in] tseg2 Unsigned int32. Time segment 2 time quanta width.
|
||||
@param[in] sam3 bool. Use best 2 out of 3 samples.
|
||||
@param[in] brp Unsigned int32. Baud rate prescaler.
|
||||
*/
|
||||
void can_init(uint32_t canport, bool listen_only, uint32_t sjw,
|
||||
uint32_t tseg1, uint32_t tseg2,
|
||||
bool sam3, uint32_t brp) {
|
||||
/* Put CAN module in reset and clear out ISR/SR/CMR/MR */
|
||||
CAN_ISR_SR_CMR_MR(canport) = CAN_MR_RM;
|
||||
/* Setup single filter scheme */
|
||||
CAN_ISR_SR_CMR_MR_SET(canport, CAN_MR_AFM);
|
||||
/* enable listen-only mode */
|
||||
if (listen_only) {
|
||||
CAN_ISR_SR_CMR_MR_SET(canport, CAN_MR_LOM);
|
||||
}
|
||||
|
||||
/* Set Baud Rate Prescaler, sync jump width, tseg1/2 */
|
||||
CAN_BTR1_BTR0_RMC_IMR(canport) = CAN_BTR0_BRP(brp) | CAN_BTR0_SJW(sjw)
|
||||
| CAN_BTR1_TSEG1(tseg1) | CAN_BTR1_TSEG2(tseg2);
|
||||
if (sam3) {
|
||||
/* enable sample bus 3 times */
|
||||
CAN_BTR1_BTR0_RMC_IMR(canport) |= CAN_BTR1_SAM;
|
||||
}
|
||||
|
||||
/* Filter: Accept incoming messages with any identifier */
|
||||
CAN_ACR(canport) = 0;
|
||||
/* Note: when mask bits are 1, the bits are ignored */
|
||||
CAN_AMR(canport) = 0xFFFFFFFFu;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/** @brief CAN Filter Clear
|
||||
Clear the message filters to receive all messages.
|
||||
@param[in] canport Unsigned int32. CAN block register base address.
|
||||
*/
|
||||
void can_filter_clear(uint32_t canport) {
|
||||
/* Filter: Accept incoming messages with any identifier */
|
||||
CAN_ACR(canport) = 0;
|
||||
/* Note: when mask bits are 1, the bits are ignored */
|
||||
CAN_AMR(canport) = 0xFFFFFFFFu;
|
||||
/* Setup single filter scheme */
|
||||
CAN_ISR_SR_CMR_MR_SET(canport, CAN_MR_AFM);
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/** @brief CAN Dual Filter Standard Frame
|
||||
Notes:
|
||||
- Acceptance Code Mask Register values of 1 indicate the filter is to ignore
|
||||
the bit. However standard CAN driver APIs use a positive logic for the mask.
|
||||
So this function inverts the mask to make this more portable/intuitive.
|
||||
- Register definition byte order is opposite what is shown in Rev 1.23 of
|
||||
the PAC55XX Family User Guide. Since both data and ID values cross byte
|
||||
boundaries, the bswap32 function is used to correct for the discrepancy.
|
||||
|
||||
@param[in] canport Unsigned int32. CAN block register base address.
|
||||
@param[in] id1 Unsigned int32. CAN ID 1. Only bits 10:0 are used.
|
||||
@param[in] id1_mask Unsigned int32. CAN ID 1 mask. Only bits 10:0 are used.
|
||||
@param[in] id2 Unsigned int32. CAN ID 2. Only bits 10:0 are used.
|
||||
@param[in] id2_mask Unsigned int32. CAN ID 2 mask. Only bits 10:0 are used.
|
||||
@param[in] db bool. CAN first data byte value.
|
||||
@param[in] db_mask bool. CAN first data byte mask.
|
||||
*/
|
||||
void can_filter_dual(uint32_t canport, uint32_t id1, uint32_t id1_mask,
|
||||
uint32_t id2, uint32_t id2_mask,
|
||||
uint8_t db, uint8_t db_mask) {
|
||||
/* set value */
|
||||
uint32_t word = ((id1 << 21) & CAN_ACR_DUAL_ID1)
|
||||
| ((id2 << 5) & CAN_ACR_DUAL_ID2)
|
||||
| ((db << 12) & CAN_ACR_DUAL_DB_UPPER) | (db & CAN_ACR_DUAL_DB_LOWER);
|
||||
CAN_ACR(canport) = __builtin_bswap32(word);
|
||||
/* set mask */
|
||||
word = ((~id1_mask << 21) & CAN_ACR_DUAL_ID1)
|
||||
| ((~id2_mask << 5) & CAN_ACR_DUAL_ID2)
|
||||
| ((~db_mask << 12) & CAN_ACR_DUAL_DB_UPPER)
|
||||
| ((~db_mask) & CAN_ACR_DUAL_DB_LOWER)
|
||||
| CAN_ACR_DUAL_RTR1 | CAN_ACR_DUAL_RTR2;
|
||||
CAN_AMR(canport) = __builtin_bswap32(word);
|
||||
/* 0: dual filter */
|
||||
CAN_ISR_SR_CMR_MR_CLEAR(canport, CAN_MR_AFM);
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/** @brief CAN Filter Single Standard Frame
|
||||
Notes:
|
||||
- Acceptance Code Mask Register values of 1 indicate the filter is to ignore
|
||||
the bit. However standard CAN driver APIs use a positive logic for the mask.
|
||||
So this function inverts the mask to make this more portable/intuitive.
|
||||
- Register definition byte order is opposite what is shown in Rev 1.23 of
|
||||
the PAC55XX Family User Guide. Since both data and ID values cross byte
|
||||
boundaries, the bswap32 function is used to correct for the discrepancy.
|
||||
|
||||
@param[in] canport Unsigned int32. CAN block register base address.
|
||||
@param[in] id Unsigned int32. CAN ID. Only bits 10:0 are used.
|
||||
@param[in] id_mask Unsigned int32. CAN ID mask. Only bits 10:0 are used.
|
||||
@param[in] db1 bool. CAN first data byte value.
|
||||
@param[in] db1_mask bool. CAN first data byte mask.
|
||||
@param[in] db2 bool. CAN second data byte value.
|
||||
@param[in] db2_mask bool. CAN second data byte mask.
|
||||
*/
|
||||
void can_filter_single_std(uint32_t canport, uint32_t id, uint32_t id_mask,
|
||||
uint8_t db1, uint8_t db1_mask,
|
||||
uint8_t db2, uint8_t db2_mask) {
|
||||
/* set value */
|
||||
uint32_t word = ((id << 21) & CAN_ACR_SINGLE_STD_ID)
|
||||
| ((db1 << 8) & CAN_ACR_SINGLE_STD_DB1)
|
||||
| ((db2 << 0) & CAN_ACR_SINGLE_STD_DB2);
|
||||
CAN_ACR(canport) = __builtin_bswap32(word);
|
||||
/* set mask */
|
||||
word = ((~id_mask << 21) & CAN_ACR_SINGLE_STD_ID)
|
||||
| CAN_ACR_SINGLE_STD_RTR | CAN_ACR_DUAL_DB_UPPER
|
||||
| ((~db1_mask << 8) & CAN_ACR_SINGLE_STD_DB1)
|
||||
| ((~db2_mask << 0) & CAN_ACR_SINGLE_STD_DB2);
|
||||
CAN_AMR(canport) = __builtin_bswap32(word);
|
||||
/* 1: single filter */
|
||||
CAN_ISR_SR_CMR_MR_SET(canport, CAN_MR_AFM);
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/** @brief CAN Filter Single Standard Frame w/RTR set
|
||||
Notes:
|
||||
- Acceptance Code Mask Register values of 1 indicate the filter is to ignore
|
||||
the bit. However standard CAN driver APIs use a positive logic for the mask.
|
||||
So this function inverts the mask to make this more portable/intuitive.
|
||||
- Register definition byte order is opposite what is shown in Rev 1.23 of
|
||||
the PAC55XX Family User Guide. Since both data and ID values cross byte
|
||||
boundaries, the bswap32 function is used to correct for the discrepancy.
|
||||
|
||||
@param[in] canport Unsigned int32. CAN block register base address.
|
||||
@param[in] id Unsigned int32. CAN ID. Only bits 10:0 are used.
|
||||
@param[in] id_mask Unsigned int32. CAN ID mask. Only bits 10:0 are used.
|
||||
@param[in] db1 bool. CAN first data byte value.
|
||||
@param[in] db1_mask bool. CAN first data byte mask.
|
||||
@param[in] db2 bool. CAN second data byte value.
|
||||
@param[in] db2_mask bool. CAN second data byte mask.
|
||||
*/
|
||||
void can_filter_single_std_rtr(uint32_t canport, uint32_t id, uint32_t id_mask,
|
||||
uint8_t db1, uint8_t db1_mask,
|
||||
uint8_t db2, uint8_t db2_mask) {
|
||||
/* set value */
|
||||
uint32_t word = ((id << 21) & CAN_ACR_SINGLE_STD_ID)
|
||||
| CAN_ACR_SINGLE_STD_RTR | ((db1 << 8) & CAN_ACR_SINGLE_STD_DB1)
|
||||
| ((db2 << 0) & CAN_ACR_SINGLE_STD_DB2);
|
||||
CAN_ACR(canport) = __builtin_bswap32(word);
|
||||
/* set mask */
|
||||
word = ((~id_mask << 21) & CAN_ACR_SINGLE_STD_ID)
|
||||
| ((~db1_mask << 8) & CAN_ACR_SINGLE_STD_DB1)
|
||||
| ((~db2_mask << 0) & CAN_ACR_SINGLE_STD_DB2);
|
||||
CAN_AMR(canport) = __builtin_bswap32(word);
|
||||
/* 1: single filter */
|
||||
CAN_ISR_SR_CMR_MR_SET(canport, CAN_MR_AFM);
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/** @brief CAN Filter Single Extended Frame
|
||||
Notes:
|
||||
- Acceptance Code Mask Register values of 1 indicate the filter is to ignore
|
||||
the bit. However standard CAN driver APIs use a positive logic for the mask.
|
||||
So this function inverts the mask to make this more portable/intuitive.
|
||||
- Register definition byte order is opposite what is shown in Rev 1.23 of
|
||||
the PAC55XX Family User Guide. Since both data and ID values cross byte
|
||||
boundaries, the bswap32 function is used to correct for the discrepancy.
|
||||
|
||||
@param[in] canport Unsigned int32. CAN block register base address.
|
||||
@param[in] id Unsigned int32. CAN ID. Only bits 28:0 are used.
|
||||
@param[in] id_mask Unsigned int32. CAN ID mask. Only bits 28:0 are used.
|
||||
*/
|
||||
void can_filter_single_ext(uint32_t canport, uint32_t id, uint32_t id_mask) {
|
||||
/* set value */
|
||||
uint32_t word = ((id << 3) & CAN_ACR_SINGLE_EXT_ID);
|
||||
CAN_ACR(canport) = __builtin_bswap32(word);
|
||||
/* set mask */
|
||||
word = ((~id_mask << 3) & CAN_ACR_SINGLE_EXT_ID) | CAN_ACR_SINGLE_EXT_RTR;
|
||||
CAN_AMR(canport) = __builtin_bswap32(word);
|
||||
/* 1: single filter */
|
||||
CAN_ISR_SR_CMR_MR_SET(canport, CAN_MR_AFM);
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/** @brief CAN Filter Single Extended Frame w/RTR set
|
||||
Notes:
|
||||
- Acceptance Code Mask Register values of 1 indicate the filter is to ignore
|
||||
the bit. However standard CAN driver APIs use a positive logic for the mask.
|
||||
So this function inverts the mask to make this more portable/intuitive.
|
||||
- Register definition byte order is opposite what is shown in Rev 1.23 of
|
||||
the PAC55XX Family User Guide. Since both data and ID values cross byte
|
||||
boundaries, the bswap32 function is used to correct for the discrepancy.
|
||||
|
||||
@param[in] canport Unsigned int32. CAN block register base address.
|
||||
@param[in] id Unsigned int32. CAN ID. Only bits 28:0 are used.
|
||||
@param[in] id_mask Unsigned int32. CAN ID mask. Only bits 28:0 are used.
|
||||
*/
|
||||
void can_filter_single_ext_rtr(uint32_t canport, uint32_t id, uint32_t id_mask) {
|
||||
/* set value */
|
||||
uint32_t word = ((id << 3) & CAN_ACR_SINGLE_EXT_ID) | CAN_ACR_SINGLE_EXT_RTR;
|
||||
CAN_ACR(canport) = __builtin_bswap32(word);
|
||||
/* set mask */
|
||||
word = ((~id_mask << 3) & CAN_ACR_SINGLE_EXT_ID);
|
||||
CAN_AMR(canport) = __builtin_bswap32(word);
|
||||
/* 1: single filter */
|
||||
CAN_ISR_SR_CMR_MR_SET(canport, CAN_MR_AFM);
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/** @brief CAN Enable IRQ
|
||||
@param[in] canport Unsigned int32. CAN block register base address.
|
||||
@param[in] irq Unsigned int8. IRQ bit(s).
|
||||
*/
|
||||
void can_enable_irq(uint32_t canport, uint8_t irq) {
|
||||
/* set to 1 (not masked) to enable */
|
||||
CAN_BTR1_BTR0_RMC_IMR(canport) |= (uint32_t)irq;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/** @brief CAN Disable IRQ
|
||||
@param[in] canport Unsigned int32. CAN block register base address.
|
||||
@param[in] irq Unsigned int8. IRQ bit(s).
|
||||
*/
|
||||
void can_disable_irq(uint32_t canport, uint8_t irq) {
|
||||
/* set to 0 (masked) to disable */
|
||||
CAN_BTR1_BTR0_RMC_IMR(canport) &= ~(uint32_t)irq;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/** @brief CAN Transmit Standard Frame
|
||||
@param[in] canport Unsigned int32. CAN block register base address.
|
||||
@param[in] id Unsigned int32. Message ID bits 10:0 used.
|
||||
@param[in] rtr bool. Remote Request bit value.
|
||||
@param[in] length Unsigned int8. Message payload length.
|
||||
@param[in] data Unsigned int8[]. Message payload data.
|
||||
@returns true if able to transmit, false otherwise.
|
||||
*/
|
||||
bool can_transmit_std(uint32_t canport, uint32_t id, bool rtr, uint8_t length,
|
||||
const uint8_t *data) {
|
||||
/* if TBS is 0, then not ready to transmit */
|
||||
if ((CAN_ISR_SR_CMR_MR(canport) & CAN_SR_TBS) == 0) {
|
||||
return false;
|
||||
}
|
||||
uint32_t word = (length & CAN_BITS_3_0)
|
||||
| (rtr ? BIT6 : 0) /* DLC/RTR/FF ==> 7:0 */
|
||||
| ((id & CAN_BITS_10_3) << 5) /* ID 10:3 ==> 15:8 */
|
||||
| ((id & CAN_BITS_2_0) << 21) /* ID 2:0 ==> 23:21 */
|
||||
| (((length > 0) ? data[0] : 0) << 24);
|
||||
CAN_TXBUF(canport) = word;
|
||||
|
||||
if (length > 1) {
|
||||
word = (data[1] << 0) | (data[2] << 8)
|
||||
| (data[3] << 16) | (data[4] << 24);
|
||||
CAN_TXBUF(canport) = word;
|
||||
}
|
||||
|
||||
if (length > 5) {
|
||||
word = (data[5] << 0) | (data[6] << 8) | (data[7] << 16);
|
||||
CAN_TXBUF(canport) = word;
|
||||
}
|
||||
|
||||
/* Request transmit */
|
||||
CAN_ISR_SR_CMR_MR_SET(canport, CAN_CMR_TR);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/** @brief CAN Transmit Extended Frame
|
||||
@param[in] canport Unsigned int32. CAN block register base address.
|
||||
@param[in] id Unsigned int32. Message ID bits 28:0 used.
|
||||
@param[in] rtr bool. Remote Request bit value.
|
||||
@param[in] length Unsigned int8. Message payload length, 0-8.
|
||||
@param[in] data Unsigned int8[]. Message payload data.
|
||||
@returns true if able to transmit, false otherwise.
|
||||
*/
|
||||
bool can_transmit_ext(uint32_t canport, uint32_t id, bool rtr, uint8_t length,
|
||||
const uint8_t *data) {
|
||||
/* if TBS is 0, then not ready to transmit */
|
||||
if ((CAN_ISR_SR_CMR_MR(canport) & CAN_SR_TBS) == 0) {
|
||||
return false;
|
||||
}
|
||||
uint32_t word = (length & CAN_BITS_3_0)
|
||||
| (rtr ? BIT6 : 0) | BIT7 /* DLC/RTR/FF ==> 7:0 */
|
||||
| ((id & CAN_BITS_28_21) >> 13) /* ID 28:21 ==> 15:8 */
|
||||
| ((id & CAN_BITS_20_13) << 3) /* ID 20:13 ==> 23:16 */
|
||||
| ((id & CAN_BITS_12_5) << 19); /* ID 12:5 ==> 31:24 */
|
||||
CAN_TXBUF(canport) = word; /* write first 32-bit word to FIFO */
|
||||
|
||||
word = ((id & CAN_BITS_4_0) << 3); /* ID 4:0 ==> 7:3 */
|
||||
if (length > 0) {
|
||||
word |= (data[0] << 8) | (data[1] << 16) | (data[2] << 24);
|
||||
}
|
||||
/* for extended frame, always write second 32-bit word to FIFO */
|
||||
CAN_TXBUF(canport) = word;
|
||||
if (length > 3) {
|
||||
word = (data[3] << 0) | (data[4] << 8)
|
||||
| (data[5] << 16) | (data[6] << 24);
|
||||
CAN_TXBUF(canport) = word;
|
||||
}
|
||||
if (length > 7) {
|
||||
word = data[7];
|
||||
CAN_TXBUF(canport) = word;
|
||||
}
|
||||
/* Request transmit */
|
||||
CAN_ISR_SR_CMR_MR_SET(canport, CAN_CMR_TR);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/** @brief CAN Abort Transmit
|
||||
Aborts the current transmission.
|
||||
|
||||
@param[in] canport Unsigned int32. CAN block register base address.
|
||||
*/
|
||||
void can_abort_transmit(uint32_t canport) {
|
||||
CAN_ISR_SR_CMR_MR_SET(canport, CAN_CMR_AT);
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/** @brief CAN Receive Message
|
||||
If no data is in the RX buffer, id and length are set to 0.
|
||||
|
||||
@param[in] canport Unsigned int32. CAN block register base address.
|
||||
@param[out] id Unsigned int32 pointer. Message ID.
|
||||
@param[out] ext bool pointer. The message ID is extended.
|
||||
@param[out] rtr bool pointer. Remote Request bit value.
|
||||
@param[out] length Unsigned int8 pointer. Length of message payload.
|
||||
@param[out] data Unsigned int8[]. Message payload data, min length 8.
|
||||
*/
|
||||
void can_receive(uint32_t canport, uint32_t *id, bool *ext, bool *rtr, uint8_t *length,
|
||||
uint8_t *data) {
|
||||
if ((CAN_ISR_SR_CMR_MR(canport) & CAN_ISR_RI) == 0 || CAN_RMC(canport) == 0) {
|
||||
*id = 0;
|
||||
*length = 0;
|
||||
return; /* empty RX FIFO */
|
||||
}
|
||||
uint32_t can_buffer = CAN_RXBUF(canport); /* read 32-bit word */
|
||||
uint8_t rx_length = can_buffer & CAN_BITS_3_0;
|
||||
bool is_extended = can_buffer & BIT7;
|
||||
if (ext) {
|
||||
*ext = is_extended;
|
||||
}
|
||||
if (rtr) {
|
||||
*rtr = can_buffer & BIT6;
|
||||
}
|
||||
if (length) {
|
||||
*length = rx_length;
|
||||
}
|
||||
uint32_t _id;
|
||||
if (is_extended) {
|
||||
/* Parse extended message ID from RXBUF */
|
||||
_id = ((can_buffer & CAN_BITS_15_8) << 13) /* ID 28:21 <== 15:8 */
|
||||
| ((can_buffer & CAN_BITS_23_16) >> 3) /* ID 20:13 <== 23:16 */
|
||||
| ((can_buffer & CAN_BITS_31_24) >> 19); /* ID 12:5 <== 31:24 */
|
||||
can_buffer = CAN_RXBUF(canport);
|
||||
_id |= ((can_buffer & CAN_BITS_7_3) >> 3); /* ID 4:0 <== 7:3 */
|
||||
|
||||
/* Parse extended message data from RXBUF */
|
||||
data[0] = can_buffer >> 8;
|
||||
data[1] = can_buffer >> 16;
|
||||
data[2] = can_buffer >> 24;
|
||||
if (rx_length > 3) {
|
||||
can_buffer = CAN_RXBUF(canport);
|
||||
data[3] = can_buffer;
|
||||
data[4] = can_buffer >> 8;
|
||||
data[5] = can_buffer >> 16;
|
||||
data[6] = can_buffer >> 24;
|
||||
}
|
||||
if (rx_length > 7) {
|
||||
can_buffer = CAN_RXBUF(canport);
|
||||
data[7] = can_buffer;
|
||||
}
|
||||
} else {
|
||||
/* Parse standard message ID from RXBUF */
|
||||
_id = ((can_buffer & CAN_BITS_15_8) >> 5) /* ID 10:3 <== 15:8 */
|
||||
| ((can_buffer & CAN_BITS_23_21) >> 21); /* ID 2:0 <== 23:21 */
|
||||
/* Parse standard message data from RXBUF */
|
||||
data[0] = can_buffer >> 24;
|
||||
if (rx_length > 1) {
|
||||
can_buffer = CAN_RXBUF(canport);
|
||||
data[1] = can_buffer;
|
||||
data[2] = can_buffer >> 8;
|
||||
data[3] = can_buffer >> 16;
|
||||
data[4] = can_buffer >> 24;
|
||||
if (rx_length > 5) {
|
||||
/* buffer contains data5,data6,data7 */
|
||||
can_buffer = CAN_RXBUF(canport);
|
||||
data[5] = can_buffer;
|
||||
data[6] = can_buffer >> 8;
|
||||
data[7] = can_buffer >> 16;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (id) {
|
||||
*id = _id;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write 1 to acknowledge/clear the interrupt
|
||||
* Note: ensure not to let the other interrupt masks be written as 1, so as
|
||||
* to avoid acknowledging them.
|
||||
* Note: CAN_ISR_RI is already high, but we still write '1' to it to clear it.
|
||||
*/
|
||||
CAN_ISR_ACKNOWLEDGE(canport, CAN_ISR_RI);
|
||||
return;
|
||||
}
|
||||
261
libopencm3/lib/pac55xx/ccs.c
Normal file
261
libopencm3/lib/pac55xx/ccs.c
Normal file
@@ -0,0 +1,261 @@
|
||||
/**
|
||||
* @brief <b>PAC55xxxx CCS Driver</b>
|
||||
* @author @htmlonly © @endhtmlonly 2020 Kevin Stefanik <kevin@allocor.tech>
|
||||
* @date March 7, 2020
|
||||
*
|
||||
* This library supports the CCS module in the PAC55xx SoC from Qorvo.
|
||||
*
|
||||
* LGPL License Terms @ref lgpl_license
|
||||
*/
|
||||
/*
|
||||
* This file is part of the libopencm3 project.
|
||||
*
|
||||
* This library is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <libopencm3/pac55xx/ccs.h>
|
||||
#include <libopencm3/pac55xx/memorymap.h>
|
||||
#include <libopencm3/pac55xx/memctl.h>
|
||||
#include <libopencm3/cm3/assert.h>
|
||||
|
||||
static volatile uint32_t ccs_extclk_frequency = 0;
|
||||
static volatile uint32_t ccs_frclk_frequency = CCS_ROSC_FREQ;
|
||||
static volatile uint32_t ccs_sclk_frequency = CCS_ROSC_FREQ;
|
||||
static volatile uint32_t ccs_pll_clk_frequency = 0;
|
||||
static volatile uint32_t ccs_hclk_frequency = CCS_ROSC_FREQ;
|
||||
static volatile uint32_t ccs_aclk_frequency = CCS_ROSC_FREQ;
|
||||
static volatile uint32_t ccs_pclk_frequency = CCS_ROSC_FREQ;
|
||||
|
||||
void ccs_frclkmux_select(uint32_t sel) {
|
||||
CCSCTL = (CCSCTL & ~CCS_CTL_FRCLKMUXSEL(CCS_CTL_FRCLKMUXSEL_MASK)) | CCS_CTL_FRCLKMUXSEL(sel);
|
||||
}
|
||||
void ccs_rosc_enable(void) {
|
||||
CCSCTL |= CCS_CTL_ROSCEN;
|
||||
}
|
||||
void ccs_rosc_disable(void) {
|
||||
CCSCTL &= ~CCS_CTL_ROSCEN;
|
||||
}
|
||||
void ccs_sclkmux_select_frclk(void) {
|
||||
CCSCTL &= ~CCS_CTL_SCLKMUXSEL;
|
||||
}
|
||||
void ccs_sclkmux_select_pllclk(void) {
|
||||
CCSCTL |= CCS_CTL_SCLKMUXSEL;
|
||||
}
|
||||
void ccs_clkfail_enable(void) {
|
||||
CCSCTL |= CCS_CTL_CLKFAILEN;
|
||||
}
|
||||
void ccs_clkfail_disable(void) {
|
||||
CCSCTL &= ~CCS_CTL_CLKFAILEN;
|
||||
}
|
||||
void ccs_clkfailmux_select_frclk(void) {
|
||||
CCSCTL &= ~CCS_CTL_CLKFAILMUXSEL;
|
||||
}
|
||||
void ccs_clkfailmux_select_pllclk(void) {
|
||||
CCSCTL |= CCS_CTL_CLKFAILMUXSEL;
|
||||
}
|
||||
void ccs_ldo_enable(void) {
|
||||
CCSCTL |= CCS_CTL_LDOEN;
|
||||
}
|
||||
void ccs_ldo_disable(void) {
|
||||
CCSCTL &= ~CCS_CTL_LDOEN;
|
||||
}
|
||||
void ccs_pclk_enable(void) {
|
||||
CCSCTL |= CCS_CTL_PCLKEN;
|
||||
}
|
||||
void ccs_pclk_disable(void) {
|
||||
CCSCTL &= ~CCS_CTL_PCLKEN;
|
||||
}
|
||||
void ccs_aclk_enable(void) {
|
||||
CCSCTL |= CCS_CTL_ACLKEN;
|
||||
}
|
||||
void ccs_aclk_disable(void) {
|
||||
CCSCTL &= ~CCS_CTL_ACLKEN;
|
||||
}
|
||||
void ccs_adcclk_enable(void) {
|
||||
CCSCTL |= CCS_CTL_ADCCLKEN;
|
||||
}
|
||||
void ccs_adcclk_disable(void) {
|
||||
CCSCTL &= ~CCS_CTL_ADCCLKEN;
|
||||
}
|
||||
void ccs_stclk_sleep_enable(void) {
|
||||
CCSCTL |= CCS_CTL_STCLKSLPEN;
|
||||
}
|
||||
void ccs_stclk_sleep_disable(void) {
|
||||
CCSCTL &= ~CCS_CTL_STCLKSLPEN;
|
||||
}
|
||||
void ccs_set_pclkdiv(uint32_t div) {
|
||||
CCSCTL = (CCSCTL & ~CCS_CTL_PCLKDIV(8)) | CCS_CTL_PCLKDIV(div);
|
||||
}
|
||||
void ccs_set_aclkdiv(uint32_t div) {
|
||||
CCSCTL = (CCSCTL & ~CCS_CTL_ACLKDIV(8)) | CCS_CTL_ACLKDIV(div);
|
||||
}
|
||||
void ccs_set_hclkdiv(uint32_t div) {
|
||||
CCSCTL = (CCSCTL & ~CCS_CTL_HCLKDIV(8)) | CCS_CTL_HCLKDIV(div);
|
||||
}
|
||||
void ccs_pll_enable(void) {
|
||||
CCSPLLCTL |= CCS_PLLCTL_PLLEN;
|
||||
}
|
||||
void ccs_pll_disable(void) {
|
||||
CCSPLLCTL &= ~CCS_PLLCTL_PLLEN;
|
||||
}
|
||||
bool ccs_pll_locked(void) {
|
||||
return (CCSPLLCTL & CCS_PLLCTL_PLLLOCK) == CCS_PLLCTL_PLLLOCK;
|
||||
}
|
||||
void ccs_pll_bypass_enable(void) {
|
||||
CCSPLLCTL |= CCS_PLLCTL_PLLBP;
|
||||
}
|
||||
void ccs_pll_bypass_disable(void) {
|
||||
CCSPLLCTL &= ~CCS_PLLCTL_PLLBP;
|
||||
}
|
||||
void ccs_pll_set_outdiv(uint32_t div) {
|
||||
CCSPLLCTL = (CCSPLLCTL & ~CCS_PLLCTL_PLLOUTDIV(CCS_PLLCTL_PLLOUTDIV_MASK)) | CCS_PLLCTL_PLLOUTDIV(div);
|
||||
}
|
||||
void ccs_pll_set_indiv(uint32_t div) {
|
||||
if (div <= 15 && div >= 1) {
|
||||
CCSPLLCTL = (CCSPLLCTL & ~CCS_PLLCTL_PLLINDIV(CCS_PLLCTL_PLLINDIV_MASK)) | CCS_PLLCTL_PLLINDIV(div);
|
||||
} else {
|
||||
cm3_assert_not_reached();
|
||||
}
|
||||
}
|
||||
void ccs_pll_set_fbdiv(uint32_t div) {
|
||||
if (div <= 16383 && div >= 4) {
|
||||
CCSPLLCTL = (CCSPLLCTL & ~CCS_PLLCTL_PLLFBDIV(CCS_PLLCTL_PLLFBDIV_MASK)) | CCS_PLLCTL_PLLFBDIV(div);
|
||||
} else {
|
||||
cm3_assert_not_reached();
|
||||
}
|
||||
}
|
||||
void css_pll_config_enable(uint32_t indiv, uint32_t fbdiv, uint32_t outdiv) {
|
||||
ccs_pll_disable();
|
||||
ccs_pll_set_fbdiv(fbdiv);
|
||||
ccs_pll_set_outdiv(outdiv);
|
||||
ccs_pll_set_indiv(indiv);
|
||||
ccs_pll_enable();
|
||||
while (!ccs_pll_locked()) ; /* Wait for PLL lock ~500us */
|
||||
}
|
||||
uint32_t ccs_get_peripheral_clk_freq(uint32_t periph, uint32_t select) {
|
||||
switch (periph) {
|
||||
case ADC_BASE:
|
||||
return ccs_sclk_frequency;
|
||||
case I2C_BASE: /* fall through */
|
||||
case USARTA_BASE: /* fall through */
|
||||
case USARTB_BASE: /* fall through */
|
||||
case USARTC_BASE: /* fall through */
|
||||
case USARTD_BASE: /* fall through */
|
||||
case CAN_BASE: /* fall through */
|
||||
case GPTIMERA_BASE: /* fall through */
|
||||
case GPTIMERB_BASE:
|
||||
return ccs_pclk_frequency;
|
||||
case TIMERA_BASE: /* fall through */
|
||||
case TIMERB_BASE: /* fall through */
|
||||
case TIMERC_BASE: /* fall through */
|
||||
case TIMERD_BASE:
|
||||
return (select == 0) ? ccs_pclk_frequency : ccs_aclk_frequency;
|
||||
case MEMCTL_BASE:
|
||||
return (select == 0) ? CCS_ROSC_FREQ : ccs_hclk_frequency;
|
||||
case WWDT_BASE:
|
||||
return (select == 0) ? ccs_frclk_frequency : CCS_ROSC_FREQ;
|
||||
case RTC_BASE:
|
||||
return ccs_frclk_frequency;
|
||||
case CRC_BASE: /* fall through */
|
||||
case SYS_TICK_BASE:
|
||||
return ccs_hclk_frequency;
|
||||
default:
|
||||
cm3_assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
void ccs_reset_clocks(void) {
|
||||
CCSCTL = CCS_CTL_LDOEN | CCS_CTL_ROSCEN |
|
||||
CCS_CTL_PCLKEN | CCS_CTL_ACLKEN |
|
||||
CCS_CTL_ADCCLKEN | CCS_CTL_STCLKSLPEN;
|
||||
CCSPLLCTL = 0;
|
||||
}
|
||||
|
||||
void ccs_configure_clocks(const struct ccs_clk_config *config) {
|
||||
MEMCTL_FLASHLOCK = MEMCTL_FLASHLOCK_ALLOW_MEMCTL_WRITE;
|
||||
|
||||
ccs_reset_clocks(); /* set safe defaults */
|
||||
ccs_frclkmux_select(CCS_CTL_FRCLKMUXSEL_ROSC);
|
||||
ccs_sclkmux_select_frclk();
|
||||
memctl_flash_select_roscclk();
|
||||
|
||||
if (config->mem_enable_cache) {
|
||||
memctl_flash_cache_enable();
|
||||
} else {
|
||||
memctl_flash_cache_disable();
|
||||
}
|
||||
|
||||
ccs_frclkmux_select(CCS_CTL_FRCLKMUXSEL_CLKREF); /* switch frclk to 4MHz CLKREF */
|
||||
|
||||
switch (config->frclk_source) {
|
||||
case CCS_CTL_FRCLKMUXSEL_ROSC:
|
||||
ccs_frclkmux_select(CCS_CTL_FRCLKMUXSEL_ROSC);
|
||||
ccs_frclk_frequency = CCS_ROSC_FREQ;
|
||||
break;
|
||||
case CCS_CTL_FRCLKMUXSEL_CLKREF:
|
||||
ccs_frclkmux_select(CCS_CTL_FRCLKMUXSEL_CLKREF);
|
||||
ccs_frclk_frequency = CCS_CLKREF_FREQ;
|
||||
break;
|
||||
case CCS_CTL_FRCLKMUXSEL_EXTCLK:
|
||||
if (config->extclk_frequency > CCS_EXTCLK_MAX_FREQ
|
||||
|| config->extclk_frequency == 0) {
|
||||
cm3_assert_not_reached();
|
||||
}
|
||||
ccs_frclkmux_select(CCS_CTL_FRCLKMUXSEL_EXTCLK);
|
||||
ccs_frclk_frequency = ccs_extclk_frequency = config->extclk_frequency;
|
||||
break;
|
||||
default:
|
||||
cm3_assert_not_reached();
|
||||
}
|
||||
|
||||
if (config->sclk_source == CCS_CTL_SCLKMUXSEL_FRCLK) {
|
||||
ccs_set_hclkdiv(config->hclkdiv);
|
||||
ccs_set_aclkdiv(config->aclkdiv);
|
||||
memctl_flash_set_wstate(config->mem_wstate);
|
||||
ccs_sclkmux_select_frclk();
|
||||
memctl_flash_set_mclkdiv(config->mem_mclkdiv);
|
||||
if (config->mem_mclksel == false) {
|
||||
memctl_flash_select_roscclk();
|
||||
} else {
|
||||
memctl_flash_select_mclk();
|
||||
}
|
||||
ccs_sclk_frequency = ccs_frclk_frequency;
|
||||
} else if (config->sclk_source == CCS_CTL_SCLKMUXSEL_PLLCLK) {
|
||||
css_pll_config_enable(config->pll_indiv, config->pll_fbdiv, config->pll_outdiv);
|
||||
ccs_set_hclkdiv(config->hclkdiv);
|
||||
ccs_set_aclkdiv(config->aclkdiv);
|
||||
memctl_flash_set_wstate(config->mem_wstate);
|
||||
ccs_sclkmux_select_pllclk();
|
||||
memctl_flash_set_mclkdiv(config->mem_mclkdiv);
|
||||
if (config->mem_mclksel == false) {
|
||||
memctl_flash_select_roscclk();
|
||||
} else {
|
||||
memctl_flash_select_mclk();
|
||||
}
|
||||
ccs_pll_clk_frequency = ((ccs_frclk_frequency * config->pll_fbdiv) / config->pll_indiv) >> config->pll_outdiv;
|
||||
ccs_sclk_frequency = ccs_pll_clk_frequency;
|
||||
} else {
|
||||
cm3_assert_not_reached();
|
||||
}
|
||||
ccs_set_pclkdiv(config->pclkdiv);
|
||||
ccs_pclk_enable();
|
||||
ccs_aclk_enable();
|
||||
ccs_adcclk_enable();
|
||||
ccs_stclk_sleep_disable();
|
||||
|
||||
ccs_hclk_frequency = ccs_sclk_frequency / config->hclkdiv;
|
||||
ccs_aclk_frequency = ccs_sclk_frequency / config->aclkdiv;
|
||||
ccs_pclk_frequency = ccs_hclk_frequency / config->pclkdiv;
|
||||
|
||||
MEMCTL_FLASHLOCK = MEMCTL_FLASHLOCK_CLEAR;
|
||||
}
|
||||
163
libopencm3/lib/pac55xx/gpio.c
Normal file
163
libopencm3/lib/pac55xx/gpio.c
Normal file
@@ -0,0 +1,163 @@
|
||||
/**
|
||||
* @ingroup PAC55xx_gpio
|
||||
* @brief <b>PAC55xxxx General-Purpose Input/Output (GPIO)</b>
|
||||
* @author @htmlonly © @endhtmlonly 2019 Brian Viele <vielster@allocor.tech>
|
||||
* @date December 1, 2019
|
||||
*
|
||||
* This library supports the GPIO module in the PAC55xx SoC from Qorvo.
|
||||
*
|
||||
* LGPL License Terms @ref lgpl_license
|
||||
*/
|
||||
/*
|
||||
* This file is part of the libopencm3 project.
|
||||
*
|
||||
* This library is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <libopencm3/pac55xx/gpio.h>
|
||||
|
||||
static uint32_t get_ccs_port_base(uint32_t gpioport) {
|
||||
switch (gpioport) {
|
||||
case GPIOA:
|
||||
return CCS_PORTA;
|
||||
case GPIOB:
|
||||
return CCS_PORTB;
|
||||
case GPIOC:
|
||||
return CCS_PORTC;
|
||||
case GPIOD:
|
||||
return CCS_PORTD;
|
||||
case GPIOE:
|
||||
return CCS_PORTE;
|
||||
case GPIOF:
|
||||
return CCS_PORTF;
|
||||
case GPIOG:
|
||||
return CCS_PORTG;
|
||||
default:
|
||||
return 0U;
|
||||
}
|
||||
}
|
||||
|
||||
void gpio_mode_setup(uint32_t gpioport, gpio_mode_t mode,
|
||||
ccs_pull_updown_t pull_up_down, uint16_t gpios) {
|
||||
/* Read the current value of the register. */
|
||||
uint32_t reg = GPIO_MODER(gpioport);
|
||||
uint32_t port = get_ccs_port_base(gpioport);
|
||||
|
||||
/* Loop through only set bits, utilize built-ins for optimized assembly. */
|
||||
int ffs = __builtin_ffs(gpios);
|
||||
while (ffs) {
|
||||
const int pin = ffs - 1;
|
||||
const int bit = (1 << pin);
|
||||
|
||||
/* Update the cached mode value by clearing then setting values. */
|
||||
reg &= ~GPIO_MODER_MASK_PIN(pin);
|
||||
reg |= GPIO_MODER_MODE(pin, mode);
|
||||
|
||||
/* Set the pinmux configurations for the pull-up / pull-down. */
|
||||
if (pull_up_down == CCS_IO_PULL_UP) {
|
||||
CCS_PDENR(port) &= ~bit;
|
||||
CCS_PUENR(port) |= bit;
|
||||
} else if (pull_up_down == CCS_IO_PULL_DOWN) {
|
||||
CCS_PUENR(port) &= ~bit;
|
||||
CCS_PDENR(port) |= bit;
|
||||
} else {
|
||||
CCS_PDENR(port) &= ~bit;
|
||||
CCS_PUENR(port) &= ~bit;
|
||||
}
|
||||
gpios ^= bit; /* Clear the bit we just serviced. */
|
||||
ffs = __builtin_ffs(gpios);
|
||||
}
|
||||
GPIO_MODER(gpioport) = reg;
|
||||
}
|
||||
|
||||
void gpio_set_outmask(uint32_t gpioport, bool enable, uint16_t gpios) {
|
||||
uint32_t reg = GPIO_OUTMASKR(gpioport);
|
||||
if (enable) {
|
||||
reg |= gpios;
|
||||
} else {
|
||||
reg &= ~gpios;
|
||||
}
|
||||
GPIO_OUTMASKR(gpioport) = reg;
|
||||
}
|
||||
|
||||
void gpio_set(uint32_t gpioport, uint16_t gpios) {
|
||||
GPIO_DOSETR(gpioport) = gpios;
|
||||
}
|
||||
|
||||
void gpio_clear(uint32_t gpioport, uint16_t gpios) {
|
||||
GPIO_DOCLEARR(gpioport) = gpios;
|
||||
}
|
||||
|
||||
uint16_t gpio_get(uint32_t gpioport, uint16_t gpios) {
|
||||
return GPIO_INR(gpioport) & gpios;
|
||||
}
|
||||
|
||||
void gpio_set_af(uint32_t gpioport, ccs_muxsel_func_t muxsel, uint16_t gpios) {
|
||||
uint32_t port = get_ccs_port_base(gpioport);
|
||||
|
||||
/* Update each of the pin configs. */
|
||||
uint32_t reg = CCS_MUXSELR(port);
|
||||
int ffs = __builtin_ffs(gpios);
|
||||
while (ffs) {
|
||||
const int pin = ffs - 1;
|
||||
|
||||
reg &= ~CCS_MUXSELR_MASK_PIN(pin);
|
||||
reg |= CCS_MUXSELR_VAL(pin, muxsel);
|
||||
|
||||
/* Set the pinmux configurations for the pull-up / pull-down. */
|
||||
gpios ^= (1 << pin); /* Clear the bit we just serviced. */
|
||||
ffs = __builtin_ffs(gpios);
|
||||
}
|
||||
CCS_MUXSELR(port) = reg;
|
||||
}
|
||||
|
||||
void gpio_set_output_options(uint32_t gpioport, ccs_drive_strength_t strength,
|
||||
uint16_t gpios) {
|
||||
uint32_t port = get_ccs_port_base(gpioport);
|
||||
|
||||
/* Update each of the pin configs. */
|
||||
uint32_t reg = CCS_DSR(port);
|
||||
int ffs = __builtin_ffs(gpios);
|
||||
while (ffs) {
|
||||
const int pin = ffs - 1;
|
||||
|
||||
reg &= ~CCS_DSR_MASK_PIN(pin);
|
||||
reg |= CCS_DSR_DS_VAL(pin, strength);
|
||||
|
||||
/* Set the pinmux configurations for the pull-up / pull-down. */
|
||||
gpios ^= (1 << pin); /* Clear the bit we just serviced. */
|
||||
ffs = __builtin_ffs(gpios);
|
||||
}
|
||||
CCS_DSR(port) = reg;
|
||||
}
|
||||
|
||||
void gpio_set_schmidt_trigger(uint32_t gpioport, bool enable, uint16_t gpios) {
|
||||
uint32_t port = get_ccs_port_base(gpioport);
|
||||
|
||||
/* Update each of the pin configs. */
|
||||
uint32_t reg = CCS_DSR(port);
|
||||
int ffs = __builtin_ffs(gpios);
|
||||
while (ffs) {
|
||||
const int pin = ffs - 1;
|
||||
if (enable) {
|
||||
reg |= CCS_DSR_SCHMIDT_PIN(pin);
|
||||
} else {
|
||||
reg &= ~CCS_DSR_SCHMIDT_PIN(pin);
|
||||
}
|
||||
|
||||
/* Set the pinmux configurations for the pull-up / pull-down. */
|
||||
gpios ^= (1 << pin); /* Clear the bit we just serviced. */
|
||||
ffs = __builtin_ffs(gpios);
|
||||
}
|
||||
CCS_DSR(port) = reg;
|
||||
}
|
||||
78
libopencm3/lib/pac55xx/memctl.c
Normal file
78
libopencm3/lib/pac55xx/memctl.c
Normal file
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* @brief <b>PAC55xxxx Memory Controller Driver</b>
|
||||
* @author @htmlonly © @endhtmlonly 2020 Kevin Stefanik <kevin@allocor.tech>
|
||||
* @date April 1, 2020
|
||||
*
|
||||
* This library supports the Memory Controller in the PAC55xx SoC from Qorvo.
|
||||
*
|
||||
* LGPL License Terms @ref lgpl_license
|
||||
*/
|
||||
/*
|
||||
* This file is part of the libopencm3 project.
|
||||
*
|
||||
* This library is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <libopencm3/pac55xx/memctl.h>
|
||||
|
||||
void memctl_flash_set_wstate(uint32_t wstate) {
|
||||
MEMCTL_MEMCTLR = (MEMCTL_MEMCTLR & ~MEMCTL_MEMCTLR_WSTATE(MEMCTL_MEMCTLR_WSTATE_MASK)) | MEMCTL_MEMCTLR_WSTATE(wstate);
|
||||
}
|
||||
void memctl_flash_set_mclkdiv(uint32_t div) {
|
||||
MEMCTL_MEMCTLR = (MEMCTL_MEMCTLR & ~MEMCTL_MEMCTLR_MCLKDIV(16)) | MEMCTL_MEMCTLR_MCLKDIV(div);
|
||||
}
|
||||
void memctl_flash_reset_write_buffer(void) {
|
||||
MEMCTL_MEMCTLR = (MEMCTL_MEMCTLR & ~MEMCTL_MEMCTLR_WRITEWORDCNT(MEMCTL_MEMCTLR_WRITEWORDCNT_MASK));
|
||||
}
|
||||
void memctl_flash_standby_mode_enable(void) {
|
||||
MEMCTL_MEMCTLR |= MEMCTL_MEMCTLR_STBY;
|
||||
}
|
||||
void memctl_flash_standby_mode_disable(void) {
|
||||
MEMCTL_MEMCTLR &= ~MEMCTL_MEMCTLR_STBY;
|
||||
}
|
||||
void memctl_flash_cache_enable(void) {
|
||||
MEMCTL_MEMCTLR &= ~MEMCTL_MEMCTLR_CACHEDIS;
|
||||
}
|
||||
void memctl_flash_cache_disable(void) {
|
||||
MEMCTL_MEMCTLR |= MEMCTL_MEMCTLR_CACHEDIS;
|
||||
}
|
||||
void memctl_flash_select_roscclk(void) {
|
||||
MEMCTL_MEMCTLR &= ~MEMCTL_MEMCTLR_MCLKSEL;
|
||||
}
|
||||
void memctl_flash_select_mclk(void) {
|
||||
MEMCTL_MEMCTLR |= MEMCTL_MEMCTLR_MCLKSEL;
|
||||
}
|
||||
void memctl_sram_ecc_enable(void) {
|
||||
MEMCTL_MEMCTLR &= ~MEMCTL_MEMCTLR_ECCDIS;
|
||||
}
|
||||
void memctl_sram_ecc_disable(void) {
|
||||
MEMCTL_MEMCTLR |= MEMCTL_MEMCTLR_ECCDIS;
|
||||
}
|
||||
void memctl_sram_ecc_single_bit_interrupt_enable(void) {
|
||||
MEMCTL_MEMCTLR |= MEMCTL_MEMCTLR_SEIE;
|
||||
}
|
||||
void memctl_sram_ecc_single_bit_interrupt_disable(void) {
|
||||
MEMCTL_MEMCTLR &= ~MEMCTL_MEMCTLR_SEIE;
|
||||
}
|
||||
void memctl_sram_ecc_dual_bit_interrupt_enable(void) {
|
||||
MEMCTL_MEMCTLR |= MEMCTL_MEMCTLR_DEIE;
|
||||
}
|
||||
void memctl_sram_ecc_dual_bit_interrupt_disable(void) {
|
||||
MEMCTL_MEMCTLR &= ~MEMCTL_MEMCTLR_DEIE;
|
||||
}
|
||||
void memctl_invaddr_interrupt_enable(void) {
|
||||
MEMCTL_MEMCTLR |= MEMCTL_MEMCTLR_INVADDRIE;
|
||||
}
|
||||
void memctl_invaddr_interrupt_disable(void) {
|
||||
MEMCTL_MEMCTLR &= ~MEMCTL_MEMCTLR_INVADDRIE;
|
||||
}
|
||||
200
libopencm3/lib/pac55xx/usart.c
Normal file
200
libopencm3/lib/pac55xx/usart.c
Normal file
@@ -0,0 +1,200 @@
|
||||
/**
|
||||
* @defgroup usart_api USART peripheral API
|
||||
* @ingroup peripheral_apis
|
||||
* @brief <b>PAC55xxxx USART Driver</b>
|
||||
* @author @htmlonly © @endhtmlonly 2020 Kevin Stefanik <kevin@allocor.tech>
|
||||
* @date February 25, 2020
|
||||
*
|
||||
* This library supports the USART module in the PAC55xx SoC from Qorvo.
|
||||
*
|
||||
* LGPL License Terms @ref lgpl_license
|
||||
*/
|
||||
/*
|
||||
* This file is part of the libopencm3 project.
|
||||
*
|
||||
* This library is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <libopencm3/pac55xx/usart.h>
|
||||
#include <libopencm3/cm3/common.h>
|
||||
|
||||
/**@{*/
|
||||
|
||||
/** @brief USART Set Baudrate
|
||||
The baud rate is computed assuming a peripheral clock of 150MHz.
|
||||
@param[in] usart unsigned 32 bit. USART block register address base @ref usart_reg_base
|
||||
@param[in] baud unsigned 32 bit. Baud rate specified in Hz.
|
||||
@return Actual baud rate.
|
||||
*/
|
||||
uint32_t usart_set_baudrate(uint32_t usart, uint32_t baud) {
|
||||
/* TODO Assumes 150MHz PCLK. Update this to ccs_get_peripheral_freq() like on other platforms */
|
||||
const uint32_t pclk = 150000000;
|
||||
uint32_t denom = (baud << 4); /* denominator is baud * 16. */
|
||||
uint32_t dlr = 0xFFFFu & ((pclk + denom / 2) / denom);
|
||||
USART_DLR(usart) = dlr;
|
||||
return pclk / (dlr << 4); /* Baud Rate = PCLK / (16 * UARTADLR) */
|
||||
}
|
||||
|
||||
/** @brief USART Configure Line Control Register
|
||||
This register sets the data bits, stop bits, and parity
|
||||
@param[in] usart unsigned 32 bit. USART block register address base @ref usart_reg_base
|
||||
@param[in] data_bits unsigned 8 bit. One of USART_DATABITS_5/6/7/8.
|
||||
@param[in] stop_bits unsigned 8 bit. One of USART_STOPBITS_1/1P5/2.
|
||||
@param[in] parity unsigned 8 bit. One of USART_PARITY_DISABLE/ODD/EVEN/FORCE1/FORCE0
|
||||
*/
|
||||
void usart_configure_lcr(uint32_t usart, uint8_t data_bits, uint8_t stop_bits,
|
||||
uint8_t parity) {
|
||||
USART_LCR(usart) = USART_LCR_WLS(data_bits)
|
||||
| ((stop_bits==USART_STOPBITS_2) ? USART_LCR_SBS : 0)
|
||||
| USART_LCR_PSELPEN(parity);
|
||||
}
|
||||
|
||||
/** @brief Enable Break Control
|
||||
Enables break control bit that forces TX pin to logic low.
|
||||
@param[in] usart unsigned 32 bit. USART block register address base @ref usart_reg_base
|
||||
*/
|
||||
void usart_break_enable(uint32_t usart) {
|
||||
USART_LCR(usart) |= USART_LCR_BCON;
|
||||
}
|
||||
|
||||
/** @brief Disable Break Control
|
||||
Disables break control bit that forces TX pin to logic low.
|
||||
@param[in] usart unsigned 32 bit. USART block register address base @ref usart_reg_base
|
||||
*/
|
||||
void usart_break_disable(uint32_t usart) {
|
||||
USART_LCR(usart) &= ~USART_LCR_BCON;
|
||||
}
|
||||
|
||||
/** @brief Enable Enhanced Mode
|
||||
Enable enhanced mode to generate interrupts when FIFO thresholds in FCR are reached.
|
||||
@param[in] usart unsigned 32 bit. USART block register address base @ref usart_reg_base
|
||||
*/
|
||||
void usart_enhanced_enable(uint32_t usart) {
|
||||
USART_EFR(usart) = USART_EFR_ENMODE;
|
||||
}
|
||||
|
||||
/** @brief Disable Enhanced Mode
|
||||
Disable enhanced mode to generate interrupts when FIFO thresholds in FCR are reached.
|
||||
@param[in] usart unsigned 32 bit. USART block register address base @ref usart_reg_base
|
||||
*/
|
||||
void usart_enhanced_disable(uint32_t usart) {
|
||||
USART_EFR(usart) &= ~USART_EFR_ENMODE;
|
||||
}
|
||||
|
||||
/** @brief Enable FIFOs
|
||||
Enable both TX and RX FIFOs. This must be set before setting the trigger levels.
|
||||
@param[in] usart unsigned 32 bit. USART block register address base @ref usart_reg_base
|
||||
*/
|
||||
void usart_fifo_enable(uint32_t usart) {
|
||||
USART_FCR(usart) |= USART_FCR_FIFOEN;
|
||||
}
|
||||
|
||||
/** @brief Disable FIFOs
|
||||
Disable both TX and RX FIFOs.
|
||||
@param[in] usart unsigned 32 bit. USART block register address base @ref usart_reg_base
|
||||
*/
|
||||
void usart_fifo_disable(uint32_t usart) {
|
||||
USART_FCR(usart) &= ~USART_FCR_FIFOEN;
|
||||
}
|
||||
|
||||
/** Set the TX and RX FIFO depth. This function also enables the FIFOs if not already.
|
||||
@param[in] usart unsigned 32 bit. USART block register address base @ref usart_reg_base
|
||||
@param[in] tx_depth unsigned 8 bit. One of USART_FIFO_TRIG_1/2/4/14CHAR.
|
||||
@param[in] rx_depth unsigned 8 bit. One of USART_FIFO_TRIG_1/2/4/14CHAR.
|
||||
*/
|
||||
void usart_set_fifo_depth(uint32_t usart, uint8_t tx_depth, uint8_t rx_depth) {
|
||||
USART_FCR(usart) |= USART_FCR_FIFOEN;
|
||||
USART_FCR(usart) = USART_FCR_TXTL(tx_depth) | USART_FCR_RXTL(rx_depth) | USART_FCR_FIFOEN;
|
||||
}
|
||||
|
||||
/** @brief Write byte to TX FIFO
|
||||
@param[in] usart unsigned 32 bit. USART block register address base @ref usart_reg_base
|
||||
@param[in] data unsigned 8 bit. Data to write to the TX FIFO.
|
||||
*/
|
||||
void usart_send(uint32_t usart, uint8_t data) {
|
||||
USART_THR(usart) = (uint32_t)data;
|
||||
}
|
||||
|
||||
/** @brief Read byte from the RX FIFO
|
||||
@param[in] usart unsigned 32 bit. USART block register address base @ref usart_reg_base
|
||||
@return Data read from the RX FIFO.
|
||||
*/
|
||||
uint8_t usart_recv(uint32_t usart) {
|
||||
return (uint8_t)USART_RBR(usart);
|
||||
}
|
||||
|
||||
/** @brief Enable RX Interrupts
|
||||
Enable both the Receive Data Available and Character Timeout interrupts.
|
||||
@param[in] usart unsigned 32 bit. USART block register address base @ref usart_reg_base
|
||||
*/
|
||||
void usart_enable_rx_interrupt(uint32_t usart) {
|
||||
USART_IER(usart) |= USART_IER_RBRIE;
|
||||
}
|
||||
|
||||
/** @brief Disable RX Interrupts
|
||||
Disable both the Receive Data Available and Character Timeout interrupts.
|
||||
@param[in] usart unsigned 32 bit. USART block register address base @ref usart_reg_base
|
||||
*/
|
||||
void usart_disable_rx_interrupt(uint32_t usart) {
|
||||
USART_IER(usart) &= ~USART_IER_RBRIE;
|
||||
}
|
||||
|
||||
/** @brief Enable TX Interrupt
|
||||
Enable the TX Holding Register Empty interrupt.
|
||||
@param[in] usart unsigned 32 bit. USART block register address base @ref usart_reg_base
|
||||
*/
|
||||
void usart_enable_tx_interrupt(uint32_t usart) {
|
||||
USART_IER(usart) |= USART_IER_THRIE;
|
||||
}
|
||||
|
||||
/** @brief Disable TX Interrupt
|
||||
Disable the TX Holding Register Empty interrupt.
|
||||
@param[in] usart unsigned 32 bit. USART block register address base @ref usart_reg_base
|
||||
*/
|
||||
void usart_disable_tx_interrupt(uint32_t usart) {
|
||||
USART_IER(usart) &= ~USART_IER_THRIE;
|
||||
}
|
||||
|
||||
/** @brief Enable RX Line Status Interrupt
|
||||
Enable the RX Line Status interrupt.
|
||||
@param[in] usart unsigned 32 bit. USART block register address base @ref usart_reg_base
|
||||
*/
|
||||
void usart_enable_rls_interrupt(uint32_t usart) {
|
||||
USART_IER(usart) |= USART_IER_RLSIE;
|
||||
}
|
||||
|
||||
/** @brief Disable RX Line Status Interrupt
|
||||
Disable the RX Line Status interrupt.
|
||||
@param[in] usart unsigned 32 bit. USART block register address base @ref usart_reg_base
|
||||
*/
|
||||
void usart_disable_rls_interrupt(uint32_t usart) {
|
||||
USART_IER(usart) &= ~USART_IER_RLSIE;
|
||||
}
|
||||
|
||||
/** @brief Clear the TX FIFO
|
||||
Clears the TX FIFO. The bit is self-clearing.
|
||||
@param[in] usart unsigned 32 bit. USART block register address base @ref usart_reg_base
|
||||
*/
|
||||
void usart_clear_tx_fifo(uint32_t usart) {
|
||||
USART_FCR(usart) |= USART_FCR_TXFIFORST;
|
||||
}
|
||||
|
||||
/** @brief Clear the RX FIFO
|
||||
Clears the RX FIFO. The bit is self-clearing.
|
||||
@param[in] usart unsigned 32 bit. USART block register address base @ref usart_reg_base
|
||||
*/
|
||||
void usart_clear_rx_fifo(uint32_t usart) {
|
||||
USART_FCR(usart) |= USART_FCR_RXFIFORST;
|
||||
}
|
||||
|
||||
/**@}*/
|
||||
24
libopencm3/lib/pac55xx/vector_chipset.c
Normal file
24
libopencm3/lib/pac55xx/vector_chipset.c
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* This file is part of the libopencm3 project.
|
||||
*
|
||||
* Copyright (C) 2019 Brian Viele <vielster@allocor.tech>
|
||||
*
|
||||
* This library is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Standard Cortex-M4F initialization of FPU. */
|
||||
static void pre_main(void) {
|
||||
/* Enable FPU */
|
||||
SCB_CPACR |= SCB_CPACR_FULL * (SCB_CPACR_CP10 | SCB_CPACR_CP11);
|
||||
}
|
||||
Reference in New Issue
Block a user