atsame5x: add support for CAN
Этот коммит содержится в:
родитель
9f5066aa6f
коммит
e7c6bd3730
12 изменённых файлов: 682 добавлений и 5 удалений
4
Makefile
4
Makefile
|
@ -343,8 +343,12 @@ smoketest:
|
|||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=atsame54-xpro examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=atsame54-xpro examples/can
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-m4-can examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=feather-m4-can examples/caninterrupt
|
||||
@$(MD5SUM) test.hex
|
||||
# test pwm
|
||||
$(TINYGO) build -size short -o test.hex -target=itsybitsy-m0 examples/pwm
|
||||
@$(MD5SUM) test.hex
|
||||
|
|
15
src/examples/can/feather-m4-can.go
Обычный файл
15
src/examples/can/feather-m4-can.go
Обычный файл
|
@ -0,0 +1,15 @@
|
|||
// +build feather_m4_can
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// power on the CAN Transceiver
|
||||
// https://learn.adafruit.com/adafruit-feather-m4-can-express/pinouts#can-bus-3078990-8
|
||||
boost_en := machine.BOOST_EN
|
||||
boost_en.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
boost_en.High()
|
||||
}
|
53
src/examples/can/main.go
Обычный файл
53
src/examples/can/main.go
Обычный файл
|
@ -0,0 +1,53 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"machine"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
can1 := machine.CAN1
|
||||
can1.Configure(machine.CANConfig{
|
||||
TransferRate: machine.CANTransferRate500kbps,
|
||||
TransferRateFD: machine.CANTransferRate1000kbps,
|
||||
Rx: machine.CAN1_RX,
|
||||
Tx: machine.CAN1_TX,
|
||||
Standby: machine.CAN1_STANDBY,
|
||||
})
|
||||
|
||||
can0 := machine.CAN0
|
||||
can0.Configure(machine.CANConfig{
|
||||
TransferRate: machine.CANTransferRate500kbps,
|
||||
TransferRateFD: machine.CANTransferRate1000kbps,
|
||||
Rx: machine.CAN0_RX,
|
||||
Tx: machine.CAN0_TX,
|
||||
Standby: machine.NoPin,
|
||||
})
|
||||
|
||||
rxMsg := machine.CANRxBufferElement{}
|
||||
|
||||
for {
|
||||
can1.Tx(0x123, []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}, false, false)
|
||||
can1.Tx(0x789, []byte{0x02, 0x24, 0x46, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}, true, false)
|
||||
time.Sleep(time.Millisecond * 1000)
|
||||
|
||||
sz0 := can0.RxFifoSize()
|
||||
if sz0 > 0 {
|
||||
fmt.Printf("CAN0 %d\r\n", sz0)
|
||||
for i := 0; i < sz0; i++ {
|
||||
can0.RxRaw(&rxMsg)
|
||||
fmt.Printf("-> %08X %X %#v\r\n", rxMsg.ID, rxMsg.DLC, rxMsg.Data())
|
||||
}
|
||||
}
|
||||
|
||||
sz1 := can1.RxFifoSize()
|
||||
if sz1 > 0 {
|
||||
fmt.Printf("CAN1 %d\r\n", sz1)
|
||||
for i := 0; i < sz1; i++ {
|
||||
can1.RxRaw(&rxMsg)
|
||||
fmt.Printf("-> %08X %X %#v\r\n", rxMsg.ID, rxMsg.DLC, rxMsg.Data())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
15
src/examples/caninterrupt/feather-m4-can.go
Обычный файл
15
src/examples/caninterrupt/feather-m4-can.go
Обычный файл
|
@ -0,0 +1,15 @@
|
|||
// +build feather_m4_can
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// power on the CAN Transceiver
|
||||
// https://learn.adafruit.com/adafruit-feather-m4-can-express/pinouts#can-bus-3078990-8
|
||||
boost_en := machine.BOOST_EN
|
||||
boost_en.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
boost_en.High()
|
||||
}
|
75
src/examples/caninterrupt/main.go
Обычный файл
75
src/examples/caninterrupt/main.go
Обычный файл
|
@ -0,0 +1,75 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
"fmt"
|
||||
"machine"
|
||||
"time"
|
||||
)
|
||||
|
||||
type canMsg struct {
|
||||
ch byte
|
||||
id uint32
|
||||
dlc byte
|
||||
data []byte
|
||||
}
|
||||
|
||||
func main() {
|
||||
ch := make(chan canMsg, 10)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case m := <-ch:
|
||||
fmt.Printf("%d %03X %X ", m.ch, m.id, m.dlc)
|
||||
for _, d := range m.data {
|
||||
fmt.Printf("%02X ", d)
|
||||
}
|
||||
fmt.Printf("\r\n")
|
||||
}
|
||||
|
||||
}
|
||||
}()
|
||||
|
||||
can1 := machine.CAN1
|
||||
can1.Configure(machine.CANConfig{
|
||||
TransferRate: machine.CANTransferRate500kbps,
|
||||
TransferRateFD: machine.CANTransferRate1000kbps,
|
||||
Rx: machine.CAN1_RX,
|
||||
Tx: machine.CAN1_TX,
|
||||
Standby: machine.CAN1_STANDBY,
|
||||
})
|
||||
// RF0NE : Rx FIFO 0 New Message Interrupt Enable
|
||||
can1.SetInterrupt(sam.CAN_IE_RF0NE, func(*machine.CAN) {
|
||||
rxMsg := machine.CANRxBufferElement{}
|
||||
can1.RxRaw(&rxMsg)
|
||||
m := canMsg{ch: 1, id: rxMsg.ID, dlc: rxMsg.DLC, data: rxMsg.Data()}
|
||||
select {
|
||||
case ch <- m:
|
||||
}
|
||||
})
|
||||
|
||||
can0 := machine.CAN0
|
||||
can0.Configure(machine.CANConfig{
|
||||
TransferRate: machine.CANTransferRate500kbps,
|
||||
TransferRateFD: machine.CANTransferRate1000kbps,
|
||||
Rx: machine.CAN0_RX,
|
||||
Tx: machine.CAN0_TX,
|
||||
Standby: machine.NoPin,
|
||||
})
|
||||
// RF0NE : Rx FIFO 0 New Message Interrupt Enable
|
||||
can0.SetInterrupt(sam.CAN_IE_RF0NE, func(*machine.CAN) {
|
||||
rxMsg := machine.CANRxBufferElement{}
|
||||
can0.RxRaw(&rxMsg)
|
||||
m := canMsg{ch: 2, id: rxMsg.ID, dlc: rxMsg.DLC, data: rxMsg.Data()}
|
||||
select {
|
||||
case ch <- m:
|
||||
}
|
||||
})
|
||||
|
||||
for {
|
||||
can0.Tx(0x123, []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}, false, false)
|
||||
time.Sleep(time.Millisecond * 500)
|
||||
can1.Tx(0x456, []byte{0xAA, 0xBB, 0xCC}, false, false)
|
||||
time.Sleep(time.Millisecond * 1000)
|
||||
}
|
||||
}
|
|
@ -328,3 +328,14 @@ var (
|
|||
SERCOM: 6,
|
||||
}
|
||||
)
|
||||
|
||||
// CAN on the SAM E54 Xplained Pro
|
||||
var (
|
||||
CAN0 = CAN{
|
||||
Bus: sam.CAN0,
|
||||
}
|
||||
|
||||
CAN1 = CAN{
|
||||
Bus: sam.CAN1,
|
||||
}
|
||||
)
|
||||
|
|
|
@ -125,7 +125,7 @@ func init() {
|
|||
D7.High()
|
||||
}
|
||||
|
||||
// I2C on the Feather M4.
|
||||
// I2C on the Feather M4 CAN.
|
||||
var (
|
||||
I2C0 = &I2C{
|
||||
Bus: sam.SERCOM2_I2CM,
|
||||
|
@ -133,10 +133,21 @@ var (
|
|||
}
|
||||
)
|
||||
|
||||
// SPI on the Feather M4.
|
||||
// SPI on the Feather M4 CAN.
|
||||
var (
|
||||
SPI0 = SPI{
|
||||
Bus: sam.SERCOM1_SPIM,
|
||||
SERCOM: 1,
|
||||
}
|
||||
)
|
||||
|
||||
// CAN on the Feather M4 CAN.
|
||||
var (
|
||||
CAN0 = CAN{
|
||||
Bus: sam.CAN0,
|
||||
}
|
||||
|
||||
CAN1 = CAN{
|
||||
Bus: sam.CAN1,
|
||||
}
|
||||
)
|
||||
|
|
468
src/machine/machine_atsame5x_can.go
Обычный файл
468
src/machine/machine_atsame5x_can.go
Обычный файл
|
@ -0,0 +1,468 @@
|
|||
// +build sam,atsame51 sam,atsame54
|
||||
|
||||
package machine
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
"errors"
|
||||
"runtime/interrupt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
CANRxFifoSize = 16
|
||||
CANTxFifoSize = 16
|
||||
CANEvFifoSize = 16
|
||||
)
|
||||
|
||||
// Message RAM can only be located in the first 64 KB area of the system RAM.
|
||||
// TODO: when the go:section pragma is merged, add the section configuration
|
||||
|
||||
//go:align 4
|
||||
var CANRxFifo [2][(8 + 64) * CANRxFifoSize]byte
|
||||
|
||||
//go:align 4
|
||||
var CANTxFifo [2][(8 + 64) * CANTxFifoSize]byte
|
||||
|
||||
//go:align 4
|
||||
var CANEvFifo [2][(8) * CANEvFifoSize]byte
|
||||
|
||||
type CAN struct {
|
||||
Bus *sam.CAN_Type
|
||||
}
|
||||
|
||||
type CANTransferRate uint32
|
||||
|
||||
// CAN transfer rates for CANConfig
|
||||
const (
|
||||
CANTransferRate125kbps CANTransferRate = 125000
|
||||
CANTransferRate250kbps CANTransferRate = 250000
|
||||
CANTransferRate500kbps CANTransferRate = 500000
|
||||
CANTransferRate1000kbps CANTransferRate = 1000000
|
||||
CANTransferRate2000kbps CANTransferRate = 2000000
|
||||
CANTransferRate4000kbps CANTransferRate = 4000000
|
||||
)
|
||||
|
||||
// CANConfig holds CAN configuration parameters. Tx and Rx need to be
|
||||
// specified with some pins. When the Standby Pin is specified, configure it
|
||||
// as an output pin and output Low in Configure(). If this operation is not
|
||||
// necessary, specify NoPin.
|
||||
type CANConfig struct {
|
||||
TransferRate CANTransferRate
|
||||
TransferRateFD CANTransferRate
|
||||
Tx Pin
|
||||
Rx Pin
|
||||
Standby Pin
|
||||
}
|
||||
|
||||
var (
|
||||
errCANInvalidTransferRate = errors.New("CAN: invalid TransferRate")
|
||||
errCANInvalidTransferRateFD = errors.New("CAN: invalid TransferRateFD")
|
||||
)
|
||||
|
||||
// Configure this CAN peripheral with the given configuration.
|
||||
func (can *CAN) Configure(config CANConfig) error {
|
||||
if config.Standby != NoPin {
|
||||
config.Standby.Configure(PinConfig{Mode: PinOutput})
|
||||
config.Standby.Low()
|
||||
}
|
||||
|
||||
mode := PinCAN0
|
||||
if can.instance() == 1 {
|
||||
mode = PinCAN1
|
||||
}
|
||||
|
||||
config.Rx.Configure(PinConfig{Mode: mode})
|
||||
config.Tx.Configure(PinConfig{Mode: mode})
|
||||
|
||||
can.Bus.CCCR.SetBits(sam.CAN_CCCR_INIT)
|
||||
for !can.Bus.CCCR.HasBits(sam.CAN_CCCR_INIT) {
|
||||
}
|
||||
|
||||
can.Bus.CCCR.SetBits(sam.CAN_CCCR_CCE)
|
||||
|
||||
can.Bus.CCCR.SetBits(sam.CAN_CCCR_BRSE | sam.CAN_CCCR_FDOE)
|
||||
can.Bus.MRCFG.Set(sam.CAN_MRCFG_QOS_MEDIUM)
|
||||
// base clock == 48 MHz
|
||||
if config.TransferRate == 0 {
|
||||
config.TransferRate = CANTransferRate500kbps
|
||||
}
|
||||
brp := uint32(6)
|
||||
switch config.TransferRate {
|
||||
case CANTransferRate125kbps:
|
||||
brp = 32
|
||||
case CANTransferRate250kbps:
|
||||
brp = 16
|
||||
case CANTransferRate500kbps:
|
||||
brp = 8
|
||||
case CANTransferRate1000kbps:
|
||||
brp = 4
|
||||
default:
|
||||
return errCANInvalidTransferRate
|
||||
}
|
||||
can.Bus.NBTP.Set(8<<sam.CAN_NBTP_NTSEG1_Pos | (brp-1)<<sam.CAN_NBTP_NBRP_Pos |
|
||||
1<<sam.CAN_NBTP_NTSEG2_Pos | 3<<sam.CAN_NBTP_NSJW_Pos)
|
||||
|
||||
if config.TransferRateFD == 0 {
|
||||
config.TransferRateFD = CANTransferRate1000kbps
|
||||
}
|
||||
if config.TransferRateFD < config.TransferRate {
|
||||
return errCANInvalidTransferRateFD
|
||||
}
|
||||
brp = uint32(2)
|
||||
switch config.TransferRateFD {
|
||||
case CANTransferRate125kbps:
|
||||
brp = 32
|
||||
case CANTransferRate250kbps:
|
||||
brp = 16
|
||||
case CANTransferRate500kbps:
|
||||
brp = 8
|
||||
case CANTransferRate1000kbps:
|
||||
brp = 4
|
||||
case CANTransferRate2000kbps:
|
||||
brp = 2
|
||||
case CANTransferRate4000kbps:
|
||||
brp = 1
|
||||
default:
|
||||
return errCANInvalidTransferRateFD
|
||||
}
|
||||
can.Bus.DBTP.Set((brp-1)<<sam.CAN_DBTP_DBRP_Pos | 8<<sam.CAN_DBTP_DTSEG1_Pos |
|
||||
1<<sam.CAN_DBTP_DTSEG2_Pos | 3<<sam.CAN_DBTP_DSJW_Pos)
|
||||
|
||||
can.Bus.RXF0C.Set(sam.CAN_RXF0C_F0OM | CANRxFifoSize<<sam.CAN_RXF0C_F0S_Pos | uint32(uintptr(unsafe.Pointer(&CANRxFifo[can.instance()][0])))&0xFFFF)
|
||||
can.Bus.RXESC.Set(sam.CAN_RXESC_F0DS_DATA64)
|
||||
can.Bus.TXESC.Set(sam.CAN_TXESC_TBDS_DATA64)
|
||||
can.Bus.TXBC.Set(CANTxFifoSize<<sam.CAN_TXBC_TFQS_Pos | 0<<sam.CAN_TXBC_NDTB_Pos | uint32(uintptr(unsafe.Pointer(&CANTxFifo[can.instance()][0])))&0xFFFF)
|
||||
can.Bus.TXEFC.Set(CANEvFifoSize<<sam.CAN_TXEFC_EFS_Pos | uint32(uintptr(unsafe.Pointer(&CANEvFifo[can.instance()][0])))&0xFFFF)
|
||||
|
||||
can.Bus.TSCC.Set(sam.CAN_TSCC_TSS_INC)
|
||||
|
||||
can.Bus.GFC.Set(0<<sam.CAN_GFC_ANFS_Pos | 0<<sam.CAN_GFC_ANFE_Pos)
|
||||
|
||||
can.Bus.SIDFC.Set(0 << sam.CAN_SIDFC_LSS_Pos)
|
||||
can.Bus.XIDFC.Set(0 << sam.CAN_SIDFC_LSS_Pos)
|
||||
|
||||
can.Bus.XIDAM.Set(0x1FFFFFFF << sam.CAN_XIDAM_EIDM_Pos)
|
||||
|
||||
can.Bus.ILE.SetBits(sam.CAN_ILE_EINT0)
|
||||
|
||||
can.Bus.CCCR.ClearBits(sam.CAN_CCCR_CCE)
|
||||
can.Bus.CCCR.ClearBits(sam.CAN_CCCR_INIT)
|
||||
for can.Bus.CCCR.HasBits(sam.CAN_CCCR_INIT) {
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Callbacks to be called for CAN.SetInterrupt(). Wre're using the magic
|
||||
// constant 2 and 32 here beacuse th SAM E51/E54 has 2 CAN and 32 interrupt
|
||||
// sources.
|
||||
var (
|
||||
canInstances [2]*CAN
|
||||
canCallbacks [2][32]func(*CAN)
|
||||
)
|
||||
|
||||
// SetInterrupt sets an interrupt to be executed when a particular CAN state.
|
||||
//
|
||||
// This call will replace a previously set callback. You can pass a nil func
|
||||
// to unset the CAN interrupt. If you do so, the change parameter is ignored
|
||||
// and can be set to any value (such as 0).
|
||||
func (can *CAN) SetInterrupt(ie uint32, callback func(*CAN)) error {
|
||||
if callback == nil {
|
||||
// Disable this CAN interrupt
|
||||
can.Bus.IE.ClearBits(ie)
|
||||
return nil
|
||||
}
|
||||
can.Bus.IE.SetBits(ie)
|
||||
|
||||
idx := 0
|
||||
switch can.Bus {
|
||||
case sam.CAN0:
|
||||
canInstances[0] = can
|
||||
case sam.CAN1:
|
||||
canInstances[1] = can
|
||||
idx = 1
|
||||
}
|
||||
|
||||
for i := uint(0); i < 32; i++ {
|
||||
if ie&(1<<i) != 0 {
|
||||
canCallbacks[idx][i] = callback
|
||||
}
|
||||
}
|
||||
|
||||
switch can.Bus {
|
||||
case sam.CAN0:
|
||||
interrupt.New(sam.IRQ_CAN0, func(interrupt.Interrupt) {
|
||||
ir := sam.CAN0.IR.Get()
|
||||
sam.CAN0.IR.Set(ir) // clear interrupt
|
||||
for i := uint(0); i < 32; i++ {
|
||||
if ir&(1<<i) != 0 && canCallbacks[0][i] != nil {
|
||||
canCallbacks[0][i](canInstances[0])
|
||||
}
|
||||
}
|
||||
}).Enable()
|
||||
case sam.CAN1:
|
||||
interrupt.New(sam.IRQ_CAN1, func(interrupt.Interrupt) {
|
||||
ir := sam.CAN1.IR.Get()
|
||||
sam.CAN1.IR.Set(ir) // clear interrupt
|
||||
for i := uint(0); i < 32; i++ {
|
||||
if ir&(1<<i) != 0 && canCallbacks[1][i] != nil {
|
||||
canCallbacks[1][i](canInstances[1])
|
||||
}
|
||||
}
|
||||
}).Enable()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// TxFifoIsFull returns whether TxFifo is full or not.
|
||||
func (can *CAN) TxFifoIsFull() bool {
|
||||
return (can.Bus.TXFQS.Get() & sam.CAN_TXFQS_TFQF_Msk) == sam.CAN_TXFQS_TFQF_Msk
|
||||
}
|
||||
|
||||
// TxRaw sends a CAN Frame according to CANTxBufferElement.
|
||||
func (can *CAN) TxRaw(e *CANTxBufferElement) {
|
||||
putIndex := (can.Bus.TXFQS.Get() & sam.CAN_TXFQS_TFQPI_Msk) >> sam.CAN_TXFQS_TFQPI_Pos
|
||||
|
||||
f := CANTxFifo[can.instance()][putIndex*(8+64) : (putIndex+1)*(8+64)]
|
||||
id := e.ID
|
||||
if !e.XTD {
|
||||
// standard identifier is stored into ID[28:18]
|
||||
id <<= 18
|
||||
}
|
||||
|
||||
f[3] = byte(id >> 24)
|
||||
if e.ESI {
|
||||
f[3] |= 0x80
|
||||
}
|
||||
if e.XTD {
|
||||
f[3] |= 0x40
|
||||
}
|
||||
if e.RTR {
|
||||
f[3] |= 0x20
|
||||
}
|
||||
f[2] = byte(id >> 16)
|
||||
f[1] = byte(id >> 8)
|
||||
f[0] = byte(0)
|
||||
f[7] = e.MM
|
||||
f[6] = e.DLC
|
||||
if e.EFC {
|
||||
f[6] |= 0x80
|
||||
}
|
||||
if e.FDF {
|
||||
f[6] |= 0x20
|
||||
}
|
||||
if e.BRS {
|
||||
f[6] |= 0x10
|
||||
}
|
||||
f[5] = 0x00 // reserved
|
||||
f[4] = 0x00 // reserved
|
||||
|
||||
length := CANDlcToLength(e.DLC, e.FDF)
|
||||
for i := byte(0); i < length; i++ {
|
||||
f[8+i] = e.DB[i]
|
||||
}
|
||||
|
||||
can.Bus.TXBAR.SetBits(1 << putIndex)
|
||||
}
|
||||
|
||||
// The Tx transmits CAN frames. It is easier to use than TxRaw, but not as
|
||||
// flexible.
|
||||
func (can *CAN) Tx(id uint32, data []byte, isFD, isExtendedID bool) {
|
||||
length := byte(len(data))
|
||||
dlc := CANLengthToDlc(length, true)
|
||||
|
||||
e := CANTxBufferElement{
|
||||
ESI: false,
|
||||
XTD: isExtendedID,
|
||||
RTR: false,
|
||||
ID: id,
|
||||
MM: 0x00,
|
||||
EFC: true,
|
||||
FDF: isFD,
|
||||
BRS: isFD,
|
||||
DLC: dlc,
|
||||
}
|
||||
|
||||
if !isFD {
|
||||
if length > 8 {
|
||||
length = 8
|
||||
}
|
||||
}
|
||||
for i := byte(0); i < length; i++ {
|
||||
e.DB[i] = data[i]
|
||||
}
|
||||
|
||||
can.TxRaw(&e)
|
||||
}
|
||||
|
||||
// RxFifoSize returns the number of CAN Frames currently stored in the RXFifo.
|
||||
func (can *CAN) RxFifoSize() int {
|
||||
sz := (can.Bus.RXF0S.Get() & sam.CAN_RXF0S_F0FL_Msk) >> sam.CAN_RXF0S_F0FL_Pos
|
||||
return int(sz)
|
||||
}
|
||||
|
||||
// RxFifoIsFull returns whether RxFifo is full or not.
|
||||
func (can *CAN) RxFifoIsFull() bool {
|
||||
sz := (can.Bus.RXF0S.Get() & sam.CAN_RXF0S_F0FL_Msk) >> sam.CAN_RXF0S_F0FL_Pos
|
||||
return sz == CANRxFifoSize
|
||||
}
|
||||
|
||||
// RxFifoIsEmpty returns whether RxFifo is empty or not.
|
||||
func (can *CAN) RxFifoIsEmpty() bool {
|
||||
sz := (can.Bus.RXF0S.Get() & sam.CAN_RXF0S_F0FL_Msk) >> sam.CAN_RXF0S_F0FL_Pos
|
||||
return sz == 0
|
||||
}
|
||||
|
||||
// RxRaw copies the received CAN frame to CANRxBufferElement.
|
||||
func (can *CAN) RxRaw(e *CANRxBufferElement) {
|
||||
idx := (can.Bus.RXF0S.Get() & sam.CAN_RXF0S_F0GI_Msk) >> sam.CAN_RXF0S_F0GI_Pos
|
||||
f := CANRxFifo[can.instance()][idx*(8+64):]
|
||||
|
||||
e.ESI = false
|
||||
if (f[3] & 0x80) != 0x00 {
|
||||
e.ESI = true
|
||||
}
|
||||
|
||||
e.XTD = false
|
||||
if (f[3] & 0x40) != 0x00 {
|
||||
e.XTD = true
|
||||
}
|
||||
|
||||
e.RTR = false
|
||||
if (f[3] & 0x20) != 0x00 {
|
||||
e.RTR = true
|
||||
}
|
||||
|
||||
id := ((uint32(f[3]) << 24) + (uint32(f[2]) << 16) + (uint32(f[1]) << 8) + uint32(f[0])) & 0x1FFFFFFF
|
||||
if (f[3] & 0x20) == 0 {
|
||||
id >>= 18
|
||||
id &= 0x000007FF
|
||||
}
|
||||
e.ID = id
|
||||
|
||||
e.ANMF = false
|
||||
if (f[7] & 0x80) != 0x00 {
|
||||
e.ANMF = true
|
||||
}
|
||||
|
||||
e.FIDX = f[7] & 0x7F
|
||||
|
||||
e.FDF = false
|
||||
if (f[6] & 0x20) != 0x00 {
|
||||
e.FDF = true
|
||||
}
|
||||
|
||||
e.BRS = false
|
||||
if (f[6] & 0x10) != 0x00 {
|
||||
e.BRS = true
|
||||
}
|
||||
|
||||
e.DLC = f[6] & 0x0F
|
||||
|
||||
e.RXTS = (uint16(f[5]) << 8) + uint16(f[4])
|
||||
|
||||
for i := byte(0); i < CANDlcToLength(e.DLC, e.FDF); i++ {
|
||||
e.DB[i] = f[i+8]
|
||||
}
|
||||
|
||||
can.Bus.RXF0A.ReplaceBits(idx, sam.CAN_RXF0A_F0AI_Msk, sam.CAN_RXF0A_F0AI_Pos)
|
||||
}
|
||||
|
||||
// Rx receives a CAN frame. It is easier to use than RxRaw, but not as
|
||||
// flexible.
|
||||
func (can *CAN) Rx() (id uint32, dlc byte, data []byte, isFd, isExtendedID bool) {
|
||||
e := CANRxBufferElement{}
|
||||
can.RxRaw(&e)
|
||||
length := CANDlcToLength(e.DLC, e.FDF)
|
||||
return e.ID, length, e.DB[:length], e.FDF, e.XTD
|
||||
}
|
||||
|
||||
func (can *CAN) instance() byte {
|
||||
if can.Bus == sam.CAN0 {
|
||||
return 0
|
||||
} else {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
// CANTxBufferElement is a struct that corresponds to the same5x' Tx Buffer
|
||||
// Element.
|
||||
type CANTxBufferElement struct {
|
||||
ESI bool
|
||||
XTD bool
|
||||
RTR bool
|
||||
ID uint32
|
||||
MM uint8
|
||||
EFC bool
|
||||
FDF bool
|
||||
BRS bool
|
||||
DLC uint8
|
||||
DB [64]uint8
|
||||
}
|
||||
|
||||
// CANRxBufferElement is a struct that corresponds to the same5x Rx Buffer and
|
||||
// FIFO Element.
|
||||
type CANRxBufferElement struct {
|
||||
ESI bool
|
||||
XTD bool
|
||||
RTR bool
|
||||
ID uint32
|
||||
ANMF bool
|
||||
FIDX uint8
|
||||
FDF bool
|
||||
BRS bool
|
||||
DLC uint8
|
||||
RXTS uint16
|
||||
DB [64]uint8
|
||||
}
|
||||
|
||||
// Data returns the received data as a slice of the size according to dlc.
|
||||
func (e CANRxBufferElement) Data() []byte {
|
||||
return e.DB[:CANDlcToLength(e.DLC, e.FDF)]
|
||||
}
|
||||
|
||||
// CANDlcToLength() converts a DLC value to its actual length.
|
||||
func CANDlcToLength(dlc byte, isFD bool) byte {
|
||||
length := dlc
|
||||
if dlc == 0x09 {
|
||||
length = 12
|
||||
} else if dlc == 0x0A {
|
||||
length = 16
|
||||
} else if dlc == 0x0B {
|
||||
length = 20
|
||||
} else if dlc == 0x0C {
|
||||
length = 24
|
||||
} else if dlc == 0x0D {
|
||||
length = 32
|
||||
} else if dlc == 0x0E {
|
||||
length = 48
|
||||
} else if dlc == 0x0F {
|
||||
length = 64
|
||||
}
|
||||
return length
|
||||
|
||||
}
|
||||
|
||||
// CANLengthToDlc() converts its actual length to a DLC value.
|
||||
func CANLengthToDlc(length byte, isFD bool) byte {
|
||||
dlc := length
|
||||
if length <= 0x08 {
|
||||
} else if length <= 12 {
|
||||
dlc = 0x09
|
||||
} else if length <= 16 {
|
||||
dlc = 0x0A
|
||||
} else if length <= 20 {
|
||||
dlc = 0x0B
|
||||
} else if length <= 24 {
|
||||
dlc = 0x0C
|
||||
} else if length <= 32 {
|
||||
dlc = 0x0D
|
||||
} else if length <= 48 {
|
||||
dlc = 0x0E
|
||||
} else if length <= 64 {
|
||||
dlc = 0x0F
|
||||
}
|
||||
return dlc
|
||||
}
|
23
src/runtime/runtime_atsame5x_can.go
Обычный файл
23
src/runtime/runtime_atsame5x_can.go
Обычный файл
|
@ -0,0 +1,23 @@
|
|||
// +build sam,atsame51 sam,atsame54
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
)
|
||||
|
||||
func init() {
|
||||
initCANClock()
|
||||
}
|
||||
|
||||
func initCANClock() {
|
||||
// Turn on clocks for CAN0/CAN1.
|
||||
sam.MCLK.AHBMASK.SetBits(sam.MCLK_AHBMASK_CAN0_)
|
||||
sam.MCLK.AHBMASK.SetBits(sam.MCLK_AHBMASK_CAN1_)
|
||||
|
||||
// Put Generic Clock Generator 1 as source for USB
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_CAN0].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
sam.GCLK.PCHCTRL[sam.PCHCTRL_GCLK_CAN1].Set((sam.GCLK_PCHCTRL_GEN_GCLK1 << sam.GCLK_PCHCTRL_GEN_Pos) |
|
||||
sam.GCLK_PCHCTRL_CHEN)
|
||||
}
|
|
@ -2,5 +2,6 @@
|
|||
"inherits": ["atsame54p20a"],
|
||||
"build-tags": ["atsame54_xpro"],
|
||||
"flash-method": "openocd",
|
||||
"openocd-interface": "cmsis-dap"
|
||||
"openocd-interface": "cmsis-dap",
|
||||
"default-stack-size": 4096
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"inherits": ["cortex-m4"],
|
||||
"build-tags": ["sam", "atsame5x", "atsame54p20", "atsame54p20a"],
|
||||
"build-tags": ["sam", "atsame5x", "atsame54", "atsame54p20", "atsame54p20a"],
|
||||
"linkerscript": "targets/atsame5xx20-no-bootloader.ld",
|
||||
"extra-files": [
|
||||
"src/device/sam/atsame54p20a.s"
|
||||
|
|
|
@ -4,5 +4,6 @@
|
|||
"flash-1200-bps-reset": "true",
|
||||
"flash-method": "msd",
|
||||
"msd-volume-name": "FTHRCANBOOT",
|
||||
"msd-firmware-name": "firmware.uf2"
|
||||
"msd-firmware-name": "firmware.uf2",
|
||||
"default-stack-size": 4096
|
||||
}
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче