mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-05 03:27:48 +00:00
new CAN API demo
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
//go:build stm32g0
|
||||
|
||||
package machine
|
||||
|
||||
// unexported functions here are implemented in the device file
|
||||
// and added to the build tags of this file.
|
||||
|
||||
// TxFIFOLevel returns amount of CAN frames stored for transmission and total Tx fifo length.
|
||||
func (can *CAN) TxFIFOLevel() (level int, maxlevel int) {
|
||||
return can.txFIFOLevel()
|
||||
}
|
||||
|
||||
// Tx puts a CAN frame in TxFIFO for transmission. Returns error if TxFIFO is full.
|
||||
func (can *CAN) Tx(id uint32, extendedID bool, data []byte) error {
|
||||
return can.tx(id, extendedID, data)
|
||||
}
|
||||
|
||||
// RxFIFOLevel returns amount of CAN frames received and stored and total Rx fifo length.
|
||||
// If the hardware is interrupt driven RxFIFOLevel should return 0,0.
|
||||
func (can *CAN) RxFIFOLevel() (level int, maxlevel int) {
|
||||
return can.rxFIFOLevel()
|
||||
}
|
||||
|
||||
// SetRxCallback sets the receive callback. flags is a bitfield where bits set are:
|
||||
// - bit 0: Is a FD frame.
|
||||
// - bit 1: Is a RTR frame.
|
||||
// - bit 2: Bitrate switch was active in frame.
|
||||
// - bit 3: ESI error state indicator active.
|
||||
func (can *CAN) SetRxCallback(cb func(data []byte, id uint32, extendedID bool, timestamp uint32, flags uint32)) {
|
||||
can.setRxCallback(cb)
|
||||
}
|
||||
|
||||
// RxPoll is called periodically for poll driven drivers. If the driver is interrupt driven
|
||||
// then RxPoll is a no-op and may return nil. Users may determine if a CAN is interrupt driven by
|
||||
// checking if RxFIFOLevel returns 0,0.
|
||||
func (can *CAN) RxPoll() error {
|
||||
return can.rxPoll()
|
||||
}
|
||||
@@ -0,0 +1,278 @@
|
||||
//go:build stm32g0b1
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/stm32"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var canRxCB [2]func(data []byte, id uint32, extendedID bool, timestamp uint32, flags uint32)
|
||||
|
||||
// Configure initializes the FDCAN peripheral and starts it.
|
||||
func (can *CAN) Configure(config FDCANConfig) error {
|
||||
if config.Standby != NoPin {
|
||||
config.Standby.Configure(PinConfig{Mode: PinOutput})
|
||||
config.Standby.Low()
|
||||
}
|
||||
|
||||
enableFDCANClock()
|
||||
|
||||
config.Tx.ConfigureAltFunc(PinConfig{Mode: PinOutput}, can.TxAltFuncSelect)
|
||||
config.Rx.ConfigureAltFunc(PinConfig{Mode: PinInputFloating}, can.RxAltFuncSelect)
|
||||
|
||||
// Exit sleep mode.
|
||||
can.Bus.SetCCCR_CSR(0)
|
||||
timeout := 10000
|
||||
for can.Bus.GetCCCR_CSA() != 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return errFDCANTimeout
|
||||
}
|
||||
}
|
||||
|
||||
// Request initialization.
|
||||
can.Bus.SetCCCR_INIT(1)
|
||||
timeout = 10000
|
||||
for can.Bus.GetCCCR_INIT() == 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return errFDCANTimeout
|
||||
}
|
||||
}
|
||||
|
||||
// Enable configuration change.
|
||||
can.Bus.SetCCCR_CCE(1)
|
||||
|
||||
if can.Bus == stm32.FDCAN1 {
|
||||
can.Bus.SetCKDIV_PDIV(0) // No clock division.
|
||||
}
|
||||
|
||||
can.Bus.SetCCCR_DAR(0) // Enable auto retransmission.
|
||||
can.Bus.SetCCCR_TXP(0) // Disable transmit pause.
|
||||
can.Bus.SetCCCR_PXHD(0) // Enable protocol exception handling.
|
||||
can.Bus.SetCCCR_FDOE(1) // FD operation.
|
||||
can.Bus.SetCCCR_BRSE(1) // Bit rate switching.
|
||||
|
||||
// Reset mode bits, then apply requested mode.
|
||||
can.Bus.SetCCCR_TEST(0)
|
||||
can.Bus.SetCCCR_MON(0)
|
||||
can.Bus.SetCCCR_ASM(0)
|
||||
can.Bus.SetTEST_LBCK(0)
|
||||
switch config.Mode {
|
||||
case FDCANModeBusMonitoring:
|
||||
can.Bus.SetCCCR_MON(1)
|
||||
case FDCANModeInternalLoopback:
|
||||
can.Bus.SetCCCR_TEST(1)
|
||||
can.Bus.SetCCCR_MON(1)
|
||||
can.Bus.SetTEST_LBCK(1)
|
||||
case FDCANModeExternalLoopback:
|
||||
can.Bus.SetCCCR_TEST(1)
|
||||
can.Bus.SetTEST_LBCK(1)
|
||||
}
|
||||
|
||||
// Nominal bit timing (64 MHz FDCAN clock, 16 tq/bit, ~80% sample point).
|
||||
if config.TransferRate == 0 {
|
||||
config.TransferRate = FDCANTransferRate500kbps
|
||||
}
|
||||
nbrp, ntseg1, ntseg2, nsjw, err := fdcanNominalBitTiming(config.TransferRate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
can.Bus.NBTP.Set(((nsjw - 1) << 25) | ((nbrp - 1) << 16) | ((ntseg1 - 1) << 8) | (ntseg2 - 1))
|
||||
|
||||
// Data bit timing (FD phase).
|
||||
if config.TransferRateFD == 0 {
|
||||
config.TransferRateFD = FDCANTransferRate1000kbps
|
||||
}
|
||||
if config.TransferRateFD < config.TransferRate {
|
||||
return errFDCANInvalidTransferRateFD
|
||||
}
|
||||
dbrp, dtseg1, dtseg2, dsjw, err := fdcanDataBitTiming(config.TransferRateFD)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
can.Bus.DBTP.Set(((dbrp - 1) << 16) | ((dtseg1 - 1) << 8) | ((dtseg2 - 1) << 4) | (dsjw - 1))
|
||||
|
||||
// Enable timestamp counter (internal, prescaler=1).
|
||||
can.Bus.TSCC.Set(1)
|
||||
|
||||
// Clear message RAM.
|
||||
base := can.sramBase()
|
||||
for addr := base; addr < base+sramcanSize; addr += 4 {
|
||||
*(*uint32)(unsafe.Pointer(addr)) = 0
|
||||
}
|
||||
|
||||
// Set filter list sizes: LSS[20:16], LSE[27:24].
|
||||
rxgfc := can.Bus.RXGFC.Get()
|
||||
rxgfc &= ^uint32(0x0F1F0000)
|
||||
rxgfc |= uint32(sramcanFLSNbr) << 16
|
||||
rxgfc |= uint32(sramcanFLENbr) << 24
|
||||
can.Bus.RXGFC.Set(rxgfc)
|
||||
|
||||
// Start peripheral.
|
||||
can.Bus.SetCCCR_CCE(0)
|
||||
can.Bus.SetCCCR_INIT(0)
|
||||
timeout = 10000
|
||||
for can.Bus.GetCCCR_INIT() != 0 {
|
||||
timeout--
|
||||
if timeout == 0 {
|
||||
return errFDCANTimeout
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (can *CAN) txFIFOLevel() (int, int) {
|
||||
free := int(can.Bus.TXFQS.Get() & 0x07) // TFFL[2:0]
|
||||
return sramcanTFQNbr - free, sramcanTFQNbr
|
||||
}
|
||||
|
||||
func (can *CAN) tx(id uint32, extendedID bool, data []byte) error {
|
||||
if can.Bus.TXFQS.Get()&0x00200000 != 0 { // TFQF bit
|
||||
return errFDCANTxFifoFull
|
||||
}
|
||||
|
||||
putIndex := (can.Bus.TXFQS.Get() >> 16) & 0x03 // TFQPI[1:0]
|
||||
txAddr := can.sramBase() + sramcanTFQSA + uintptr(putIndex)*sramcanTFQSize
|
||||
|
||||
// Header word 1: identifier and flags.
|
||||
var w1 uint32
|
||||
if extendedID {
|
||||
w1 = (id & 0x1FFFFFFF) | fdcanElementMaskXTD
|
||||
} else {
|
||||
w1 = (id & 0x7FF) << 18
|
||||
}
|
||||
|
||||
// Header word 2: DLC only (classic CAN, no FD/BRS).
|
||||
length := byte(len(data))
|
||||
if length > 8 {
|
||||
length = 8
|
||||
}
|
||||
w2 := uint32(length) << 16
|
||||
|
||||
*(*uint32)(unsafe.Pointer(txAddr)) = w1
|
||||
*(*uint32)(unsafe.Pointer(txAddr + 4)) = w2
|
||||
|
||||
// Copy data with 32-bit word access (Cortex-M0+).
|
||||
for w := byte(0); w < (length+3)/4; w++ {
|
||||
var word uint32
|
||||
base := w * 4
|
||||
for b := byte(0); b < 4 && base+b < length; b++ {
|
||||
word |= uint32(data[base+b]) << (b * 8)
|
||||
}
|
||||
*(*uint32)(unsafe.Pointer(txAddr + 8 + uintptr(w)*4)) = word
|
||||
}
|
||||
|
||||
can.Bus.TXBAR.Set(1 << putIndex)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (can *CAN) rxFIFOLevel() (int, int) {
|
||||
level := int(can.Bus.RXF0S.Get() & 0x0F) // F0FL[3:0]
|
||||
return level, sramcanRF0Nbr
|
||||
}
|
||||
|
||||
func (can *CAN) setRxCallback(cb func(data []byte, id uint32, extendedID bool, timestamp uint32, flags uint32)) {
|
||||
canRxCB[can.instance] = cb
|
||||
}
|
||||
|
||||
func (can *CAN) rxPoll() error {
|
||||
cb := canRxCB[can.instance]
|
||||
if cb == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for can.Bus.RXF0S.Get()&0x0F != 0 {
|
||||
getIndex := (can.Bus.RXF0S.Get() >> 8) & 0x03 // F0GI[1:0]
|
||||
rxAddr := can.sramBase() + sramcanRF0SA + uintptr(getIndex)*sramcanRF0Size
|
||||
|
||||
w1 := *(*uint32)(unsafe.Pointer(rxAddr))
|
||||
w2 := *(*uint32)(unsafe.Pointer(rxAddr + 4))
|
||||
|
||||
extendedID := w1&fdcanElementMaskXTD != 0
|
||||
var id uint32
|
||||
if extendedID {
|
||||
id = w1 & fdcanElementMaskEXTID
|
||||
} else {
|
||||
id = (w1 & fdcanElementMaskSTDID) >> 18
|
||||
}
|
||||
|
||||
timestamp := w2 & fdcanElementMaskTS
|
||||
dlc := byte((w2 & fdcanElementMaskDLC) >> 16)
|
||||
|
||||
var flags uint32
|
||||
if w2&fdcanElementMaskFDF != 0 {
|
||||
flags |= 1 // bit 0: FD frame
|
||||
}
|
||||
if w1&fdcanElementMaskRTR != 0 {
|
||||
flags |= 2 // bit 1: RTR
|
||||
}
|
||||
if w2&fdcanElementMaskBRS != 0 {
|
||||
flags |= 4 // bit 2: BRS
|
||||
}
|
||||
if w1&fdcanElementMaskESI != 0 {
|
||||
flags |= 8 // bit 3: ESI
|
||||
}
|
||||
|
||||
dataLen := dlcToBytes[dlc&0x0F]
|
||||
var buf [64]byte
|
||||
for w := byte(0); w < (dataLen+3)/4; w++ {
|
||||
word := *(*uint32)(unsafe.Pointer(rxAddr + 8 + uintptr(w)*4))
|
||||
base := w * 4
|
||||
for b := byte(0); b < 4 && base+b < dataLen; b++ {
|
||||
buf[base+b] = byte(word >> (b * 8))
|
||||
}
|
||||
}
|
||||
|
||||
// Acknowledge before callback so the FIFO slot is freed.
|
||||
can.Bus.RXF0A.Set(uint32(getIndex))
|
||||
cb(buf[:dataLen], id, extendedID, timestamp, flags)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (can *CAN) sramBase() uintptr {
|
||||
if can.Bus == stm32.FDCAN2 {
|
||||
return uintptr(sramcanBase) + sramcanSize
|
||||
}
|
||||
return uintptr(sramcanBase)
|
||||
}
|
||||
|
||||
// fdcanNominalBitTiming returns prescaler and segment values for the nominal (arbitration) phase.
|
||||
// STM32G0 FDCAN clock = 64 MHz, 16 time quanta per bit, ~80% sample point.
|
||||
func fdcanNominalBitTiming(rate FDCANTransferRate) (brp, tseg1, tseg2, sjw uint32, err error) {
|
||||
switch rate {
|
||||
case FDCANTransferRate125kbps:
|
||||
return 32, 13, 2, 4, nil
|
||||
case FDCANTransferRate250kbps:
|
||||
return 16, 13, 2, 4, nil
|
||||
case FDCANTransferRate500kbps:
|
||||
return 8, 13, 2, 4, nil
|
||||
case FDCANTransferRate1000kbps:
|
||||
return 4, 13, 2, 4, nil
|
||||
default:
|
||||
return 0, 0, 0, 0, errFDCANInvalidTransferRate
|
||||
}
|
||||
}
|
||||
|
||||
// fdcanDataBitTiming returns prescaler and segment values for the data phase (FD).
|
||||
func fdcanDataBitTiming(rate FDCANTransferRate) (brp, tseg1, tseg2, sjw uint32, err error) {
|
||||
switch rate {
|
||||
case FDCANTransferRate125kbps:
|
||||
return 32, 13, 2, 4, nil
|
||||
case FDCANTransferRate250kbps:
|
||||
return 16, 13, 2, 4, nil
|
||||
case FDCANTransferRate500kbps:
|
||||
return 8, 13, 2, 4, nil
|
||||
case FDCANTransferRate1000kbps:
|
||||
return 4, 13, 2, 4, nil
|
||||
case FDCANTransferRate2000kbps:
|
||||
return 2, 13, 2, 4, nil
|
||||
case FDCANTransferRate4000kbps:
|
||||
return 1, 13, 2, 4, nil
|
||||
default:
|
||||
return 0, 0, 0, 0, errFDCANInvalidTransferRateFD
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user