Mercurial > ecos
changeset 2406:641c0b821739
CAN driver for LPC2xxx, plus misc CAN improvements - from Uwe Kindler
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/packages/devs/can/arm/lpc2xxx/current/ChangeLog @@ -0,0 +1,42 @@ +2007-07-01 Uwe Kindler <uwe_kindler@web.de> + + * LPC2xxx CAN driver package created + * cdl/can_lpc2xxx.cdl + * include/can_lpc2xxx.h + * include/can_lpc2xxx_baudrates.h + * src/can_lpc2xxx.c + +//=========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos 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 General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. +// at http://sources.redhat.com/ecos/ecos-license/ +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//===========================================================================
new file mode 100644 --- /dev/null +++ b/packages/devs/can/arm/lpc2xxx/current/cdl/can_lpc2xxx.cdl @@ -0,0 +1,258 @@ +# ==================================================================== +# +# can_lpc2xxx.cdl +# +# eCos LPC2xxx CAN module configuration data +# +# ==================================================================== +#####ECOSGPLCOPYRIGHTBEGIN#### +## ------------------------------------------- +## This file is part of eCos, the Embedded Configurable Operating System. +## Copyright (C) 2003, 2004 eCosCentric Limited +## +## eCos is free software; you can redistribute it and/or modify it under +## the terms of the GNU General Public License as published by the Free +## Software Foundation; either version 2 or (at your option) any later version. +## +## eCos 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 General Public License +## for more details. +## +## You should have received a copy of the GNU General Public License along +## with eCos; if not, write to the Free Software Foundation, Inc., +## 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +## +## As a special exception, if other files instantiate templates or use macros +## or inline functions from this file, or you compile this file and link it +## with other works to produce a work based on this file, this file does not +## by itself cause the resulting work to be covered by the GNU General Public +## License. However the source code for this file must still be made available +## in accordance with section (3) of the GNU General Public License. +## +## This exception does not invalidate any other reasons why a work based on +## this file might be covered by the GNU General Public License. +## ------------------------------------------- +#####ECOSGPLCOPYRIGHTEND#### +# ==================================================================== +######DESCRIPTIONBEGIN#### +# +# Author(s): Uwe Kindler +# Contributors: +# Date: 2007-02-10 +# +#####DESCRIPTIONEND#### +# ==================================================================== + + +cdl_package CYGPKG_DEVS_CAN_LPC2XXX { + display "Philips LPC2xxx CAN device drivers" + parent CYGPKG_IO_CAN_DEVICES + active_if CYGPKG_IO_CAN + active_if CYGPKG_HAL_ARM_LPC2XXX + requires CYGPKG_ERROR + + implements CYGINT_IO_CAN_STD_CAN_ID + implements CYGINT_IO_CAN_EXT_CAN_ID + + include_dir cyg/io + description " + This option enables the CAN device drivers for the + Philips LPC2XXX." + compile -library=libextras.a can_lpc2xxx.c + define_proc { + puts $::cdl_system_header "/***** CAN driver proc output start *****/" + puts $::cdl_system_header "#define CYGDAT_IO_CAN_DEVICE_HEADER <pkgconf/devs_can_lpc2xxx.h>" + puts $::cdl_system_header "/***** CAN driver proc output end *****/" + } + + cdl_component CYGOPT_DEVS_CAN_LPC2XXX_RUNTIME_ACCFILT { + display "Acceptance filter runtime configuration" + flavor bool + implements CYGINT_IO_CAN_RUNTIME_MBOX_CFG + description " + The LPC2xxx CAN module supports a global acceptance filter. Enabling this + option provides support for runtime configuration of this acceptance filter. + If each CAN channel should receive all CAN messages and individual message + filtering is not required then disable this option to eleminate almost + the complete acceptance filter code and to decrease codesize. If this + option is disabled the option CYGOPT_IO_CAN_RUNTIME_MBOX_CFG is not + available and the configuration key CYG_IO_SET_CONFIG_CAN_MSGBUF is + not supported by this driver." + + cdl_option CYGOPT_DEVS_CAN_LPC2XXX_EXTENDED_CFG_KEYS { + display "Extended acceptance filtering" + flavor bool + default_value 0 + description " + The common CAN I/O layer supports setup of single message filters for + reception of single CAN messages. The global LPC2xxx acceptance filter + supports not only single message filters but also message groups. + A message group is defined by a lower bound CAN identifier and an upper + bound CAN identifier. The acceptance filter will accept all messages within + this range of CAN identifiers. The acceptance filter supports a number of + message groups for each CAN channel. The support of message filter groups + is not conform to the standard API of the CAN I/O layer and should only + be used for application where portability is not important." + } + } + + cdl_option CYGDBG_DEVS_CAN_LPC2XXX_DEBUG { + display "Support printing debug information" + default_value 0 + description " + Check this box to turn ON debug options for AT91SAM7 CAN device driver." + } + + # Support up to 4 on-chip CAN modules. The number may vary between + # processor variants so it is easy to update this here + for { set ::channel 0 } { $::channel < 4 } { incr ::channel } { + + cdl_interface CYGINT_DEVS_CAN_LPC2XXX_CAN[set ::channel] { + display "Platform provides CAN [set ::channel]" + flavor bool + description " + This interface will be implemented if the specific LPC2xxx + processor being used has on-chip CAN [set ::channel], and if + that CAN module is accessible on the target hardware." + } + + cdl_component CYGPKG_DEVS_CAN_LPC2XXX_CAN[set ::channel] { + display "Allow access to the on-chip CAN [set ::channel] via a CAN driver" + flavor bool + active_if CYGINT_DEVS_CAN_LPC2XXX_CAN[set ::channel] + default_value 1 + implements CYGINT_IO_CAN_CHANNELS + description " + If the application needs to access the on-chip CAN module [set ::channel] + via an eCos CAN driver then this option should be enabled." + + cdl_option CYGPKG_DEVS_CAN_LPC2XXX_CAN[set ::channel]_NAME { + display "Device name for CAN module [set ::channel]" + flavor data + default_value [format {"\"/dev/can%d\""} $::channel] + description " + This option controls the name that an eCos application + should use to access this device via cyg_io_lookup(), + open(), or similar calls." + } + + + cdl_option CYGNUM_DEVS_CAN_LPC2XXX_CAN[set ::channel]_KBAUD { + display "Default baud rate for CAN module [set ::channel]" + flavor data + default_value 100 + legal_values { 10 20 50 100 125 250 500 800 1000 "AUTO"} + description "This option determines the initial baud rate in KBaud for + CAN module [set ::channel]" + } + + cdl_option CYGNUM_DEVS_CAN_LPC2XXX_CAN[set ::channel]_QUEUESIZE_TX { + display "Size of TX Queue for the CAN module [set ::channel] driver" + flavor data + default_value 32 + legal_values 1 to 1024 + description " + The CAN device driver will run in interrupt mode and will + perform buffering of outgoing data. This option controls the number + of CAN messages the TX queue can store." + } + + cdl_option CYGNUM_DEVS_CAN_LPC2XXX_CAN[set ::channel]_QUEUESIZE_RX { + display "Size of RX Queue for the CAN module [set ::channel] driver" + flavor data + default_value 64 + legal_values 8 to 4096 + description " + The CAN device driver will run in interrupt mode and will + perform buffering of incoming data. This option controls the number + of CAN events the RX queue can store." + } + + cdl_option CYGOPT_DEVS_CAN_LPC2XXX_CAN[set ::channel]_ACCFILT_STARTUP_CFG { + display "Acceptance filter startup configuration" + flavor data + legal_values {"RX_ALL" "RX_NONE"} + default_value {"RX_ALL"} + active_if CYGOPT_DEVS_CAN_LPC2XXX_RUNTIME_ACCFILT + description " + Normally the acceptance filter will be configured at startup time to + receive all available CAN messages. The application can setup single + message filters during runtime later. If RX_NONE is selected then the + acceptance filter for this channel is configured to receive no CAN + message identifier." + } + + } + } + + cdl_option CYGPKG_DEVS_CAN_LPC2XXX_TESTS { + display "CAN LPC2xxx device driver tests" + flavor data + no_define + calculated { "tests/can_busload tests/can_rx_tx" } + description " + This option specifies the set of tests for the LPC2xxx CAN device driver." + } + + cdl_option CYGBLD_DEVS_CAN_LPC2XXX_EXTRA_TESTS { + display "Build extra CAN tests" + default_value 0 + no_define + description " + This option enables the building of some extra tests which + can be used when testing / debugging the LPC2xxx CAN driver. These + are not built by default since they do not use the dedicated + testing infrastructure. All tests require a properly configured + CAN network with a second CAN node that can send and receive + CAN messages." + + make -priority 320 { + <PREFIX>/bin/can_multichan_rx : <PACKAGE>/tests/can_multichan_rx.c + @sh -c "mkdir -p tests $(dir $@)" + $(CC) -c $(INCLUDE_PATH) -Wp,-MD,deps.tmp -I$(dir $<) $(CFLAGS) -o tests/can_multichan_rx.o $< + @echo $@ ": \\" > $(notdir $@).deps + @echo $(wildcard $(PREFIX)/lib/*) " \\" >> $(notdir $@).deps + @tail -n +2 deps.tmp >> $(notdir $@).deps + @echo >> $(notdir $@).deps + @rm deps.tmp + $(CC) $(LDFLAGS) -L$(PREFIX)/lib -Ttarget.ld -o $@ tests/can_multichan_rx.o + } + + make -priority 320 { + <PREFIX>/bin/can_multichan_tx : <PACKAGE>/tests/can_multichan_tx.c + @sh -c "mkdir -p tests $(dir $@)" + $(CC) -c $(INCLUDE_PATH) -Wp,-MD,deps.tmp -I$(dir $<) $(CFLAGS) -o tests/can_multichan_tx.o $< + @echo $@ ": \\" > $(notdir $@).deps + @echo $(wildcard $(PREFIX)/lib/*) " \\" >> $(notdir $@).deps + @tail -n +2 deps.tmp >> $(notdir $@).deps + @echo >> $(notdir $@).deps + @rm deps.tmp + $(CC) $(LDFLAGS) -L$(PREFIX)/lib -Ttarget.ld -o $@ tests/can_multichan_tx.o + } + + make -priority 320 { + <PREFIX>/bin/can_baudrates : <PACKAGE>/tests/can_baudrates.c + @sh -c "mkdir -p tests $(dir $@)" + $(CC) -c $(INCLUDE_PATH) -Wp,-MD,deps.tmp -I$(dir $<) $(CFLAGS) -o tests/can_baudrates.o $< + @echo $@ ": \\" > $(notdir $@).deps + @echo $(wildcard $(PREFIX)/lib/*) " \\" >> $(notdir $@).deps + @tail -n +2 deps.tmp >> $(notdir $@).deps + @echo >> $(notdir $@).deps + @rm deps.tmp + $(CC) $(LDFLAGS) -L$(PREFIX)/lib -Ttarget.ld -o $@ tests/can_baudrates.o + } + + make -priority 320 { + <PREFIX>/bin/can_extended_cfg : <PACKAGE>/tests/can_extended_cfg.c + @sh -c "mkdir -p tests $(dir $@)" + $(CC) -c $(INCLUDE_PATH) -Wp,-MD,deps.tmp -I$(dir $<) $(CFLAGS) -o tests/can_extended_cfg.o $< + @echo $@ ": \\" > $(notdir $@).deps + @echo $(wildcard $(PREFIX)/lib/*) " \\" >> $(notdir $@).deps + @tail -n +2 deps.tmp >> $(notdir $@).deps + @echo >> $(notdir $@).deps + @rm deps.tmp + $(CC) $(LDFLAGS) -L$(PREFIX)/lib -Ttarget.ld -o $@ tests/can_extended_cfg.o + } + } +}
new file mode 100644 --- /dev/null +++ b/packages/devs/can/arm/lpc2xxx/current/include/can_lpc2xxx.h @@ -0,0 +1,93 @@ +#ifndef CYGONCE_CAN_LPC2XXX_H +#define CYGONCE_CAN_LPC2XXX_H +//========================================================================== +// +// devs/can/arm/lpc2xxx/current/include/can_lpc2xxx.h +// +// Extended configuration option for LPC2xxx CAN driver +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. +// Copyright (C) 2003 Gary Thomas +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos 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 General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. +// at http://sources.redhat.com/ecos/ecos-license/ +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): Uwe Kindler +// Contributors: Uwe Kindler +// Date: 2007-02-08 +// Purpose: Extended configuration options for LPC2xxx CAN driver +// Description: +// +//####DESCRIPTIONEND#### +// +//========================================================================== + + +//========================================================================== +// DEFINES +//========================================================================== +// +// The LPC2XXX supports enhanced configuration options that are not supported +// be the generic CAN I/O layer. Be careful with using this extension +// because they may reduce portability of your pplication +// + +//-------------------------------------------------------------------------- +// Message filter configuration +// +#define CYG_IO_SET_CONFIG_LPC2XXX_ACCFILT_GROUP CYG_IO_SET_CONFIG_CAN_ABORT + 0x10 // add message filter group + + +//-------------------------------------------------------------------------- +// Mode setup of LPC2XXX +// +#define CYGNUM_CAN_MODE_LPC2XXX_LISTEN_ONLY 0x80 // set controller in listen only mode + + +//========================================================================== +// DATA TYPES +//========================================================================== +// +// structure for configuration of message filter groups +// +typedef struct cyg_can_filtergroup_cfg_st +{ + cyg_can_id_type ext; + cyg_uint32 lower_id_bound; + cyg_uint32 upper_id_bound; +} cyg_can_filtergroup_cfg; + + +//--------------------------------------------------------------------------- +#endif // CYGONCE_CAN_LPC2XXX_H
new file mode 100644 --- /dev/null +++ b/packages/devs/can/arm/lpc2xxx/current/include/can_lpc2xxx_baudrates.h @@ -0,0 +1,216 @@ +#ifndef CYGONCE_CAN_LPC2XXX_BAUDRATES_H +#define CYGONCE_CAN_LPC2XXX_BAUDRATES_H +//========================================================================== +// +// devs/can/arm/lpc2xxx/current/include/can_lpc2xxx_baudrates.h +// +// Precalculated values for bit timing register +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. +// Copyright (C) 2003 Gary Thomas +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos 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 General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. +// at http://sources.redhat.com/ecos/ecos-license/ +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): Uwe Kindler +// Contributors: Uwe Kindler +// Date: 2007-07-01 +// Purpose: Precalculated bit timing values for various baudrates +// Description: +// +//####DESCRIPTIONEND#### +// +//========================================================================== + + +// +// Peripheral clock speed +// +#define CYGNUM_CAN_LPC2XXX_VPB_CLK (CYGNUM_HAL_ARM_LPC2XXX_CLOCK_SPEED / CYGNUM_HAL_ARM_LPC2XXX_VPBDIV) + +// +// Macro for creation of CAN_BR value for baudrate tbl +// +#define CAN_BR_TBL_ENTRY(_brp_, _tseg1_, _tseg2_, _sjw_, _sam_) \ + ((_sam_ << 23) | (_tseg2_ << 20) | (_tseg1_ << 16) | (_sjw_ << 14) | (_brp_)) + + +//========================================================================== +// BAUDRATES +//========================================================================== +#if CYGNUM_CAN_LPC2XXX_VPB_CLK == 60000000 +// +// Table with register values for baudrates at peripheral clock of 60 MHz +// +static const cyg_uint32 lpc2xxx_br_tbl[] = +{ + CAN_BR_TBL_ENTRY(00, 00, 00, 0, 0), // 10 kbaud - not supported + CAN_BR_TBL_ENTRY(00, 00, 00, 0, 0), // 20 kbaud - not supported + CAN_BR_TBL_ENTRY(59, 15, 02, 0, 1), // 50 kbaud + CAN_BR_TBL_ENTRY(39, 11, 01, 0, 1), // 100 kbaud + CAN_BR_TBL_ENTRY(29, 12, 01, 0, 1), // 125 kbaud + CAN_BR_TBL_ENTRY(14, 12, 01, 0, 1), // 250 kbaud + CAN_BR_TBL_ENTRY(07, 11, 01, 0, 0), // 500 kbaud + CAN_BR_TBL_ENTRY(04, 11, 01, 0, 0), // 800 kbaud + CAN_BR_TBL_ENTRY(03, 11, 01, 0, 0), // 1000 kbaud + CAN_BR_TBL_ENTRY(00, 00, 00, 0, 0), // Autobaud - not supported +}; +#define HAL_LPC2XXX_BAUD_TBL_DEFINED 1 +#endif // CYGNUM_CAN_LPC2XXX_VPB_CLK == 60000000 + +#if CYGNUM_CAN_LPC2XXX_VPB_CLK == 30000000 +// +// Table with register values for baudrates at peripheral clock of 30 MHz +// +static const cyg_uint32 lpc2xxx_br_tbl[] = +{ + CAN_BR_TBL_ENTRY(00, 00, 00, 0, 0), // 10 kbaud - not supported + CAN_BR_TBL_ENTRY(00, 00, 00, 0, 0), // 20 kbaud - not supported + CAN_BR_TBL_ENTRY(59, 15, 02, 0, 1), // 50 kbaud + CAN_BR_TBL_ENTRY(39, 11, 01, 0, 1), // 100 kbaud + CAN_BR_TBL_ENTRY(29, 12, 01, 0, 1), // 125 kbaud + CAN_BR_TBL_ENTRY(14, 12, 01, 0, 1), // 250 kbaud + CAN_BR_TBL_ENTRY(07, 11, 01, 0, 0), // 500 kbaud + CAN_BR_TBL_ENTRY(04, 11, 01, 0, 0), // 800 kbaud + CAN_BR_TBL_ENTRY(03, 11, 01, 0, 0), // 1000 kbaud + CAN_BR_TBL_ENTRY(00, 00, 00, 0, 0), // Autobaud - not supported +}; +#define HAL_LPC2XXX_BAUD_TBL_DEFINED 1 +#endif // CYGNUM_CAN_LPC2XXX_VPB_CLK == 30000000 + +#if CYGNUM_CAN_LPC2XXX_VPB_CLK == 15000000 +// +// Table with register values for baudrates at peripheral clock of 15 MHz +// +static const cyg_uint32 lpc2xxx_br_tbl[] = +{ + CAN_BR_TBL_ENTRY(59, 15, 07, 0, 1), // 10 kbaud + CAN_BR_TBL_ENTRY(49, 11, 01, 0, 1), // 20 kbaud + CAN_BR_TBL_ENTRY(19, 11, 01, 0, 1), // 50 kbaud + CAN_BR_TBL_ENTRY(09, 11, 01, 0, 1), // 100 kbaud + CAN_BR_TBL_ENTRY(07, 11, 01, 0, 1), // 125 kbaud + CAN_BR_TBL_ENTRY(03, 11, 01, 0, 1), // 250 kbaud + CAN_BR_TBL_ENTRY(01, 11, 01, 0, 0), // 500 kbaud + CAN_BR_TBL_ENTRY(00, 00, 00, 0, 0), // 800 kbaud - not supported + CAN_BR_TBL_ENTRY(00, 11, 01, 0, 0), // 1000 kbaud + CAN_BR_TBL_ENTRY(00, 00, 00, 0, 0), // Autobaud - not supported +}; +#define HAL_LPC2XXX_BAUD_TBL_DEFINED 1 +#endif // CYGNUM_CAN_LPC2XXX_VPB_CLK == 15000000 + +#if CYGNUM_CAN_LPC2XXX_VPB_CLK == 48000000 +// +// Table with register values for baudrates at peripheral clock of 48 MHz +// +static const cyg_uint32 lpc2xxx_br_tbl[] = +{ + CAN_BR_TBL_ENTRY(00, 00, 00, 0, 0), // 10 kbaud - not supported + CAN_BR_TBL_ENTRY(00, 00, 00, 0, 0), // 20 kbaud - not supported + CAN_BR_TBL_ENTRY(59, 12, 01, 0, 1), // 50 kbaud + CAN_BR_TBL_ENTRY(29, 12, 01, 0, 1), // 100 kbaud + CAN_BR_TBL_ENTRY(23, 12, 01, 0, 1), // 125 kbaud + CAN_BR_TBL_ENTRY(11, 12, 01, 0, 1), // 250 kbaud + CAN_BR_TBL_ENTRY(05, 12, 01, 0, 0), // 500 kbaud + CAN_BR_TBL_ENTRY(03, 11, 01, 0, 0), // 800 kbaud + CAN_BR_TBL_ENTRY(02, 12, 01, 0, 0), // 1000 kbaud + CAN_BR_TBL_ENTRY(00, 00, 00, 0, 0), // Autobaud - not supported +}; +#define HAL_LPC2XXX_BAUD_TBL_DEFINED 1 +#endif // CYGNUM_CAN_LPC2XXX_VPB_CLK == 48000000 + +#if CYGNUM_CAN_LPC2XXX_VPB_CLK == 24000000 +// +// Table with register values for baudrates at peripheral clock of 24 MHz +// +static const cyg_uint32 lpc2xxx_br_tbl[] = +{ + CAN_BR_TBL_ENTRY(00, 00, 00, 0, 0), // 10 kbaud - not supported + CAN_BR_TBL_ENTRY(59, 15, 02, 0, 1), // 20 kbaud + CAN_BR_TBL_ENTRY(29, 12, 01, 0, 1), // 50 kbaud + CAN_BR_TBL_ENTRY(14, 12, 01, 0, 1), // 100 kbaud + CAN_BR_TBL_ENTRY(11, 12, 01, 0, 1), // 125 kbaud + CAN_BR_TBL_ENTRY(05, 12, 01, 0, 1), // 250 kbaud + CAN_BR_TBL_ENTRY(02, 12, 01, 0, 0), // 500 kbaud + CAN_BR_TBL_ENTRY(01, 11, 01, 0, 0), // 800 kbaud + CAN_BR_TBL_ENTRY(01, 05, 00, 0, 0), // 1000 kbaud + CAN_BR_TBL_ENTRY(00, 00, 00, 0, 0), // Autobaud - not supported +}; +#define HAL_LPC2XXX_BAUD_TBL_DEFINED 1 +#endif // CYGNUM_CAN_LPC2XXX_VPB_CLK == 24000000 + +#if CYGNUM_CAN_LPC2XXX_VPB_CLK == 12000000 +// +// Table with register values for baudrates at peripheral clock of 12 MHz +// +static const cyg_uint32 lpc2xxx_br_tbl[] = +{ + CAN_BR_TBL_ENTRY(59, 15, 02, 0, 1), // 10 kbaud - not supported + CAN_BR_TBL_ENTRY(39, 11, 01, 0, 1), // 20 kbaud + CAN_BR_TBL_ENTRY(14, 12, 01, 0, 1), // 50 kbaud + CAN_BR_TBL_ENTRY(07, 11, 01, 0, 1), // 100 kbaud + CAN_BR_TBL_ENTRY(05, 12, 01, 0, 1), // 125 kbaud + CAN_BR_TBL_ENTRY(02, 12, 01, 0, 1), // 250 kbaud + CAN_BR_TBL_ENTRY(02, 05, 00, 0, 0), // 500 kbaud + CAN_BR_TBL_ENTRY(00, 11, 01, 0, 0), // 800 kbaud + CAN_BR_TBL_ENTRY(00, 09, 00, 0, 0), // 1000 kbaud + CAN_BR_TBL_ENTRY(00, 00, 00, 0, 0), // Autobaud - not supported +}; +#define HAL_LPC2XXX_BAUD_TBL_DEFINED 1 +#endif // CYGNUM_CAN_LPC2XXX_VPB_CLK == 12000000 + + +//========================================================================== +// BIT TIMING MACRO +//========================================================================== +// +// Macro fills baudrate register value depending on selected baudrate +// For several LPC2XXX peripheral clock speeds we provide a pre calculated +// baudrate table. If the board uses another clock speed, then the platform +// HAL needs to provide an own HAL_LPC2XXX_GET_CAN_BR() macro that returns +// valid baudrate register values or it needs to patch this file with +// an additional table for the desired clock speed +// +// +// If a certain baudrate is not supported, then this macro shall return +// 0 as the baudrate register value +// +#ifdef HAL_LPC2XXX_BAUD_TBL_DEFINED +#define HAL_LPC2XXX_GET_CAN_BR(_baudrate_, _br_) \ +CYG_MACRO_START \ + _br_ = lpc2xxx_br_tbl[(_baudrate_) - CYGNUM_CAN_KBAUD_10]; \ +CYG_MACRO_END +#endif // HAL_LPC2XXX_BAUD_TBL_DEFINED + +//------------------------------------------------------------------------- +#endif // #ifndef CYGONCE_CAN_LPC2XXX_BAUDRATES_H
new file mode 100644 --- /dev/null +++ b/packages/devs/can/arm/lpc2xxx/current/src/can_accfilt_lpc2xxx.c @@ -0,0 +1,987 @@ +//========================================================================== +// +// devs/can/arm/lpc2xxx/current/src/can_accfilt_lpc2xxx.c +// +// Acceptance filter management for LPC2xxx CAN driver +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. +// Copyright (C) 2003 Gary Thomas +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos 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 General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. +// at http://sources.redhat.com/ecos/ecos-license/ +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): Uwe Kindler +// Contributors: Uwe Kindler +// Date: 2007-05-28 +// Purpose: Support LPC2xxx on-chip CAN acceptance filters +// Description: +// +//####DESCRIPTIONEND#### +// +//========================================================================== + + +//=========================================================================== +// Data types +//=========================================================================== +// +// Acceptance filter entry +// +typedef struct lpc2xxx_accfilt_entry +{ + cyg_uint32 data; // the value inclusive channel number + cyg_uint32 id; + cyg_uint32 lower_id_bound; + cyg_uint32 upper_id_bound; + cyg_uint8 channel_no; +} lpc2xxx_accfilt_entry_t; + + +//=========================================================================== +// Declarations +//=========================================================================== +//-------------------------------------------------------------------------- +// Lowlevel acceptance filter access +// +#ifdef CYGOPT_IO_CAN_RUNTIME_MBOX_CFG +static bool lpc2xxx_can_accfilt_add(lpc2xxx_can_info_t *info, + cyg_uint32 lower_id, + cyg_uint32 upper_id, + cyg_can_id_type ext); +void lpc2xxx_can_accfilt_ram_insert_entry(cyg_uint32 TableAddress, cyg_uint16 EntryNo); +void lpc2xxx_can_accfilt_ram_remove_entry(cyg_uint32 TableAddress, cyg_uint16 EntryNo); +void lpc2xxx_can_accfilt_remove_all_ctrl_entries(lpc2xxx_can_info_t *info); +#else +static void lpc2xxx_can_accfilt_simple_rx_all(void); +#endif // CYGOPT_IO_CAN_RUNTIME_MBOX_CFG +void lpc2xxx_can_accfilt_reset(void); + +#ifdef CYGDBG_DEVS_CAN_LPC2XXX_DEBUG +void lpc2xxx_can_accfilt_dbg_dump(void); +void lpc2xxx_can_reg_dump(struct cyg_devtab_entry* devtab_entry); +#endif + + +#ifdef CYGOPT_IO_CAN_RUNTIME_MBOX_CFG +//=========================================================================== +// Calculate address of entry in certain table +//=========================================================================== +static cyg_uint32 lpc2xxx_can_accfilt_calc_entry_address(cyg_uint32 TableAddressRegister, cyg_uint16 EntryNo) +{ + cyg_uint32 EntryAddress = 0xFFFFFFFF; + cyg_uint32 TableAddress; + + HAL_READ_UINT32(TableAddressRegister, TableAddress); + switch (TableAddressRegister) + { +#ifdef CYGOPT_IO_CAN_STD_CAN_ID + case CAN_ACCFILT_SFF_SA: + EntryAddress = ((EntryNo / 2) << 2) + TableAddress; + break; + + case CAN_ACCFILT_SFF_GRP_SA: + EntryAddress = TableAddress + (EntryNo << 2); + break; +#endif // CYGOPT_IO_CAN_STD_CAN_ID + +#ifdef CYGOPT_IO_CAN_EXT_CAN_ID + case CAN_ACCFILT_EFF_SA: + EntryAddress = TableAddress + (EntryNo << 2); + break; + + case CAN_ACCFILT_EFF_GRP_SA: + EntryAddress = TableAddress + (EntryNo << 3); + break; +#endif // CYGOPT_IO_CAN_EXT_CAN_ID + default: + CYG_ASSERT(0, "Wrong TableAddressRegister"); + } + + return EntryAddress; +} + + +//=========================================================================== +// Remove one single entry from acceptance filter table +//=========================================================================== +void lpc2xxx_can_accfilt_ram_remove_entry(cyg_uint32 Table, cyg_uint16 EntryNo) +{ + cyg_int32 remove_address = lpc2xxx_can_accfilt_calc_entry_address(Table, EntryNo); + cyg_int32 entry_address; + lsc_buf_t lsc_val; + cyg_uint8 entry_size = sizeof(cyg_uint32); + cyg_uint32 sff_sa; + cyg_uint32 sff_grp_sa; + cyg_uint32 eff_sa; + cyg_uint32 eff_grp_sa; + cyg_int32 end_of_table; + + HAL_READ_UINT32(CAN_ACCFILT_SFF_SA, sff_sa); + HAL_READ_UINT32(CAN_ACCFILT_SFF_GRP_SA, sff_grp_sa); + HAL_READ_UINT32(CAN_ACCFILT_EFF_SA, eff_sa); + HAL_READ_UINT32(CAN_ACCFILT_EFF_GRP_SA, eff_grp_sa); + HAL_READ_UINT32(CAN_ACCFILT_ENDOFTABLE, end_of_table); + + // + // Do not try to remove from an empty table + // + if (!end_of_table) + { + return; + } + + entry_address = remove_address; + + if ((remove_address < eff_grp_sa) && (CAN_ACCFILT_EFF_GRP_SA != Table)) + { + if ((remove_address < eff_sa) && (CAN_ACCFILT_EFF_SA != Table)) + { + if ((remove_address < sff_grp_sa) && (CAN_ACCFILT_SFF_GRP_SA != Table)) + { + lsc_buf_t nextval; + + if (EntryNo % 2) + { + HAL_READ_UINT32(CAN_ACCFILT_RAM_BASE + remove_address, lsc_val.dword); + HAL_READ_UINT32(CAN_ACCFILT_RAM_BASE + remove_address + sizeof(cyg_uint32), nextval.dword); + lsc_val.column.upper = nextval.column.lower; + entry_address += sizeof(cyg_uint32); + } + + // + // Start copy immediatelly after removed entry + // + while (entry_address < sff_grp_sa) + { + HAL_READ_UINT32(CAN_ACCFILT_RAM_BASE + entry_address, lsc_val.dword); + HAL_READ_UINT32(CAN_ACCFILT_RAM_BASE + entry_address + sizeof(cyg_uint32), nextval.dword); + lsc_val.column.lower = lsc_val.column.upper; + lsc_val.column.upper = nextval.column.lower; + HAL_WRITE_UINT32(CAN_ACCFILT_RAM_BASE + entry_address, lsc_val.dword); + entry_address += sizeof(cyg_uint32); + } + + // + // now check if the lower identifier is disabled - if it is disabled, then + // also the upper identifier is invalid and we can remove the entry completely + // if the lower identifier is not disabled, then it is valid and we need + // to disable the upper identifier because it contains an invalid entry + // + if (lsc_val.column.lower & ACCFILT_STD_DIS) + { + sff_grp_sa -= sizeof(cyg_uint32); + entry_address = sff_grp_sa; + } + else + { + HAL_READ_UINT32(CAN_ACCFILT_RAM_BASE + sff_grp_sa - sizeof(cyg_uint32), lsc_val.dword); + lsc_val.column.upper = 0xffff; + HAL_WRITE_UINT32(CAN_ACCFILT_RAM_BASE + sff_grp_sa - sizeof(cyg_uint32), lsc_val.dword); + entry_size = 0; // we do not need to remove anything + } + } // if (pLine < pStdGrpStart) + + eff_sa -= entry_size; + } // if (pLine < pExtIdStart) + + eff_grp_sa -= entry_size; + } // if (pLine < pExtGrpStart) + + // + // If no entry was removed then we can leave immediatelly without changing any + // table pointers because we only did a change inside the sff table + // + if (!entry_size) + { +#ifdef CYGDBG_DEVS_CAN_LPC2XXX_DEBUG + lpc2xxx_can_accfilt_dbg_dump(); +#endif + return; + } + + if (CAN_ACCFILT_EFF_GRP_SA == Table) + { + // + // If we are in the area of extended groups then we need to remove + // 2 lines because lower and upper identifier need 1 line each + // + entry_size += sizeof(cyg_uint32); + } + + end_of_table -= entry_size; + + // + // Move alle entries one or two dword downwads - that means we remove a line + // + while (entry_address < end_of_table) + { + HAL_READ_UINT32(CAN_ACCFILT_RAM_BASE + entry_address + entry_size, lsc_val.dword); + HAL_WRITE_UINT32(CAN_ACCFILT_RAM_BASE + entry_address, lsc_val.dword); + entry_address += sizeof(cyg_uint32); + } + + HAL_WRITE_UINT32(CAN_ACCFILT_SFF_SA, sff_sa); + HAL_WRITE_UINT32(CAN_ACCFILT_SFF_GRP_SA, sff_grp_sa); + HAL_WRITE_UINT32(CAN_ACCFILT_EFF_SA, eff_sa); + HAL_WRITE_UINT32(CAN_ACCFILT_EFF_GRP_SA, eff_grp_sa); + HAL_WRITE_UINT32(CAN_ACCFILT_ENDOFTABLE, end_of_table); + +#ifdef CYGDBG_DEVS_CAN_LPC2XXX_DEBUG + lpc2xxx_can_accfilt_dbg_dump(); +#endif +} + +//=========================================================================== +// Insert one empty line into ram - all entries behind this line will be +// moved one entry upwards +//=========================================================================== +void lpc2xxx_can_accfilt_ram_insert_entry(cyg_uint32 Table, cyg_uint16 EntryNo) +{ + cyg_int16 insert_address = lpc2xxx_can_accfilt_calc_entry_address(Table, EntryNo); + cyg_int16 entry_address; + cyg_int16 copy_start = insert_address; + lsc_buf_t lsc_val; + cyg_uint8 entry_size = sizeof(cyg_uint32); + cyg_uint32 sff_sa; + cyg_uint32 sff_grp_sa; + cyg_uint32 eff_sa; + cyg_uint32 eff_grp_sa; + cyg_uint32 end_of_table; + + + HAL_READ_UINT32(CAN_ACCFILT_SFF_SA, sff_sa); + HAL_READ_UINT32(CAN_ACCFILT_SFF_GRP_SA, sff_grp_sa); + HAL_READ_UINT32(CAN_ACCFILT_EFF_SA, eff_sa); + HAL_READ_UINT32(CAN_ACCFILT_EFF_GRP_SA, eff_grp_sa); + HAL_READ_UINT32(CAN_ACCFILT_ENDOFTABLE, end_of_table); + + if ((insert_address <= eff_grp_sa) && (CAN_ACCFILT_EFF_GRP_SA != Table)) + { + if ((insert_address <= eff_sa) && (CAN_ACCFILT_EFF_SA != Table)) + { + if ((insert_address <= sff_grp_sa) && (CAN_ACCFILT_SFF_GRP_SA != Table)) + { + // + // If we are in the range of standard identifiers then we need to + // do some special copy procedure for this area because a standard entry + // is only 2 byte long. Copy only til start of area with standard groups + // + if (sff_grp_sa) + { + HAL_READ_UINT32(CAN_ACCFILT_RAM_BASE + sff_grp_sa - sizeof(cyg_uint32), lsc_val.dword); // read last entry + // + // now check if the upper identifier is disabled - if it is disabled, then + // we have an odd number of std ids in the list. Then we do not need to + // insert a new line - we simply need to copy all entries 2 bytes upwards + // that meeans we only need to change the std id area and do not need to touch + // any other filter id area. + // If the last entry is not disabled, then we have a valid filter here. + // Then we need to insert a complete new line, that means we also have to mave + // all following entries and filter tables one dword upwards. + // + if (lsc_val.words.low & ACCFILT_STD_DIS) + { + copy_start = end_of_table + sizeof(cyg_uint32); // we do not need to insert a new line and do not copy anything + entry_size = 0; + } + } + + if (entry_size) + { + copy_start = sff_grp_sa; // copy everything behind std id group + sff_grp_sa += entry_size; + } + } // if (pLine < pStdGrpStart) + + eff_sa += entry_size; + } // if (pLine < pExtIdStart) + + eff_grp_sa += entry_size; + } // if (pLine < pExtGrpStart) + + if (CAN_ACCFILT_EFF_GRP_SA == Table) + { + // + // If we are in the area of extended groups then we need to insert + // 2 lines because lower and upper identifier need 1 line each + // + entry_size += sizeof(cyg_uint32); // one entry is 2 dword long + } + + entry_address = end_of_table - sizeof(cyg_uint32); + end_of_table += entry_size; // add one additional entry + + // + // Move alle entries one or two dwords upwards - that means we insert a new empty line + // + while (entry_address >= copy_start) + { + HAL_READ_UINT32(CAN_ACCFILT_RAM_BASE + (cyg_uint32)entry_address, lsc_val.dword); + HAL_WRITE_UINT32(CAN_ACCFILT_RAM_BASE + (cyg_uint32)entry_address + entry_size, lsc_val.dword); + entry_address -= sizeof(cyg_uint32); + } + + // + // For the std ID area we need a special pocedure + // + if (CAN_ACCFILT_SFF_SA == Table) + { + lsc_buf_t preval; + // + // Start copy with last entry of std id table + // + entry_address = sff_grp_sa - sizeof(cyg_uint32); + + while (entry_address > insert_address) + { + HAL_READ_UINT32(CAN_ACCFILT_RAM_BASE + (cyg_uint32)entry_address, lsc_val.dword); + HAL_READ_UINT32(CAN_ACCFILT_RAM_BASE + (cyg_uint32)entry_address - sizeof(cyg_uint32), preval.dword); + lsc_val.column.upper = lsc_val.column.lower; + lsc_val.column.lower = preval.column.upper; + HAL_WRITE_UINT32(CAN_ACCFILT_RAM_BASE + (cyg_uint32)entry_address, lsc_val.dword); + entry_address -= sizeof(cyg_uint32); + } + + // + // If we insert an entry into the lower colum, then we need to move the + // content of the lower column into the upper column + // + if (!(EntryNo % 2)) + { + HAL_READ_UINT32(CAN_ACCFILT_RAM_BASE + (cyg_uint32)insert_address, lsc_val.dword); + lsc_val.column.upper = lsc_val.column.lower; + HAL_WRITE_UINT32(CAN_ACCFILT_RAM_BASE + (cyg_uint32)insert_address, lsc_val.dword); + } + + // + // If we inserted a new line, then we have an odd number of identifiers now + // and need to disable the last (the upper) entry + // + if (entry_size) + { + lsc_val.column.upper = 0xFFFF; // disable the entry + HAL_WRITE_UINT32(CAN_ACCFILT_RAM_BASE + sff_grp_sa - sizeof(cyg_uint32) , lsc_val.dword); + } + } + + HAL_WRITE_UINT32(CAN_ACCFILT_SFF_SA, sff_sa); + HAL_WRITE_UINT32(CAN_ACCFILT_SFF_GRP_SA, sff_grp_sa); + HAL_WRITE_UINT32(CAN_ACCFILT_EFF_SA, eff_sa); + HAL_WRITE_UINT32(CAN_ACCFILT_EFF_GRP_SA, eff_grp_sa); + HAL_WRITE_UINT32(CAN_ACCFILT_ENDOFTABLE, end_of_table); +} + + +//=========================================================================== +// Query number of entries in a certain table +//=========================================================================== +static cyg_uint16 lpc2xxx_can_accfilt_get_table_entries(cyg_uint32 TableStartAddress) +{ + cyg_uint32 start; + cyg_uint32 end; + + switch (TableStartAddress) + { +#ifdef CYGOPT_IO_CAN_STD_CAN_ID + case CAN_ACCFILT_SFF_SA: + HAL_READ_UINT32(CAN_ACCFILT_SFF_SA, start); + HAL_READ_UINT32(CAN_ACCFILT_SFF_GRP_SA, end); + if (end - start) + { + lsc_buf_t data; + HAL_READ_UINT32(CAN_ACCFILT_RAM_BASE + end - sizeof(cyg_uint32), data.dword); + if (data.column.upper & ACCFILT_STD_DIS) + { + return (((end - start) >> 1) - 1); + } + } + return (end - start) >> 1; + + case CAN_ACCFILT_SFF_GRP_SA: + HAL_READ_UINT32(CAN_ACCFILT_SFF_GRP_SA, start); + HAL_READ_UINT32(CAN_ACCFILT_EFF_SA, end); + return (end - start) >> 2; +#endif // CYGOPT_IO_CAN_STD_CAN_ID + +#ifdef CYGOPT_IO_CAN_EXT_CAN_ID + case CAN_ACCFILT_EFF_SA: + HAL_READ_UINT32(CAN_ACCFILT_EFF_SA, start); + HAL_READ_UINT32(CAN_ACCFILT_EFF_GRP_SA, end); + return (end - start) >> 2; + + case CAN_ACCFILT_EFF_GRP_SA: + HAL_READ_UINT32(CAN_ACCFILT_EFF_GRP_SA, start); + HAL_READ_UINT32(CAN_ACCFILT_ENDOFTABLE, end); + return (end - start) >> 3; +#endif // CYGOPT_IO_CAN_EXT_CAN_ID + default: + CYG_FAIL("Invalid identifier table address"); + return 0; + } // switch (TableStartAddress) +} + +//=========================================================================== +// Query certain entry from table +//=========================================================================== +static void lpc2xxx_can_accfilt_get_entry(cyg_uint32 TableStartAddress, cyg_uint16 EntryNo, lpc2xxx_accfilt_entry_t *pEntry) +{ + cyg_uint32 EntryAddress = lpc2xxx_can_accfilt_calc_entry_address(TableStartAddress, EntryNo); + lsc_buf_t Data; + + HAL_READ_UINT32(CAN_ACCFILT_RAM_BASE + EntryAddress, Data.dword); + pEntry->data = Data.dword; + switch (TableStartAddress) + { +#ifdef CYGOPT_IO_CAN_STD_CAN_ID + case CAN_ACCFILT_SFF_SA: + { + cyg_uint16 column; + if (EntryNo % 2) + { + column = Data.column.upper; + } + else + { + column = Data.column.lower; + } + pEntry->id = ACCFILT_STD_GET_ID(column); + pEntry->channel_no = ACCFILT_STD_GET_CTRL(column); + } + break; + + case CAN_ACCFILT_SFF_GRP_SA: + pEntry->lower_id_bound = ACCFILT_STD_GET_ID(Data.column.lower); + pEntry->upper_id_bound = ACCFILT_STD_GET_ID(Data.column.upper); + pEntry->channel_no = ACCFILT_STD_GET_CTRL(Data.column.lower); + break; +#endif // #ifdef CYGOPT_IO_CAN_STD_CAN_ID + +#ifdef CYGOPT_IO_CAN_EXT_CAN_ID + case CAN_ACCFILT_EFF_SA: + pEntry->id = ACCFILT_EXT_GET_ID(Data.dword); + pEntry->channel_no = ACCFILT_EXT_GET_CTRL(Data.dword); + break; + + case CAN_ACCFILT_EFF_GRP_SA: + pEntry->lower_id_bound = ACCFILT_EXT_GET_ID(Data.dword); + pEntry->channel_no = ACCFILT_EXT_GET_CTRL(Data.dword); + HAL_READ_UINT32(CAN_ACCFILT_RAM_BASE+ EntryAddress + sizeof(cyg_uint32), Data.dword); + pEntry->upper_id_bound = ACCFILT_EXT_GET_ID(Data.dword); + break; +#endif // #ifedf CYGOPT_IO_CAN_EXT_CAN_ID + default: + CYG_FAIL("Invalid identifier table address"); + } // switch () +} + + +//=========================================================================== +// Set certain entry in table +//=========================================================================== +static void lpc2xxx_can_accfilt_set_entry(cyg_uint32 TableStartAddress, cyg_uint16 EntryNo, lpc2xxx_accfilt_entry_t *pEntry) +{ + cyg_uint32 EntryAddress = lpc2xxx_can_accfilt_calc_entry_address(TableStartAddress, EntryNo); + lsc_buf_t Data; + + switch (TableStartAddress) + { +#ifdef CYGOPT_IO_CAN_STD_CAN_ID + case CAN_ACCFILT_SFF_SA: + { + HAL_READ_UINT32(CAN_ACCFILT_RAM_BASE + EntryAddress, Data.dword); + if (EntryNo % 2) + { + Data.column.upper = (pEntry->channel_no << 13) | (pEntry->id & ACCFILT_STD_ID_MASK); + } + else + { + Data.column.lower = (pEntry->channel_no << 13) | (pEntry->id & ACCFILT_STD_ID_MASK); + } + } + break; + + case CAN_ACCFILT_SFF_GRP_SA: + Data.column.lower = (pEntry->channel_no << 13) | (pEntry->lower_id_bound & ACCFILT_STD_ID_MASK); + Data.column.upper = (pEntry->channel_no << 13) | (pEntry->upper_id_bound & ACCFILT_STD_ID_MASK); + break; +#endif // #ifdef CYGOPT_IO_CAN_STD_CAN_ID + +#ifdef CYGOPT_IO_CAN_EXT_CAN_ID + case CAN_ACCFILT_EFF_SA: + Data.dword = (pEntry->channel_no << 29) | (pEntry->id & ACCFILT_EXT_ID_MASK); + break; + + case CAN_ACCFILT_EFF_GRP_SA: + { + lsc_buf_t Data2; + + Data.dword = (pEntry->channel_no << 29) | (pEntry->lower_id_bound & ACCFILT_EXT_ID_MASK); + Data2.dword = (pEntry->channel_no << 29) | (pEntry->upper_id_bound & ACCFILT_EXT_ID_MASK); + HAL_WRITE_UINT32(CAN_ACCFILT_RAM_BASE + EntryAddress + sizeof(cyg_uint32), Data2.dword); + } + break; +#endif // #ifdef CYGOPT_IO_CAN_EXT_CAN_ID + + default: + CYG_FAIL("Invalid identifier table address"); + } // switch () + + HAL_WRITE_UINT32(CAN_ACCFILT_RAM_BASE + EntryAddress, Data.dword); +} + + +//=========================================================================== +// Add one entry to acceptance filter RAM +// If upper ID is > lower ID then we have to add a group filter - else we +// have to add a single message filter here +//=========================================================================== +static bool lpc2xxx_can_accfilt_add(lpc2xxx_can_info_t *info, + cyg_uint32 lower_id, + cyg_uint32 upper_id, + cyg_can_id_type ext) +{ + cyg_uint32 accfilt_bck; // acceptance filter backup + cyg_uint32 end_of_table; + cyg_uint32 table; + lpc2xxx_accfilt_entry_t entry; + lpc2xxx_accfilt_entry_t new_entry; + + + // + // first step: disable acceptance filter and prepare it for modification + // + HAL_READ_UINT32(CAN_ACCFILT_AFMR, accfilt_bck); + HAL_WRITE_UINT32(CAN_ACCFILT_AFMR, AFMR_OFF | AFMR_BYPASS); + + // + // Check if table is full + // + HAL_READ_UINT32(CAN_ACCFILT_ENDOFTABLE, end_of_table); + if (end_of_table >= ACCFILT_RAM_SIZE) + { + return false; + } + + new_entry.id = lower_id; + new_entry.lower_id_bound = lower_id; + new_entry.upper_id_bound = upper_id; + + // + // Here we rely on the ISR vector ordering for calculaion of of channel number + // Maybe this is not the right way for newer LPC parts + // + new_entry.channel_no = (CAN_ISRVEC(info) - CYGNUM_HAL_INTERRUPT_CAN1_TX) + 1; + + // + // If lower_id == upper_id then we know that we have to setup a single message filter + // here. If it is not equal the it is group of identifiers to receive + // + if ((lower_id == upper_id) || (lower_id > upper_id)) + { + // + // setup single message filter (standard or extended) here + // +#ifdef CYGOPT_IO_CAN_EXT_CAN_ID + if (ext) + { + table = CAN_ACCFILT_EFF_SA; + } + else +#endif // #ifdef CYGOPT_IO_CAN_EXT_CAN_ID + { +#ifdef CYGOPT_IO_CAN_STD_CAN_ID + table = CAN_ACCFILT_SFF_SA; +#endif // CYGOPT_IO_CAN_STD_CAN_ID + } + } + else + { + // + // setup single message filter (standard or extended) here + // +#ifdef CYGOPT_IO_CAN_EXT_CAN_ID + if (ext) + { + table = CAN_ACCFILT_EFF_GRP_SA; + } + else +#endif // #ifdef CYGOPT_IO_CAN_EXT_CAN_ID + { +#ifdef CYGOPT_IO_CAN_STD_CAN_ID + table = CAN_ACCFILT_SFF_GRP_SA; +#endif // #ifdef CYGOPT_IO_CAN_STD_CAN_ID + } + } + + cyg_uint16 entries = lpc2xxx_can_accfilt_get_table_entries(table); + cyg_uint16 i; + + + for (i = 0; i < entries; ++i) + { + lpc2xxx_can_accfilt_get_entry(table, i, &entry); + + if (entry.channel_no > new_entry.channel_no) + { + break; + } + + if ((entry.channel_no == new_entry.channel_no) + && (entry.id > new_entry.id)) + { + break; + } + } // for (i = 0; i < entries; ++i) + + lpc2xxx_can_accfilt_ram_insert_entry(table, i); + lpc2xxx_can_accfilt_set_entry(table, i, &new_entry); + + // + // finally restore the prvious state of the acceptance filter + // + HAL_WRITE_UINT32(CAN_ACCFILT_AFMR, accfilt_bck); + return true; +} + + +//=========================================================================== +// Remove all entries from a certain controller +//=========================================================================== +void lpc2xxx_can_accfilt_remove_all_ctrl_entries(lpc2xxx_can_info_t *info) +{ + cyg_uint32 accfilt_bck; // acceptance filter backup + cyg_uint16 i; + cyg_uint16 entries; + cyg_uint32 TableStartAddress = CAN_ACCFILT_SFF_SA; + lpc2xxx_accfilt_entry_t Entry; + cyg_uint8 channel_no = (CAN_ISRVEC(info) - CYGNUM_HAL_INTERRUPT_CAN1_TX) + 1; + cyg_uint16 entry_idx; + + // + // first step: disable acceptance filter and prepare it for modification + // + HAL_READ_UINT32(CAN_ACCFILT_AFMR, accfilt_bck); + HAL_WRITE_UINT32(CAN_ACCFILT_AFMR, AFMR_OFF | AFMR_BYPASS); + + // + // now remove all entries for a certain controller + // + for (TableStartAddress = CAN_ACCFILT_SFF_SA; TableStartAddress < CAN_ACCFILT_ENDOFTABLE; TableStartAddress += 4) + { + entries = lpc2xxx_can_accfilt_get_table_entries(TableStartAddress); + entry_idx = 0; + for (i = 0; i < entries; ++i) + { + lpc2xxx_can_accfilt_get_entry(TableStartAddress, entry_idx, &Entry); + if (Entry.channel_no == channel_no) + { + lpc2xxx_can_accfilt_ram_remove_entry(TableStartAddress, entry_idx); + } + else + { + entry_idx++; + } + } // for (i = 0; i < entries; ++i) + } // for (TableStartAddress = CAN_ACCFILT_SFF_SA ... + + // + // finally restore the prvious state of the acceptance filter + // + HAL_WRITE_UINT32(CAN_ACCFILT_AFMR, accfilt_bck); +} +#endif // CYGOPT_IO_CAN_RUNTIME_MBOX_CFG + + +#ifndef CYGOPT_IO_CAN_RUNTIME_MBOX_CFG +//=========================================================================== +// Setup reception of all CAN identifiers +// If runtime acceptance filter configuration is not required then we simply +// setup the acceptance filter here to receive all CAN identifiers +//=========================================================================== +static void lpc2xxx_can_accfilt_simple_rx_all(void) +{ + cyg_uint32 regval; + + // + // First check if it is really necessary to setup filters. If end of table is + // != 0 then the acceptance filter is already setup properly + // + HAL_READ_UINT32(CAN_ACCFILT_ENDOFTABLE, regval); + if (regval) + { + return; + } + + cyg_uint32 accfilt_bck; // acceptance filter backup + cyg_uint8 i = 0; // loop counter + lsc_buf_t accfilt_entry; // std group entry +#ifdef CYGOPT_IO_CAN_STD_CAN_ID + cyg_uint8 std_address = 0; // std group entry address +#ifdef CYGOPT_IO_CAN_EXT_CAN_ID + cyg_uint8 ext_address = lpc2xxx_global_can_info.init_cnt << 2; +#endif +#else + cyg_uint8 ext_address = 0; +#endif + + // + // first step: disable acceptance filter and prepare it for modification + // + HAL_READ_UINT32(CAN_ACCFILT_AFMR, accfilt_bck); + HAL_WRITE_UINT32(CAN_ACCFILT_AFMR, AFMR_OFF | AFMR_BYPASS); + + // + // Write table start adresses - we use only standard group and extended filter + // group + // + HAL_WRITE_UINT32(CAN_ACCFILT_SFF_SA, 0); + HAL_WRITE_UINT32(CAN_ACCFILT_SFF_GRP_SA, 0); + HAL_WRITE_UINT32(CAN_ACCFILT_EFF_SA, ext_address); + HAL_WRITE_UINT32(CAN_ACCFILT_EFF_GRP_SA, ext_address); + + // + // Now loop through all active CAN channels and setup the acceptance filter for + // each channel to receive all standard and extended CAN identifiers + // + while (lpc2xxx_global_can_info.active_channels[i]) + { + lpc2xxx_can_info_t *info = (lpc2xxx_can_info_t *)lpc2xxx_global_can_info.active_channels[i++]->dev_priv; + cyg_uint8 channel_no = (CAN_ISRVEC(info) - CYGNUM_HAL_INTERRUPT_CAN1_TX) + 1; + +#ifdef CYGOPT_IO_CAN_STD_CAN_ID + accfilt_entry.column.lower = (channel_no << 13) | (0x000 & ACCFILT_STD_ID_MASK); + accfilt_entry.column.upper = (channel_no << 13) | (0x7FF & ACCFILT_STD_ID_MASK); + HAL_WRITE_UINT32(CAN_ACCFILT_RAM_BASE + std_address, accfilt_entry.dword); + std_address += sizeof(cyg_uint32); +#endif // CYGOPT_IO_CAN_STD_CAN_ID + +#ifdef CYGOPT_IO_CAN_EXT_CAN_ID + accfilt_entry.dword = (channel_no << 29) | (0x00000000 & ACCFILT_EXT_ID_MASK); + HAL_WRITE_UINT32(CAN_ACCFILT_RAM_BASE + ext_address, accfilt_entry.dword); + ext_address += sizeof(cyg_uint32); + accfilt_entry.dword = (channel_no << 29) | (0x1FFFFFFF & ACCFILT_EXT_ID_MASK); + HAL_WRITE_UINT32(CAN_ACCFILT_RAM_BASE + ext_address, accfilt_entry.dword); + ext_address += sizeof(cyg_uint32); +#endif // CYGOPT_IO_CAN_EXT_CAN_ID + } // while (lpc2xxx_global_can_info.active_channels[i]) + + // + // finally store end of table value and restore the previous state of the + // acceptance filter + // + HAL_WRITE_UINT32(CAN_ACCFILT_ENDOFTABLE, ext_address); + HAL_WRITE_UINT32(CAN_ACCFILT_AFMR, accfilt_bck); +} +#endif // CYGOPT_IO_CAN_RUNTIME_MBOX_CFG + + +//=========================================================================== +// Reset acceptance filter to poweron defaults +//=========================================================================== +void lpc2xxx_can_accfilt_reset(void) +{ + cyg_uint32 accfilt_bck; // acceptance filter backup + // + // first step: disable acceptance filter and prepare it for modification + // + HAL_READ_UINT32(CAN_ACCFILT_AFMR, accfilt_bck); + HAL_WRITE_UINT32(CAN_ACCFILT_AFMR, AFMR_OFF | AFMR_BYPASS); + + // + // Now write zero to all addresses of acceptance filter table + // + HAL_WRITE_UINT32(CAN_ACCFILT_SFF_SA, 0); + HAL_WRITE_UINT32(CAN_ACCFILT_SFF_GRP_SA, 0); + HAL_WRITE_UINT32(CAN_ACCFILT_EFF_SA, 0); + HAL_WRITE_UINT32(CAN_ACCFILT_EFF_GRP_SA, 0); + HAL_WRITE_UINT32(CAN_ACCFILT_ENDOFTABLE, 0); + + // + // finally restore the prvious state of the acceptance filter + // + HAL_WRITE_UINT32(CAN_ACCFILT_AFMR, accfilt_bck); +} + + +//=========================================================================== +// Dump content of acceptance filter lookup table +//=========================================================================== +#ifdef CYGDBG_DEVS_CAN_LPC2XXX_DEBUG +void lpc2xxx_can_accfilt_dbg_dump(void) +{ + cyg_uint32 sff_sa; + cyg_uint32 sff_grp_sa; + cyg_uint32 eff_sa; + cyg_uint32 eff_grp_sa; + cyg_uint32 end_of_table; + cyg_uint32 entry_address; + lsc_buf_t data; + + + HAL_READ_UINT32(CAN_ACCFILT_SFF_SA, sff_sa); + HAL_READ_UINT32(CAN_ACCFILT_SFF_GRP_SA, sff_grp_sa); + HAL_READ_UINT32(CAN_ACCFILT_EFF_SA, eff_sa); + HAL_READ_UINT32(CAN_ACCFILT_EFF_GRP_SA, eff_grp_sa); + HAL_READ_UINT32(CAN_ACCFILT_ENDOFTABLE, end_of_table); + + entry_address = sff_sa; + + // + // Print lookup table registers + // + diag_printf("\n\nDUMP CAN ACCEPTANCE FILTER REGISTERS\n"); + diag_printf("----------------------------------------\n"); + diag_printf("SFF_sa:\t\t0x%08x\n", sff_sa); + diag_printf("SFF_GRP_sa:\t0x%08x\n", sff_grp_sa); + diag_printf("EFF_sa:\t\t0x%08x\n", eff_sa); + diag_printf("EFF_GRP_sa:\t0x%08x\n", eff_grp_sa); + diag_printf("EOT:\t\t0x%08x\n", end_of_table); + + // + // Print table of standard identifiers + // + diag_printf("\n\nDUMP CAN LOOKUP TABLE RAM"); + diag_printf("\nSFF_sa\t\tcolumn_lower\tcolumn_upper\traw_data\n"); + diag_printf("----------------------------------------------------------\n"); + while (entry_address < sff_grp_sa) + { + HAL_READ_UINT32(CAN_ACCFILT_RAM_BASE + entry_address, data.dword); + diag_printf("0x%04x:\t\t0x%x\t\t0x%x\t\t0x%x\n", entry_address, data.column.lower, data.column.upper, data.dword); + entry_address += sizeof(cyg_uint32); + } + + // + // Print table of standard identifier groups + // + diag_printf("\nSFF_GRP_sa\tcolumn_lower\tcolumn_upper\traw_data\n"); + diag_printf("----------------------------------------------------------\n"); + while (entry_address < eff_sa) + { + HAL_READ_UINT32(CAN_ACCFILT_RAM_BASE + entry_address, data.dword); + diag_printf("0x%04x:\t\t0x%x\t\t0x%x\t\t0x%x\n", entry_address, data.column.lower, data.column.upper, data.dword); + entry_address += sizeof(cyg_uint32); + } + + // + // Print table of extended identifiers + // + diag_printf("\nEFF_sa\t\t-\t\t-\t\traw_data\n"); + diag_printf("----------------------------------------------------------\n"); + while (entry_address < eff_grp_sa) + { + HAL_READ_UINT32(CAN_ACCFILT_RAM_BASE + entry_address, data.dword); + diag_printf("0x%04x:\t\t\t\t\t\t0x%x\n", entry_address, data.dword); + entry_address += sizeof(cyg_uint32); + } + + // + // Print table of extended identifier groups + // + diag_printf("\nEFF_GRP_sa\t-\t\t-\t\traw_data\n"); + diag_printf("----------------------------------------------------------\n"); + while (entry_address < end_of_table) + { + HAL_READ_UINT32(CAN_ACCFILT_RAM_BASE + entry_address, data.dword); + diag_printf("0x%04x:\t\t\t\t\t\t0x%x\n", entry_address, data.dword); + entry_address += sizeof(cyg_uint32); + } +} +#endif // CYGDBG_DEVS_CAN_LPC2XXX_DEBUG + +//=========================================================================== +// Dump content of acceptance filter lookup table +//=========================================================================== +#ifdef CYGDBG_DEVS_CAN_LPC2XXX_DEBUG +void lpc2xxx_can_reg_dump(struct cyg_devtab_entry* devtab_entry) +{ + can_channel *chan = (can_channel*)devtab_entry->priv; + cyg_uint32 reg_val; + CAN_DECLARE_INFO(chan); + + chan = chan; // avoid compiler warnings for unused variables + // + // Print table of extended identifier groups + // + diag_printf("\n\nCAN REGISTER DUMP\n"); + diag_printf("\nRegister\tValue\n"); + diag_printf("----------------------------------------------------------\n"); + HAL_READ_UINT32(CAN_CTRL_MOD(info), reg_val); + diag_printf("CANMOD\t\t0x%08x\n", reg_val); + HAL_READ_UINT32(CAN_CTRL_CMR(info), reg_val); + diag_printf("CANCMR\t\t0x%08x\n", reg_val); + HAL_READ_UINT32(CAN_CTRL_GSR(info), reg_val); + diag_printf("CANGSR\t\t0x%08x\n", reg_val); + HAL_READ_UINT32(CAN_CTRL_ICR(info), reg_val); + diag_printf("CANICR\t\t0x%08x\n", reg_val); + HAL_READ_UINT32(CAN_CTRL_IER(info), reg_val); + diag_printf("CANIER\t\t0x%08x\n", reg_val); + HAL_READ_UINT32(CAN_CTRL_BTR(info), reg_val); + diag_printf("CANBTR\t\t0x%08x\n", reg_val); + HAL_READ_UINT32(CAN_CTRL_EWL(info), reg_val); + diag_printf("CANEWL\t\t0x%08x\n", reg_val); + HAL_READ_UINT32(CAN_CTRL_SR(info), reg_val); + diag_printf("CANSR\t\t0x%08x\n", reg_val); + HAL_READ_UINT32(CAN_CTRL_RFS(info), reg_val); + diag_printf("CANRFS\t\t0x%08x\n", reg_val); + HAL_READ_UINT32(CAN_CTRL_RID(info), reg_val); + diag_printf("CANRID\t\t0x%08x\n", reg_val); + HAL_READ_UINT32(CAN_CTRL_RDA(info), reg_val); + diag_printf("CANRDA\t\t0x%08x\n", reg_val); + HAL_READ_UINT32(CAN_CTRL_RDB(info), reg_val); + diag_printf("CANRDB\t\t0x%08x\n", reg_val); + + diag_printf("\n\nCAN CENTRAL REGISTER DUMP\n"); + diag_printf("\nRegister\tValue\n"); + diag_printf("----------------------------------------------------------\n"); + HAL_READ_UINT32(CAN_CENTRAL_TXSR, reg_val); + diag_printf("CANTxSR\t\t0x%08x\n", reg_val); + HAL_READ_UINT32(CAN_CENTRAL_RXSR, reg_val); + diag_printf("CANRxSR\t\t0x%08x\n", reg_val); + HAL_READ_UINT32(CAN_CENTRAL_MSR, reg_val); + diag_printf("CANMSR\t\t0x%08x\n", reg_val); + + diag_printf("\n\nCAN ACCEPTANCE FILTER REGISTER DUMP\n"); + diag_printf("\nRegister\tValue\n"); + diag_printf("----------------------------------------------------------\n"); + HAL_READ_UINT32(CAN_ACCFILT_AFMR, reg_val); + diag_printf("AFMR\t\t0x%08x\n", reg_val); + HAL_READ_UINT32(CAN_ACCFILT_LUT_ERR, reg_val); + diag_printf("LUTERR\t\t0x%08x\n", reg_val); + HAL_READ_UINT32(CAN_ACCFILT_LUT_ERR_ADDR, reg_val); + diag_printf("LUTERRADDR\t0x%08x\n", reg_val); +} +#endif // #ifdef CYGDBG_DEVS_CAN_LPC2XXX_DEBUG + +//--------------------------------------------------------------------------- +// EOF can_accfilt_lpc2xxx.c
new file mode 100644 --- /dev/null +++ b/packages/devs/can/arm/lpc2xxx/current/src/can_lpc2xxx.c @@ -0,0 +1,1863 @@ +//========================================================================== +// +// devs/can/arm/lpc2xxx/current/src/can_lpc2xxx.c +// +// CAN driver for LPC2xxx microcontrollers +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. +// Copyright (C) 2003 Gary Thomas +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos 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 General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. +// at http://sources.redhat.com/ecos/ecos-license/ +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): Uwe Kindler +// Contributors: Uwe Kindler +// Date: 2007-04-09 +// Purpose: Support LPC2xxx on-chip CAN moduls +// Description: +// +//####DESCRIPTIONEND#### +// +//========================================================================== + + +//========================================================================== +// INCLUDES +//========================================================================== +#include <pkgconf/system.h> +#include <pkgconf/io_can.h> +#include <pkgconf/io.h> +#include <pkgconf/devs_can_lpc2xxx.h> + +#include <cyg/infra/diag.h> + +#include <cyg/io/io.h> +#include <cyg/io/devtab.h> +#include <cyg/io/can.h> +#include <cyg/io/can_lpc2xxx.h> +#include <cyg/io/can_lpc2xxx_baudrates.h> + +#include <cyg/hal/hal_arch.h> +#include <cyg/hal/hal_intr.h> +#include <cyg/hal/hal_io.h> + +#include <cyg/hal/hal_diag.h> +#include <cyg/infra/cyg_ass.h> + + +//=========================================================================== +// DEFINES +//=========================================================================== +// +// Check if the macro HAL_LPC2XXX_GET_CAN_BR is provided +// +#ifndef HAL_LPC2XXX_GET_CAN_BR +#error "Macro HAL_LPC2XXX_GET_CAN_BR() missing" +#endif + +// +// Support debug output if this option is enabled in CDL file +// +#ifdef CYGDBG_DEVS_CAN_LPC2XXX_DEBUG +#define LPC2XXX_DBG_PRINT diag_printf +#else +#define LPC2XXX_DBG_PRINT( fmt, ... ) +#endif + + +//--------------------------------------------------------------------------- +// we define our own set of register bits in order to be independent from +// platform specific names +// + +//--------------------------------------------------------------------------- +// Memory map of CAN block +// +#define CAN_ACCFILT_RAM_BASE 0xE0038000 +#define CAN_ACCFILT_REG_BASE 0xE003C000 +#define CAN_CENTRAL_REG_BASE 0xE0040000 +#define CAN_CTRL_1_REG_BASE 0xE0044000 +#define CAN_CTRL_2_REG_BASE 0xE0048000 +#define CAN_CTRL_3_REG_BASE 0xE004C000 +#define CAN_CTRL_4_REG_BASE 0xE0050000 + + +//--------------------------------------------------------------------------- +// CAN Acceptance Filter register layout +// +#define CAN_ACCFILT_AFMR (CAN_ACCFILT_REG_BASE + 0x0000) +#define CAN_ACCFILT_SFF_SA (CAN_ACCFILT_REG_BASE + 0x0004) +#define CAN_ACCFILT_SFF_GRP_SA (CAN_ACCFILT_REG_BASE + 0x0008) +#define CAN_ACCFILT_EFF_SA (CAN_ACCFILT_REG_BASE + 0x000C) +#define CAN_ACCFILT_EFF_GRP_SA (CAN_ACCFILT_REG_BASE + 0x0010) +#define CAN_ACCFILT_ENDOFTABLE (CAN_ACCFILT_REG_BASE + 0x0014) +#define CAN_ACCFILT_LUT_ERR_ADDR (CAN_ACCFILT_REG_BASE + 0x0018) +#define CAN_ACCFILT_LUT_ERR (CAN_ACCFILT_REG_BASE + 0x001C) + +//--------------------------------------------------------------------------- +// CAN_ACCFILT_AFMR Bits +// +#define AFMR_OFF 0x00000001 // 1 = Acceptance filter is not operational +#define AFMR_BYPASS 0x00000002 // 1 = all Rx messages are accepted on enabled CAN controllers. +#define AFMR_FULLCAN 0x00000004 // 1 = FullCAN mode +#define AFMR_ON 0x00000000 // Acceptance filter on +#define ACCFILT_RAM_SIZE 2048 // size of acceptance filter ram + + +//--------------------------------------------------------------------------- +// Acceptance filter tool macros +// +#define ACCFILT_STD_ID_MASK 0x7FF +#define ACCFILT_EXT_ID_MASK 0x1FFFFFFF +#define ACCFILT_STD_DIS 0x1000 +#define ACCFILT_STD_CTRL_MASK 0xE000 +#define ACCFILT_EXT_CTRL_MASK 0xE0000000 +#define ACCFILT_STD_GET_CTRL(_entry_) (((_entry_) >> 13) & 0x7) +#define ACCFILT_STD_GET_CTRL_LOWER(_entry_) (((_entry_) >> 29) & 0x7) +#define ACCFILT_STD_GET_CTRL_UPPER(_entry_) (((_entry_) >> 13) & 0x7) +#define ACCFILT_STD_GET_ID(_entry_) ((_entry_) & ACCFILT_STD_ID_MASK) +#define ACCFILT_EXT_GET_ID(_entry_) ((_entry_) & ACCFILT_EXT_ID_MASK) +#define ACCFILT_EXT_GET_CTRL(_entry_) (((_entry_) >> 29) & 0x7) +#define ACCFILT_EXT_SET_CTRL(_entry_, _ctrl_) ((_entry_ & 0xE0000000) | ((_ctrl_) << 29)) + + +//--------------------------------------------------------------------------- +// CAN Central CAN Registers register layout +// +#define CAN_CENTRAL_TXSR (CAN_CENTRAL_REG_BASE + 0x0000) +#define CAN_CENTRAL_RXSR (CAN_CENTRAL_REG_BASE + 0x0004) +#define CAN_CENTRAL_MSR (CAN_CENTRAL_REG_BASE + 0x0008) + + +//--------------------------------------------------------------------------- +// CAN Controller register offsets +// Registers are offsets from base CAN module control register +// +#define CANREG_MOD 0x0000 +#define CANREG_CMR 0x0004 +#define CANREG_GSR 0x0008 +#define CANREG_ICR 0x000C +#define CANREG_IER 0x0010 +#define CANREG_BTR 0x0014 +#define CANREG_EWL 0x0018 +#define CANREG_SR 0x001C +#define CANREG_RFS 0x0020 +#define CANREG_RID 0x0024 +#define CANREG_RDA 0x0028 +#define CANREG_RDB 0x002C +#define CANREG_TFI1 0x0030 +#define CANREG_TID1 0x0034 +#define CANREG_TDA1 0x0038 +#define CANREG_TDB1 0x003C +#define CANREG_TFI2 0x0040 +#define CANREG_TID2 0x0044 +#define CANREG_TDA2 0x0048 +#define CANREG_TDB2 0x004C +#define CANREG_TFI3 0x0050 +#define CANREG_TID3 0x0054 +#define CANREG_TDA3 0x0058 +#define CANREG_TDB3 0x005C + + +//--------------------------------------------------------------------------- +// CAN Controller register layout +// +#define CAN_CTRL_MOD(_extra_) (CAN_CTRL_BASE(_extra_) + CANREG_MOD) +#define CAN_CTRL_CMR(_extra_) (CAN_CTRL_BASE(_extra_) + CANREG_CMR) +#define CAN_CTRL_GSR(_extra_) (CAN_CTRL_BASE(_extra_) + CANREG_GSR) +#define CAN_CTRL_ICR(_extra_) (CAN_CTRL_BASE(_extra_) + CANREG_ICR) +#define CAN_CTRL_IER(_extra_) (CAN_CTRL_BASE(_extra_) + CANREG_IER) +#define CAN_CTRL_BTR(_extra_) (CAN_CTRL_BASE(_extra_) + CANREG_BTR) +#define CAN_CTRL_EWL(_extra_) (CAN_CTRL_BASE(_extra_) + CANREG_EWL) +#define CAN_CTRL_SR(_extra_) (CAN_CTRL_BASE(_extra_) + CANREG_SR) +#define CAN_CTRL_RFS(_extra_) (CAN_CTRL_BASE(_extra_) + CANREG_RFS) +#define CAN_CTRL_RID(_extra_) (CAN_CTRL_BASE(_extra_) + CANREG_RID) +#define CAN_CTRL_RDA(_extra_) (CAN_CTRL_BASE(_extra_) + CANREG_RDA) +#define CAN_CTRL_RDB(_extra_) (CAN_CTRL_BASE(_extra_) + CANREG_RDB) +#define CAN_CTRL_TFI1(_extra_) (CAN_CTRL_BASE(_extra_) + CANREG_TFI1) +#define CAN_CTRL_TID1(_extra_) (CAN_CTRL_BASE(_extra_) + CANREG_TID1) +#define CAN_CTRL_TDA1(_extra_) (CAN_CTRL_BASE(_extra_) + CANREG_TDA1) +#define CAN_CTRL_TDB1(_extra_) (CAN_CTRL_BASE(_extra_) + CANREG_TDB1) +#define CAN_CTRL_TFI2(_extra_) (CAN_CTRL_BASE(_extra_) + CANREG_TFI2) +#define CAN_CTRL_TID2(_extra_) (CAN_CTRL_BASE(_extra_) + CANREG_TID2) +#define CAN_CTRL_TDA2(_extra_) (CAN_CTRL_BASE(_extra_) + CANREG_TDA2) +#define CAN_CTRL_TDB2(_extra_) (CAN_CTRL_BASE(_extra_) + CANREG_TDB2) +#define CAN_CTRL_TFI3(_extra_) (CAN_CTRL_BASE(_extra_) + CANREG_TFI3) +#define CAN_CTRL_TID3(_extra_) (CAN_CTRL_BASE(_extra_) + CANREG_TID3) +#define CAN_CTRL_TDA3(_extra_) (CAN_CTRL_BASE(_extra_) + CANREG_TDA3) +#define CAN_CTRL_TDB3(_extra_) (CAN_CTRL_BASE(_extra_) + CANREG_TDB3) + + +//--------------------------------------------------------------------------- +// CAN_CTRL_ICR register bits +// +#define ICR_RX 0x00000001 +#define ICR_TX1 0x00000002 +#define ICR_ERR_WARN 0x00000004 +#define ICR_DATA_OVR 0x00000008 +#define ICR_WAKE_UP 0x00000010 +#define ICR_ERR_PASSIVE 0x00000020 +#define ICR_ARBITR_LOST 0x00000040 +#define ICR_BUS_ERR 0x00000080 +#define ICR_ID_READY 0x00000100 +#define ICR_TX2 0x00000200 +#define ICR_TX3 0x00000400 +#define ICR_GET_ERRBIT(_icr_) (((_icr_) >> 16) & 0x1F) +#define ICR_ERR_DIRECTION 0x00200000 +#define ICR_GET_ERRCODE(_icr_) (((_icr_) >> 22) & 0x03) +#define ICR_GET_ALCBIT(_icr_) (((_icr_) >> 24) & 0x1F) + +#define CAN_ALL_ERR_INT (ICR_ERR_PASSIVE | ICR_ARBITR_LOST | ICR_BUS_ERR | ICR_ERR_WARN) +#define CAN_MISC_INT (CAN_ALL_ERR_INT | ICR_WAKE_UP) + + +//--------------------------------------------------------------------------- +// CAN_CTRL_ICR register bits +// +#define ICR_ERRCODE_BIT_ERR 0x00 +#define ICR_ERRCODE_FORM_ERR 0x01 +#define ICR_ERRCODE_STUFF_ERR 0x02 +#define ICR_ERRCODE_OTHER_ERR 0x03 + + +//--------------------------------------------------------------------------- +// CAN_CTRL_RFS register bits +// +#define RFS_ACCFILT_INDEX_MASK 0x000003FF +#define RFS_RECEIVED_IN_BYPASS_MODE 0x00000400 +#define RFS_DLC_MASK 0x000F0000 +#define RFS_RTR 0x40000000 +#define RFS_EXT 0x80000000 +#define RFS_GET_DLC(_regval_) (((_regval_) >> 16) & 0xF) + +//--------------------------------------------------------------------------- +// CAN_CTRL_CMR register bits +// +#define CMR_TX_REQ 0x00000001 +#define CMR_TX_ABORT 0x00000002 +#define CMR_RX_RELEASE_BUF 0x00000004 +#define CMR_CLEAR_DATA_OVR 0x00000008 +#define CMR_SELF_RX_REQ 0x00000010 +#define CMR_SEND_TX_BUF1 0x00000020 +#define CMR_SEND_TX_BUF2 0x00000040 +#define CMR_SEND_TX_BUF3 0x00000080 + + +//--------------------------------------------------------------------------- +// CAN_CTRL_TFI register bits +// +#define TFI_PRIO_MASK 0x000000FF +#define TFI_DLC_MASK 0x000F0000 +#define TFI_DLC_RTR 0x40000000 +#define TFI_DLC_EXT 0x80000000 + + +//--------------------------------------------------------------------------- +// CAN_CTRL_MOD register bits +// +#define CANMOD_OPERATIONAL 0x00000000 +#define CANMOD_RESET 0x00000001 +#define CANMOD_LISTEN_ONLY 0x00000002 +#define CANMOD_SELF_TEST 0x00000004 +#define CANMOD_TX_BUF_CFG 0x00000008 +#define CANMOD_SLEEP 0x00000010 +#define CANMOD_REV_POLARITY 0x00000020 +#define CANMOD_TEST 0x00000040 + + +//--------------------------------------------------------------------------- +// CAN_CTRL_IER register bits +// +#define IER_RX 0x00000001 +#define IER_TX1 0x00000002 +#define IER_ERR_WARN 0x00000004 +#define IER_DATA_OVR 0x00000008 +#define IER_WAKE_UP 0x00000010 +#define IER_ERR_PASSIVE 0x00000020 +#define IER_ARBITR_LOST 0x00000040 +#define IER_BUS_ERR 0x00000080 +#define IER_ID_READY 0x00000100 +#define IER_TX2 0x00000200 +#define IER_TX3 0x00000400 + + +//--------------------------------------------------------------------------- +// CAN_CTRL_GSR register bits +// +#define GSR_RX_MSG_AVAILABLE 0x00000001 +#define GSR_DATA_OVR 0x00000002 +#define GSR_TX_NOT_PENDING 0x00000004 +#define GSR_ALL_TX_COMPLETE 0x00000008 +#define GSR_RECEIVING_ACTIVE 0x00000010 +#define GSR_SENDING_ACTIVE 0x00000020 +#define GSR_ERR 0x00000040 +#define GSR_BUS_OFF 0x00000080 +#define GSR_RXERR_CNT(_reg_) (((_reg_) >> 16) & 0xFF) +#define GSR_TXERR_CNT(_reg_) (((_reg_) >> 24) & 0xFF) + + +//--------------------------------------------------------------------------- +// CAN_CTRL_SR register bits +// +#define SR_RX_MSG_AVAILABLE 0x01 +#define SR_DATA_OVR 0x02 +#define SR_TX_BUF_WRITE_OK 0x04 // TBS1, TBS2, TBS3 (Bit 2, 10, 18) +#define SR_TX_COMPLETE 0x08 // TCS1, TCS2, TCS3 (Bit 3, 11, 19) +#define SR_RECEIVING_ACTIVE 0x10 +#define SR_SENDING_ACTIVE 0x20 // TS1, TS2, TS3 (5, 13, 21) +#define SR_ERR 0x40 +#define SR_BUS_OFF 0x80 + + +//--------------------------------------------------------------------------- +// Optimize for the case of a single CAN channel, while still allowing +// multiple channels. +// +#if CYGINT_IO_CAN_CHANNELS == 1 +#define CAN_CTRL_BASE(_extra_) CAN_CTRL_SINGLETON_BASE +#define CAN_ISRVEC(_extra_) CAN_SINGLETON_ISRVEC +#define CAN_DECLARE_INFO(_chan_) +#define CAN_DECLARE_CHAN(_data_) +#else +#define CAN_CTRL_BASE(_extra_) ((_extra_)->base) +#define CAN_ISRVEC(_extra_) ((_extra_)->isrvec) +#define CAN_DECLARE_INFO(_chan_) lpc2xxx_can_info_t *info = (lpc2xxx_can_info_t *)chan->dev_priv; +#define CAN_DECLARE_CHAN(_data_) can_channel *chan = (can_channel *)data; +#endif // CYGINT_IO_CAN_CHANNELS == 1 + + +#ifdef CYGOPT_DEVS_CAN_LPC2XXX_CAN0_ACCFILT_STARTUP_CFG_RX_ALL +#define CAN0_FLAG_STARTUP_ACCFILT_SETUP INFO_FLAG_STARTUP_RX_ALL +#else +#define CAN0_FLAG_STARTUP_ACCFILT_SETUP 0x00 +#endif + +#ifdef CYGOPT_DEVS_CAN_LPC2XXX_CAN1_ACCFILT_STARTUP_CFG_RX_ALL +#define CAN1_FLAG_STARTUP_ACCFILT_SETUP INFO_FLAG_STARTUP_RX_ALL +#else +#define CAN1_FLAG_STARTUP_ACCFILT_SETUP 0x00 +#endif + +#ifdef CYGOPT_DEVS_CAN_LPC2XXX_CAN2_ACCFILT_STARTUP_CFG_RX_ALL +#define CAN2_FLAG_STARTUP_ACCFILT_SETUP INFO_FLAG_STARTUP_RX_ALL +#else +#define CAN2_FLAG_STARTUP_ACCFILT_SETUP 0x00 +#endif + +#ifdef CYGOPT_DEVS_CAN_LPC2XXX_CAN3_ACCFILT_STARTUP_CFG_RX_ALL +#define CAN3_FLAG_STARTUP_ACCFILT_SETUP INFO_FLAG_STARTUP_RX_ALL +#else +#define CAN3_FLAG_STARTUP_ACCFILT_SETUP 0x00 +#endif + + +//=========================================================================== +// DATA TYPES +//=========================================================================== +// +// Structure stores LPC2xxx CAN channel related stuff +// +typedef struct lpc2xxx_can_info_st +{ + cyg_interrupt tx_interrupt; + cyg_handle_t tx_interrupt_handle; + cyg_interrupt rx_interrupt; + cyg_handle_t rx_interrupt_handle; + cyg_can_state state; // state of CAN controller + cyg_uint32 icr; // buffers icr register + cyg_uint8 flags; // flags indicating several states +#if CYGINT_IO_CAN_CHANNELS > 1 + cyg_uint32 base; // Per-bus h/w details + cyg_uint8 isrvec; // ISR vector (peripheral id) +#endif +} lpc2xxx_can_info_t; + + +#define INFO_FLAG_RX_ALL 0x01 // this bit indicates that channel receives all CAN messages - no filtering active +#define INFO_FLAG_STARTUP_RX_ALL 0x02 // this bit indicates filter state at startup + + +// +// lpc2xxx info initialisation +// +#define LPC2XXX_CTRL_NOT_INITIALIZED 0xFF +#if CYGINT_IO_CAN_CHANNELS > 1 +#define LPC2XXX_CAN_INFO(_l, _base, _isrvec, _flags) \ +lpc2xxx_can_info_t _l = { \ + state : LPC2XXX_CTRL_NOT_INITIALIZED, \ + base : (_base), \ + isrvec : (_isrvec), \ + flags : (_flags), \ + icr : 0, \ +}; +#else +#define LPC2XXX_CAN_INFO(_l, _flags) \ +lpc2xxx_can_info_t _l = { \ + state : CYGNUM_CAN_STATE_STOPPED, \ + flags : (_flags), \ +}; +#endif + + +// +// Acceptance filter data +// +typedef struct lpc2xxx_global_can_info_st +{ + cyg_interrupt err_interrupt; + cyg_handle_t err_interrupt_handle; + cyg_uint16 free_filters; // number of free message filters + cyg_uint8 init_cnt; // counts number of initialized channels + can_channel* active_channels[5]; // stores pointers to active channels - the last entry is just a delimiter +} lpc2xxx_global_can_info_t; + + +// +// The number of available message filters depends on the size of the +// acceptance filter RAM and on the size of one entry. The size of +// one entry is 4 byte (standard ID only 2 byte, extended groups 8 byte) +// +#define ACCFILT_COMMON_ENTRY_SIZE 4 +#define LPC2XXX_CAN_MSG_FILTERS_MAX (ACCFILT_RAM_SIZE / ACCFILT_COMMON_ENTRY_SIZE) +lpc2xxx_global_can_info_t lpc2xxx_global_can_info = +{ + .free_filters = LPC2XXX_CAN_MSG_FILTERS_MAX, + .init_cnt = 0, + .active_channels = {0, 0, 0, 0, 0}, +}; + + + +// +// Data type for access of single bytes/words of an dword value +// +typedef union lsc_buf_u +{ + cyg_uint8 bytes[4]; + struct + { + cyg_uint16 low; + cyg_uint16 high; + } words; + + struct + { + cyg_uint16 upper; // uppper column of acceptance filter ram + cyg_uint16 lower; // lower column of acceptance filter ram + } column; + + cyg_uint32 dword; +} lsc_buf_t; + + +//=========================================================================== +// GLOBAL DATA +//=========================================================================== +#if CYGINT_IO_CAN_CHANNELS > 1 +#ifdef CYGINT_DEVS_CAN_LPC2XXX_CAN0 +LPC2XXX_CAN_INFO(lpc2xxx_can0_info, + CAN_CTRL_1_REG_BASE, + CYGNUM_HAL_INTERRUPT_CAN1_TX, + CAN0_FLAG_STARTUP_ACCFILT_SETUP); +#endif + +#ifdef CYGINT_DEVS_CAN_LPC2XXX_CAN1 +LPC2XXX_CAN_INFO(lpc2xxx_can1_info, + CAN_CTRL_2_REG_BASE, + CYGNUM_HAL_INTERRUPT_CAN2_TX, + CAN1_FLAG_STARTUP_ACCFILT_SETUP); +#endif + +#ifdef CYGINT_DEVS_CAN_LPC2XXX_CAN2 +LPC2XXX_CAN_INFO(lpc2xxx_can2_info, + CAN_CTRL_3_REG_BASE, + CYGNUM_HAL_INTERRUPT_CAN3_TX, + CAN2_FLAG_STARTUP_ACCFILT_SETUP); +#endif + +#ifdef CYGINT_DEVS_CAN_LPC2XXX_CAN3 +LPC2XXX_CAN_INFO(lpc2xxx_can3_info, + CAN_CTRL_4_REG_BASE, + CYGNUM_HAL_INTERRUPT_CAN4_TX, + CAN3_FLAG_STARTUP_ACCFILT_SETUP); +#endif +#else // CYGINT_IO_CAN_CHANNELS == 1 +#ifdef CYGINT_DEVS_CAN_LPC2XXX_CAN0 +LPC2XXX_CAN_INFO(lpc2xxx_can0_info, CAN0_FLAG_STARTUP_ACCFILT_SETUP); +#define CAN_CTRL_SINGLETON_BASE CAN_CTRL_1_REG_BASE +#define CAN_SINGLETON_ISRVEC CYGNUM_HAL_INTERRUPT_CAN1_TX +#endif + +#ifdef CYGINT_DEVS_CAN_LPC2XXX_CAN1 +LPC2XXX_CAN_INFO(lpc2xxx_can1_info, CAN1_FLAG_STARTUP_ACCFILT_SETUP); +#define CAN_CTRL_SINGLETON_BASE CAN_CTRL_2_REG_BASE +#define CAN_SINGLETON_ISRVEC CYGNUM_HAL_INTERRUPT_CAN2_TX +#endif + +#ifdef CYGINT_DEVS_CAN_LPC2XXX_CAN2 +LPC2XXX_CAN_INFO(lpc2xxx_can2_info, CAN2_FLAG_STARTUP_ACCFILT_SETUP); +#define CAN_CTRL_SINGLETON_BASE CAN_CTRL_3_REG_BASE +#define CAN_SINGLETON_ISRVEC CYGNUM_HAL_INTERRUPT_CAN3_TX +#endif + +#ifdef CYGINT_DEVS_CAN_LPC2XXX_CAN3 +LPC2XXX_CAN_INFO(lpc2xxx_can3_info, CAN3_FLAG_STARTUP_ACCFILT_SETUP); +#define CAN_CTRL_SINGLETON_BASE CAN_CTRL_4_REG_BASE +#define CAN_SINGLETON_ISRVEC CYGNUM_HAL_INTERRUPT_CAN4_TX +#endif + +#endif // #if CYGINT_IO_CAN_CHANNELS > 1 + + +//=========================================================================== +// PROTOTYPES +//=========================================================================== + +//-------------------------------------------------------------------------- +// Device driver interface functions +// +static bool lpc2xxx_can_init(struct cyg_devtab_entry* devtab_entry); +static Cyg_ErrNo lpc2xxx_can_lookup(struct cyg_devtab_entry** tab, struct cyg_devtab_entry* sub_tab, const char* name); +static Cyg_ErrNo lpc2xxx_can_set_config(can_channel *chan, cyg_uint32 key, const void* buf, cyg_uint32* len); +static Cyg_ErrNo lpc2xxx_can_get_config(can_channel *chan, cyg_uint32 key, const void* buf, cyg_uint32* len); +static bool lpc2xxx_can_putmsg(can_channel *priv, CYG_CAN_MSG_T *pmsg, void *pdata); +static bool lpc2xxx_can_getevent(can_channel *priv, CYG_CAN_EVENT_T *pevent, void *pdata); +static void lpc2xxx_can_start_xmit(can_channel* chan); +static void lpc2xxx_can_stop_xmit(can_channel* chan); + + +//-------------------------------------------------------------------------- +// ISRs and DSRs +// +static cyg_uint32 lpc2xxx_can_tx_ISR(cyg_vector_t vector, cyg_addrword_t data); +static void lpc2xxx_can_tx_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data); +static cyg_uint32 lpc2xxx_can_rx_ISR(cyg_vector_t vector, cyg_addrword_t data); +static void lpc2xxx_can_rx_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data); +static cyg_uint32 lpc2xxx_can_err_ISR(cyg_vector_t vector, cyg_addrword_t data); +static void lpc2xxx_can_err_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data); + + +//-------------------------------------------------------------------------- +// Private utility functions +// +static bool lpc2xxx_can_config_channel(can_channel* chan, cyg_can_info_t* config, cyg_bool init); +static bool lpc2xxx_can_set_baud(can_channel *chan, cyg_can_baud_rate_t *baudrate); +static Cyg_ErrNo lpc2xxx_enter_lowpower_mode(can_channel *chan); +static void lpc2xxx_start_module(can_channel *chan); +static cyg_can_state lpc2xxx_get_state(lpc2xxx_can_info_t *info); +static void lpc2xxx_set_state(lpc2xxx_can_info_t *info, cyg_can_state state); + + +//-------------------------------------------------------------------------- +// Message box configuration +// +static void lpc2xxx_can_config_rx_all(can_channel *chan); +#ifdef CYGOPT_IO_CAN_RUNTIME_MBOX_CFG +static void lpc2xxx_can_config_rx_none(can_channel *chan); +static bool lpc2xxx_can_add_rx_filter(lpc2xxx_can_info_t *info, cyg_can_filter *filter); +#endif // CYGOPT_IO_CAN_RUNTIME_MBOX_CFG + + +#include "can_accfilt_lpc2xxx.c" + +//=========================================================================== +// GENERIC CAN IO DATA INITIALISATION +//=========================================================================== +CAN_LOWLEVEL_FUNS(lpc2xxx_can_lowlevel_funs, + lpc2xxx_can_putmsg, + lpc2xxx_can_getevent, + lpc2xxx_can_get_config, + lpc2xxx_can_set_config, + lpc2xxx_can_start_xmit, + lpc2xxx_can_stop_xmit + ); + + +//--------------------------------------------------------------------------- +// CAN channel 0 +// +#ifdef CYGPKG_DEVS_CAN_LPC2XXX_CAN0 +CYG_CAN_EVENT_T lpc2xxx_can0_rxbuf[CYGNUM_DEVS_CAN_LPC2XXX_CAN0_QUEUESIZE_RX]; // buffer for RX can events +CYG_CAN_MSG_T lpc2xxx_can0_txbuf[CYGNUM_DEVS_CAN_LPC2XXX_CAN0_QUEUESIZE_TX]; // buffer for TX can messages + + +CAN_CHANNEL_USING_INTERRUPTS(lpc2xxx_can0_chan, + lpc2xxx_can_lowlevel_funs, + lpc2xxx_can0_info, + CYG_CAN_BAUD_RATE(CYGNUM_DEVS_CAN_LPC2XXX_CAN0_KBAUD), + lpc2xxx_can0_txbuf, CYGNUM_DEVS_CAN_LPC2XXX_CAN0_QUEUESIZE_TX, + lpc2xxx_can0_rxbuf, CYGNUM_DEVS_CAN_LPC2XXX_CAN0_QUEUESIZE_RX + ); + + +DEVTAB_ENTRY(lpc2xxx_can0_devtab, + CYGPKG_DEVS_CAN_LPC2XXX_CAN0_NAME, + 0, // Does not depend on a lower level interface + &cyg_io_can_devio, + lpc2xxx_can_init, + lpc2xxx_can_lookup, // CAN driver may need initializing + &lpc2xxx_can0_chan + ); +#endif // CYGPKG_DEVS_CAN_LPC2XXX_CAN0 + + +//--------------------------------------------------------------------------- +// CAN channel 1 +// +#ifdef CYGPKG_DEVS_CAN_LPC2XXX_CAN1 +CYG_CAN_EVENT_T lpc2xxx_can1_rxbuf[CYGNUM_DEVS_CAN_LPC2XXX_CAN1_QUEUESIZE_RX]; // buffer for RX can events +CYG_CAN_MSG_T lpc2xxx_can1_txbuf[CYGNUM_DEVS_CAN_LPC2XXX_CAN1_QUEUESIZE_TX]; // buffer for TX can messages + + +CAN_CHANNEL_USING_INTERRUPTS(lpc2xxx_can1_chan, + lpc2xxx_can_lowlevel_funs, + lpc2xxx_can1_info, + CYG_CAN_BAUD_RATE(CYGNUM_DEVS_CAN_LPC2XXX_CAN1_KBAUD), + lpc2xxx_can1_txbuf, CYGNUM_DEVS_CAN_LPC2XXX_CAN1_QUEUESIZE_TX, + lpc2xxx_can1_rxbuf, CYGNUM_DEVS_CAN_LPC2XXX_CAN1_QUEUESIZE_RX + ); + + +DEVTAB_ENTRY(lpc2xxx_can1_devtab, + CYGPKG_DEVS_CAN_LPC2XXX_CAN1_NAME, + 0, // Does not depend on a lower level interface + &cyg_io_can_devio, + lpc2xxx_can_init, + lpc2xxx_can_lookup, // CAN driver may need initializing + &lpc2xxx_can1_chan + ); +#endif // CYGPKG_DEVS_CAN_LPC2XXX_CAN1 + + +//--------------------------------------------------------------------------- +// CAN channel 2 +// +#ifdef CYGPKG_DEVS_CAN_LPC2XXX_CAN2 +CYG_CAN_EVENT_T lpc2xxx_can2_rxbuf[CYGNUM_DEVS_CAN_LPC2XXX_CAN2_QUEUESIZE_RX]; // buffer for RX can events +CYG_CAN_MSG_T lpc2xxx_can2_txbuf[CYGNUM_DEVS_CAN_LPC2XXX_CAN2_QUEUESIZE_TX]; // buffer for TX can messages + + +CAN_CHANNEL_USING_INTERRUPTS(lpc2xxx_can2_chan, + lpc2xxx_can_lowlevel_funs, + lpc2xxx_can2_info, + CYG_CAN_BAUD_RATE(CYGNUM_DEVS_CAN_LPC2XXX_CAN2_KBAUD), + lpc2xxx_can2_txbuf, CYGNUM_DEVS_CAN_LPC2XXX_CAN2_QUEUESIZE_TX, + lpc2xxx_can2_rxbuf, CYGNUM_DEVS_CAN_LPC2XXX_CAN2_QUEUESIZE_RX + ); + + +DEVTAB_ENTRY(lpc2xxx_can2_devtab, + CYGPKG_DEVS_CAN_LPC2XXX_CAN2_NAME, + 0, // Does not depend on a lower level interface + &cyg_io_can_devio, + lpc2xxx_can_init, + lpc2xxx_can_lookup, // CAN driver may need initializing + &lpc2xxx_can2_chan + ); +#endif // CYGPKG_DEVS_CAN_LPC2XXX_CAN2 + + +//--------------------------------------------------------------------------- +// CAN channel 3 +// +#ifdef CYGPKG_DEVS_CAN_LPC2XXX_CAN3 +CYG_CAN_EVENT_T lpc2xxx_can3_rxbuf[CYGNUM_DEVS_CAN_LPC2XXX_CAN3_QUEUESIZE_RX]; // buffer for RX can events +CYG_CAN_MSG_T lpc2xxx_can3_txbuf[CYGNUM_DEVS_CAN_LPC2XXX_CAN3_QUEUESIZE_TX]; // buffer for TX can messages + + +CAN_CHANNEL_USING_INTERRUPTS(lpc2xxx_can3_chan, + lpc2xxx_can_lowlevel_funs, + lpc2xxx_can3_info, + CYG_CAN_BAUD_RATE(CYGNUM_DEVS_CAN_LPC2XXX_CAN3_KBAUD), + lpc2xxx_can3_txbuf, CYGNUM_DEVS_CAN_LPC2XXX_CAN3_QUEUESIZE_TX, + lpc2xxx_can3_rxbuf, CYGNUM_DEVS_CAN_LPC2XXX_CAN3_QUEUESIZE_RX + ); + + +DEVTAB_ENTRY(lpc2xxx_can3_devtab, + CYGPKG_DEVS_CAN_LPC2XXX_CAN3_NAME, + 0, // Does not depend on a lower level interface + &cyg_io_can_devio, + lpc2xxx_can_init, + lpc2xxx_can_lookup, // CAN driver may need initializing + &lpc2xxx_can3_chan + ); +#endif // CYGPKG_DEVS_CAN_LPC2XXX_CAN3 + + +//=========================================================================== +// IMPLEMENTATION +//=========================================================================== + + + +//=========================================================================== +/// First initialisation and reset of CAN modul. +//=========================================================================== +static bool lpc2xxx_can_init(struct cyg_devtab_entry* devtab_entry) +{ + can_channel *chan = (can_channel*)devtab_entry->priv; + lpc2xxx_can_info_t *info = (lpc2xxx_can_info_t *)chan->dev_priv; + bool res; + +#ifdef CYGDBG_IO_INIT + diag_printf("LPC2XXX CAN init\n"); +#endif + + // + // Create TX interrupt + // + cyg_drv_interrupt_create(CAN_ISRVEC(info), + 0, // Priority does not matter LPC2xxx + (cyg_addrword_t)chan, // Data item passed to interrupt handler + lpc2xxx_can_tx_ISR, + lpc2xxx_can_tx_DSR, + &info->tx_interrupt_handle, + &info->tx_interrupt); + cyg_drv_interrupt_attach(info->tx_interrupt_handle); + cyg_drv_interrupt_unmask(CAN_ISRVEC(info)); + + // + // Create RX interrupt + // + cyg_drv_interrupt_create(CAN_ISRVEC(info) + 6, + 0, // Priority does not matter for LPC2xxx + (cyg_addrword_t)chan, // Data item passed to interrupt handler + lpc2xxx_can_rx_ISR, + lpc2xxx_can_rx_DSR, + &info->rx_interrupt_handle, + &info->rx_interrupt); + cyg_drv_interrupt_attach(info->rx_interrupt_handle); + cyg_drv_interrupt_unmask(CAN_ISRVEC(info) + 6); + + // + // Now create and enable error interrupt. This interrupt is global for all channels + // and so we need to call it only one times - when the first channel is + // initialized + // + if (!lpc2xxx_global_can_info.init_cnt) + { + // + // Create err interrupt + // + cyg_drv_interrupt_create(CYGNUM_HAL_INTERRUPT_CAN, + 0, // Priority does not matter for LPX2xxx + 0, // Data item passed to interrupt handler + lpc2xxx_can_err_ISR, + lpc2xxx_can_err_DSR, + &lpc2xxx_global_can_info.err_interrupt_handle, + &lpc2xxx_global_can_info.err_interrupt); + cyg_drv_interrupt_attach(lpc2xxx_global_can_info.err_interrupt_handle); + cyg_drv_interrupt_unmask(CYGNUM_HAL_INTERRUPT_CAN); + } + + res = lpc2xxx_can_config_channel(chan, &chan->config, true); + lpc2xxx_global_can_info.active_channels[lpc2xxx_global_can_info.init_cnt++] = chan; + return res; +} + + +//=========================================================================== +// Configure can channel +//=========================================================================== +static bool lpc2xxx_can_config_channel(can_channel* chan, cyg_can_info_t* config, cyg_bool init) +{ + CAN_DECLARE_INFO(chan); + cyg_uint32 temp32; + bool res = true; + + if (init) + { + // + // In case platform needs extra initialization (i.e. setup of CAN transcievers) + // it should implement this macro + // +#ifdef CYGPRI_IO_CAN_LPC2XXX_PLF_INIT_HOOK + CYGPRI_IO_CAN_LPC2XXX_PLF_INIT_HOOK(chan, config); +#endif + + HAL_WRITE_UINT32(CAN_ACCFILT_AFMR, AFMR_OFF); // Acceptance Filter Mode Register = off + HAL_WRITE_UINT32(CAN_CTRL_MOD(info), CANMOD_RESET); // Go into reset mode + HAL_WRITE_UINT32(CAN_CTRL_IER(info), 0); // disable all interrupts + HAL_WRITE_UINT32(CAN_CTRL_GSR(info), 0); // Clear Status register - clears error counters + + // + // If this is the first channel to initialize then we reset the CAN registers + // and setup the CAN I/O pins + // + if (!lpc2xxx_global_can_info.init_cnt) + { + lpc2xxx_can_accfilt_reset(); +#if defined(CYGPKG_DEVS_CAN_LPC2XXX_CAN0) || defined(CYGPKG_DEVS_CAN_LPC2XXX_CAN1) || defined(CYGPKG_DEVS_CAN_LPC2XXX_CAN3) + HAL_READ_UINT32(CYGARC_HAL_LPC2XXX_REG_PIN_BASE + CYGARC_HAL_LPC2XXX_REG_PINSEL1, temp32); +#ifdef CYGPKG_DEVS_CAN_LPC2XXX_CAN0 + temp32 |= 0x00040000L; // Set bit 18 +#endif // CYGPKG_DEVS_CAN_LPC2XXX_CAN0 +#ifdef CYGPKG_DEVS_CAN_LPC2XXX_CAN1 + temp32 |= 0x00014000L; // Set bits 14 and 16 +#endif // CYGPKG_DEVS_CAN_LPC2XXX_CAN1 +#ifdef CYGPKG_DEVS_CAN_LPC2XXX_CAN2 + temp32 |= 0x00001800L; // Set bits 11 and 12 +#endif + HAL_WRITE_UINT32(CYGARC_HAL_LPC2XXX_REG_PIN_BASE + CYGARC_HAL_LPC2XXX_REG_PINSEL1, temp32); +#endif // defined(CYGPKG_DEVS_CAN_LPC2XXX_CAN0) || defined(CYGPKG_DEVS_CAN_LPC2XXX_CAN1) + + +#ifdef CYGPKG_DEVS_CAN_LPC2XXX_CAN3 + HAL_READ_UINT32(CYGARC_HAL_LPC2XXX_REG_PIN_BASE + CYGARC_HAL_LPC2XXX_REG_PINSEL0, temp32); + temp32 |= 0x0F000000L; + HAL_WRITE_UINT32(CYGARC_HAL_LPC2XXX_REG_PIN_BASE + CYGARC_HAL_LPC2XXX_REG_PINSEL0, temp32); +#endif // CYGPKG_DEVS_CAN_LPC2XXX_CAN3 + } + } // if (init) + + res = lpc2xxx_can_set_baud(chan, &config->baud); // set baudrate + // $$$$ enable receive interrupt? + HAL_WRITE_UINT32(CAN_CTRL_MOD(info), CANMOD_OPERATIONAL); // enter normal operating mode + + // + // store new config values + // + if (config != &chan->config) + { + chan->config = *config; + } + + return res; +} + + +//=========================================================================== +// Set baudrate of certain can channel +//=========================================================================== +static bool lpc2xxx_can_set_baud(can_channel *chan, cyg_can_baud_rate_t *baudrate) +{ + bool res = true; + cyg_uint32 canbtr; + cyg_uint32 canmod; + CAN_DECLARE_INFO(chan); + + // + // Get bit timings from HAL because bit timings depend on sysclock + // If the macro fills the canbtr value with 0 then the baudrate + // is not supported and the function returns false + // + HAL_LPC2XXX_GET_CAN_BR(*baudrate, canbtr); + if (0 == canbtr) + { + return false; + } + + // + // Any modificatons to the baudrate register must be done while CAN + // module is in reset mode. So we first set the CAN moduel in reset + // mode, then we set baudrate and then we restore content of CANMOD + // register + // + HAL_READ_UINT32(CAN_CTRL_MOD(info), canmod); // backup canmod register + HAL_WRITE_UINT32(CAN_CTRL_MOD(info), CANMOD_RESET); // Go into reset mode + HAL_WRITE_UINT32(CAN_CTRL_BTR(info), canbtr); // write baudrate value + HAL_WRITE_UINT32(CAN_CTRL_MOD(info), canmod); // restore previous value + + return res; +} + + +//=========================================================================== +// Lookup the device and return its handle +//=========================================================================== +static Cyg_ErrNo lpc2xxx_can_lookup(struct cyg_devtab_entry** tab, struct cyg_devtab_entry* sub_tab, const char* name) +{ + can_channel* chan = (can_channel*) (*tab)->priv; + CAN_DECLARE_INFO(chan); + cyg_uint32 regval; + + chan->callbacks->can_init(chan); + + // + // If runtime acceptance filter configuration is supported then we only + // configure RX ALL if the user selected the RX ALL setup in config utility + // +#ifdef CYGOPT_IO_CAN_RUNTIME_MBOX_CFG + if (info->flags & INFO_FLAG_STARTUP_RX_ALL) +#endif // CYGOPT_IO_CAN_RUNTIME_MBOX_CFG + { + lpc2xxx_can_config_rx_all(chan); + } + + HAL_WRITE_UINT32(CAN_ACCFILT_AFMR, AFMR_ON); // Activate acceptance filter + HAL_READ_UINT32(CAN_CTRL_IER(info), regval); + regval = regval | IER_RX | CAN_MISC_INT; // enable all interrupts + HAL_WRITE_UINT32(CAN_CTRL_IER(info), regval); + + return ENOERR; +} + + +#ifdef CYGOPT_IO_CAN_RUNTIME_MBOX_CFG +//=========================================================================== +// Setup AT91SAM7 CAN module in a state where all message boxes are disabled +// After this call it is possible to add single message buffers and filters +//=========================================================================== +static void lpc2xxx_can_config_rx_none(can_channel *chan) +{ + lpc2xxx_can_info_t *info = (lpc2xxx_can_info_t *)chan->dev_priv; + + // + // Remove all acceptance filters + // $$$$ maybe we should also abort any pending transfers and + // disable receive interrupts ? + // + lpc2xxx_can_accfilt_remove_all_ctrl_entries(info); + info->flags &= ~INFO_FLAG_RX_ALL; +#ifdef CYGDBG_DEVS_CAN_LPC2XXX_DEBUG + lpc2xxx_can_accfilt_dbg_dump(); +#endif +} + + +//=========================================================================== +// Add one single message filter to acceptance filter +//=========================================================================== +static bool lpc2xxx_can_add_rx_filter(lpc2xxx_can_info_t *info, cyg_can_filter *filter) +{ + bool res; + + res = lpc2xxx_can_accfilt_add(info, filter->msg.id, 0, filter->msg.ext); + if (!res) + { + filter->handle = CYGNUM_CAN_MSGBUF_NA; + } +#ifdef CYGDBG_DEVS_CAN_LPC2XXX_DEBUG + lpc2xxx_can_accfilt_dbg_dump(); +#endif + return res; +} +#endif // CYGOPT_IO_CAN_RUNTIME_MBOX_CFG + + + +#ifdef CYGOPT_IO_CAN_RUNTIME_MBOX_CFG +//=========================================================================== +// Configure message buffers +//=========================================================================== +static Cyg_ErrNo lpc2xxx_can_config_msgbuf(can_channel *chan, const void* buf, cyg_uint32* len) +{ + Cyg_ErrNo res = ENOERR; + lpc2xxx_can_info_t *info = (lpc2xxx_can_info_t *)chan->dev_priv; + cyg_can_msgbuf_cfg *msg_buf = (cyg_can_msgbuf_cfg *)buf; + + if (*len != sizeof(cyg_can_msgbuf_cfg)) + { + return -EINVAL; + } + + switch (msg_buf->cfg_id) + { + // + // clear all message filters and remote buffers - prepare for message buffer + // configuration + // + case CYGNUM_CAN_MSGBUF_RESET_ALL : + { + lpc2xxx_can_config_rx_none(chan); + } + break; + + // + // setup driver for reception of all standard and extended messages + // + case CYGNUM_CAN_MSGBUF_RX_FILTER_ALL : + { + if (!(info->flags & INFO_FLAG_RX_ALL)) // if rx_all is enabled we do not need to do anything + { + lpc2xxx_can_config_rx_all(chan); // setup RX all state + } + } + break; + + // + // add single message filter, message with filter ID will be received + // + case CYGNUM_CAN_MSGBUF_RX_FILTER_ADD : + { + cyg_can_filter *filter = (cyg_can_filter*) buf; + + // + // if the acceptance filter is configured to receive all messages then + // it is not allowed to add single message filters because then more + // than one acceptance filter would receive the same CAN id + // + if (info->flags & INFO_FLAG_RX_ALL) + { + return -EPERM; + } + + // + // try to allocate a free acceptance filter entry - if we have a free one + // then we can prepare the acceptance filter table for reception of + // this message + // + if (!lpc2xxx_can_add_rx_filter(info, filter)) + { + return -EPERM; + } + } + break; //CYGNUM_CAN_MSGBUF_RX_FILTER_ADD + + +#ifdef CYGOPT_IO_CAN_REMOTE_BUF + // + // Try to add a new RTR response message buffer for automatic transmisson + // of data frame on reception of a remote frame + // + case CYGNUM_CAN_MSGBUF_REMOTE_BUF_ADD : + { + // $$$$ TODO implement remote repsonse buffers in software + return -ENOSUPP; + } + break; + + // + // write data into remote response buffer + // + case CYGNUM_CAN_MSGBUF_REMOTE_BUF_WRITE : + { + // $$$$ TODO implement remote response buffers in software + return -ENOSUPP; + } + break; +#endif // #ifdef CYGOPT_IO_CAN_REMOTE_BUF + default: + return -EINVAL; + } // switch (buf->cfg_id) + + return res; +} +#endif // CYGOPT_IO_CAN_RUNTIME_MBOX_CFG + + +//=========================================================================== +// Read state of CAN controller +// The CAN state variable for each channel is modiefied by DSR so if we +// read the state we need to lock DSRs to protect the data access +//=========================================================================== +static cyg_can_state lpc2xxx_get_state(lpc2xxx_can_info_t *info) +{ + cyg_can_state result; + + cyg_drv_dsr_lock(); + result = info->state; + cyg_drv_dsr_unlock(); + + return result; +} + + +//=========================================================================== +// Set state of CAN controller +// The CAN state variable for each channel is modiefied by DSR so if we +// write the state we need to lock DSRs to protect the data access +//=========================================================================== +static void lpc2xxx_set_state(lpc2xxx_can_info_t *info, cyg_can_state state) +{ + cyg_drv_dsr_lock(); + info->state = state; + cyg_drv_dsr_unlock(); +} + + +//=========================================================================== +// Enter low power mode +//=========================================================================== +static Cyg_ErrNo lpc2xxx_enter_lowpower_mode(can_channel *chan) +{ + cyg_uint32 regval; + lpc2xxx_can_info_t *info = (lpc2xxx_can_info_t *)chan->dev_priv; + + // + // Before we enter low power mode, we have to enable wake up interrupt + // Normally this interrupt is always enabled so we do not need to do + // anything here + // + HAL_READ_UINT32(CAN_CTRL_MOD(info), regval); + + // + // Software can only set SM when RM in the CAN Mode register is 0 + // + if (regval & CANMOD_RESET) + { + return -EPERM; + } + + //regval &= CANMOD_SLEEP; + lpc2xxx_set_state(info, CYGNUM_CAN_STATE_STANDBY); + HAL_WRITE_UINT32(CAN_CTRL_MOD(info), CANMOD_SLEEP); + return ENOERR; +} + + +//=========================================================================== +// Start CAN module - set CANMOD operational and enable all interrupts +//=========================================================================== +static void lpc2xxx_start_module(can_channel *chan) +{ + cyg_uint32 regval; + lpc2xxx_can_info_t *info = (lpc2xxx_can_info_t *)chan->dev_priv; + + HAL_WRITE_UINT32(CAN_CTRL_MOD(info), CANMOD_OPERATIONAL); + // + // The interrupt enable register is also modified by ISR and DSR so + // we need to protect acces here + // + cyg_drv_isr_lock(); + HAL_READ_UINT32(CAN_CTRL_IER(info), regval); + regval = regval | IER_RX | CAN_MISC_INT; // enable all interrupts + HAL_WRITE_UINT32(CAN_CTRL_IER(info), regval); + info->state = CYGNUM_CAN_STATE_ACTIVE; + cyg_drv_isr_unlock(); +} + + +//=========================================================================== +// Enter reset mode +//=========================================================================== +static void lpc2xx_enter_reset_mode(can_channel *chan) +{ + lpc2xxx_can_info_t *info = (lpc2xxx_can_info_t *)chan->dev_priv; + + info->state = CYGNUM_CAN_STATE_STOPPED; + HAL_WRITE_UINT32(CAN_CTRL_MOD(info), CANMOD_RESET); +} + + +#ifdef CYGOPT_DEVS_CAN_LPC2XXX_EXTENDED_CFG_KEYS +//=========================================================================== +// Add message filter group +//=========================================================================== +static Cyg_ErrNo lpc2xxx_can_config_accfilt_group(can_channel *chan, const void* buf, cyg_uint32* len) +{ + bool res; + cyg_can_filtergroup_cfg *filter_grp = (cyg_can_filtergroup_cfg *)buf; + lpc2xxx_can_info_t *info = (lpc2xxx_can_info_t *)chan->dev_priv; + + + if (*len != sizeof(cyg_can_filtergroup_cfg)) + { + return -EINVAL; + } + + if (filter_grp->lower_id_bound >= filter_grp->upper_id_bound) + { + return -EINVAL; + } + + // + // if the acceptance filter is configured to receive all messages then + // it is not allowed to add single message filter groups because then more + // than one acceptance filter would receive the same CAN id + // + if (info->flags & INFO_FLAG_RX_ALL) + { + return -EPERM; + } + + res = lpc2xxx_can_accfilt_add(info, + filter_grp->lower_id_bound, + filter_grp->upper_id_bound, + filter_grp->ext); + + + return res ? ENOERR : -EPERM; +} +#endif // CYGOPT_DEVS_CAN_LPC2XXX_EXTENDED_CFG_KEYS + + +//=========================================================================== +// Change device configuration +//=========================================================================== +static Cyg_ErrNo lpc2xxx_can_set_config(can_channel *chan, cyg_uint32 key, const void* buf, cyg_uint32* len) +{ + Cyg_ErrNo res = ENOERR; + + switch (key) + { + // + // Setup a new CAN configuration. This will i.e. setup a new baud rate + // + case CYG_IO_SET_CONFIG_CAN_INFO: + { + cyg_can_info_t* config = (cyg_can_info_t*) buf; + if (*len < sizeof(cyg_can_info_t)) + { + return -EINVAL; + } + *len = sizeof(cyg_can_info_t); + if (!lpc2xxx_can_config_channel(chan, config, false)) + { + return -EINVAL; + } + } + break; + +#ifdef CYGOPT_IO_CAN_RUNTIME_MBOX_CFG + // + // configure message buffers + // + case CYG_IO_SET_CONFIG_CAN_MSGBUF : + { + res = lpc2xxx_can_config_msgbuf(chan, buf, len); + } + break; +#endif // CYGOPT_IO_CAN_RUNTIME_MBOX_CFG + +#ifdef CYGOPT_DEVS_CAN_LPC2XXX_EXTENDED_CFG_KEYS + // + // Add message filter group to acceptance filter + // + case CYG_IO_SET_CONFIG_LPC2XXX_ACCFILT_GROUP : + { + return lpc2xxx_can_config_accfilt_group(chan, buf, len); + } + break; +#endif // CYGOPT_DEVS_CAN_LPC2XXX_EXTENDED_CFG_KEYS + + // + // Change CAN state of CAN module + // + case CYG_IO_SET_CONFIG_CAN_MODE : + { + cyg_can_mode *can_mode = (cyg_can_mode*) buf; + + if (*len != sizeof(cyg_can_mode)) + { + return -EINVAL; + } + *len = sizeof(cyg_can_mode); + + // + // decide what to do acording to mode + // + switch (*can_mode) + { + // + // The controller does not support a stopped and standby state so we + // simply enter the low power state here. This state is also safe for + // message buffer configuration + // + case CYGNUM_CAN_MODE_STOP : lpc2xx_enter_reset_mode(chan); break; + case CYGNUM_CAN_MODE_START : lpc2xxx_start_module(chan); break; + case CYGNUM_CAN_MODE_STANDBY : lpc2xxx_enter_lowpower_mode(chan); break; + case CYGNUM_CAN_MODE_CONFIG : lpc2xx_enter_reset_mode(chan); break; + } + } + break; // case CYG_IO_SET_CONFIG_CAN_MODE : + // + // Unknown config key - indicate this by returning -EINVAL + // + default: + return -EINVAL; + } // switch (key) + + return res; +} + + +//=========================================================================== +// Query device configuration +//=========================================================================== +static Cyg_ErrNo lpc2xxx_can_get_config(can_channel *chan, cyg_uint32 key, const void* buf, cyg_uint32* len) +{ + Cyg_ErrNo res = ENOERR; + lpc2xxx_can_info_t *info = (lpc2xxx_can_info_t *)chan->dev_priv; + + switch(key) + { + // + // query state of CAN controller + // + case CYG_IO_GET_CONFIG_CAN_STATE : + { + cyg_can_state *can_state = (cyg_can_state*) buf; + + if (*len != sizeof(cyg_can_state)) + { + return -EINVAL; + } + *len = sizeof(cyg_can_state); + *can_state = lpc2xxx_get_state(info); + } + break; + +#ifdef CYGOPT_IO_CAN_RUNTIME_MBOX_CFG + // + // Query message box information - returns available and free message + // boxes + // + case CYG_IO_GET_CONFIG_CAN_MSGBUF_INFO : + { + cyg_can_msgbuf_info *mbox_info = (cyg_can_msgbuf_info*) buf; + + if (*len != sizeof(cyg_can_msgbuf_info)) + { + return -EINVAL; + } + cyg_uint32 end_of_table; + *len = sizeof(cyg_can_msgbuf_info); + + HAL_READ_UINT32(CAN_ACCFILT_ENDOFTABLE, end_of_table); + mbox_info->count = LPC2XXX_CAN_MSG_FILTERS_MAX; + mbox_info->free = (ACCFILT_RAM_SIZE - end_of_table) / ACCFILT_COMMON_ENTRY_SIZE; + } + break; +#endif // CYGOPT_IO_CAN_RUNTIME_MBOX_CFG + + + // + // Query hardware description of FlexCAN device driver + // + case CYG_IO_GET_CONFIG_CAN_HDI : + { + cyg_can_hdi *hdi = (cyg_can_hdi *)buf; + // + // comes from high level driver so we do not need to + // check buffer size here + // + hdi->support_flags = CYGNUM_CAN_HDI_FRAMETYPE_EXT_ACTIVE + | CYGNUM_CAN_HDI_FULLCAN; + } + break; + + default : + res = -EINVAL; + }// switch(key) + + return res; +} + + +//=========================================================================== +// Send single message +//=========================================================================== +static bool lpc2xxx_can_putmsg(can_channel *chan, CYG_CAN_MSG_T *pmsg, void *pdata) +{ + cyg_uint32 regval; + CAN_DECLARE_INFO(priv); + + // + // We use only one single transmit buffer of the three available buffers + // We use buffer 1 (buffer 2 and 3 are unused) + // + // The errata sheet tells the following about the transmit buffers: + // Problem: The Triple Transmit Buffer function cannot be used. + // Work-around: Use any one Transmit buffer only (Use either Transmit Buffer 1, + // Transmit Buffer 2 or Transmit Buffer 3 exclusively). The buffer you decided + // to use should be loaded only when there is no pending transmission. + // + HAL_READ_UINT32(CAN_CTRL_SR(info), regval); + if (!(regval & SR_TX_BUF_WRITE_OK)) + { + return false; + } + + regval = pmsg->dlc << 16; + if (pmsg->rtr) + { + regval |= TFI_DLC_RTR; + } + +#ifdef CYGOPT_IO_CAN_EXT_CAN_ID + if (pmsg->ext) + { + regval |= TFI_DLC_EXT; + } +#endif // #define CYGOPT_IO_CAN_EXT_CAN_ID + HAL_WRITE_UINT32(CAN_CTRL_TFI1(info), regval); // write DLC + HAL_WRITE_UINT32(CAN_CTRL_TID1(info), pmsg->id); // write ID + HAL_WRITE_UINT32(CAN_CTRL_TDA1(info), pmsg->data.dwords[0]); // write first 4 data bytes + HAL_WRITE_UINT32(CAN_CTRL_TDB1(info), pmsg->data.dwords[1]); // write second 4 data bytes + + // + // Request transmission of message + // The errata sheet tells the following about tx request: + // Introduction: The CAN module can lose arbitration to another CAN node during an + // attempt to transmit a CAN message. The message of the CAN node the arbitration was + // lost to is supposed to be received correctly by the CAN module. + // Problem: Messages might not be received correctly if during a CAN Transmission the + // CAN bus arbitration is lost to another CAN node. + // Work-around: Use the Self Reception Request command instead of the Transmission + // Request command. However, it has to be taken into account that now all transmitted + // messages may be received if not prevented by appropriate Acceptance Filter settings. + // (Don't set up Acceptance Filter Message Identifiers for the messages you are + // transmitting yourself.) + // + HAL_WRITE_UINT32(CAN_CTRL_CMR(info), CMR_TX_REQ | CMR_SEND_TX_BUF1); // Write transmission request + //HAL_WRITE_UINT32(CAN_CTRL_CMR(info), CMR_SELF_RX_REQ | CMR_SEND_TX_BUF1); // Write self transmission request + + return true; +} + + +//=========================================================================== +// Read event from device driver +//=========================================================================== +static bool lpc2xxx_can_getevent(can_channel *chan, CYG_CAN_EVENT_T *pevent, void *pdata) +{ + lpc2xxx_can_info_t *info = (lpc2xxx_can_info_t *)chan->dev_priv; + bool res = true; + cyg_uint32 regval; + cyg_uint32 event = *((cyg_uint32*)pdata); + lsc_buf_t data; + + // + // Handle RX event + // + if (event & ICR_RX) + { + cyg_uint32 id; + + pevent->flags |= CYGNUM_CAN_EVENT_RX; + HAL_READ_UINT32(CAN_CTRL_RFS(info), regval); + HAL_READ_UINT32(CAN_CTRL_RID(info), id); + +#ifdef CYGOPT_IO_CAN_EXT_CAN_ID + if (regval & RFS_EXT) + { + pevent->msg.ext = CYGNUM_CAN_ID_EXT; + pevent->msg.id = id & 0x1FFFFFFF; + } + else +#endif // #define CYGOPT_IO_CAN_EXT_CAN_ID + { +#ifdef CYGOPT_IO_CAN_STD_CAN_ID + pevent->msg.ext = CYGNUM_CAN_ID_STD; + pevent->msg.id = id & 0x7FF; +#endif // #ifdef CYGOPT_IO_CAN_STD_CAN_ID + } // if (regval & RFS_EXT) + + if (regval & RFS_RTR) + { + pevent->msg.rtr = CYGNUM_CAN_FRAME_RTR; + } + else + { + pevent->msg.rtr = CYGNUM_CAN_FRAME_DATA; + HAL_READ_UINT32(CAN_CTRL_RDA(info), pevent->msg.data.dwords[0]); + HAL_READ_UINT32(CAN_CTRL_RDB(info), pevent->msg.data.dwords[1]); + } //if (regval & RFS_RTR) + pevent->msg.dlc = RFS_GET_DLC(regval); + + // + // Now check if an data overrun occured - a message was lost because the + // preceeding message to this CAN controller was not read and released + // quickly enough. After reading the status we clear the overrun bit + // + HAL_READ_UINT32(CAN_CTRL_GSR(info), regval); + if (regval & GSR_DATA_OVR) + { + pevent->flags |= CYGNUM_CAN_EVENT_OVERRUN_RX; + HAL_WRITE_UINT32(CAN_CTRL_CMR(info), CMR_CLEAR_DATA_OVR); + } + } + + // + // Handle TX events + // + if (event & ICR_TX1) + { + } + + // + // Handle all other events + // + if (event & CAN_MISC_INT) + { + HAL_READ_UINT32(CAN_CTRL_GSR(info), data.dword); + + // + // 1: Error Warning Interrupt -- this bit is set on every change (set or clear) of the Error + // Status or Bus Status bit in CANSR, if the EIE bit in CAN is 1 at the time of the + // change. + // + if (event & ICR_ERR_WARN) + { + // + // If one of the warning counters is above 96 then the controller is in bus warning + // state. If both counters are below 96 the this interrupt indicates that the + // controller has left the bus warning state and is error active again + // + if (data.bytes[2] >= 96) + { + pevent->flags |= CYGNUM_CAN_EVENT_WARNING_RX; + info->state = CYGNUM_CAN_STATE_BUS_WARN; + } + else if (data.bytes[3] >= 96) + { + pevent->flags |= CYGNUM_CAN_EVENT_WARNING_TX; + info->state = CYGNUM_CAN_STATE_BUS_WARN; + } + else + { + info->state = CYGNUM_CAN_STATE_ACTIVE; + } + LPC2XXX_DBG_PRINT("ICR_ERR_WARN\n"); + } + + // + // 1: Wake-Up Interrupt: this bit is set if the CAN controller is sleeping and bus activity + // is detected, if the WUIE bit in CANIE is 1. + // + if (event & ICR_WAKE_UP) + { + pevent->flags |= CYGNUM_CAN_EVENT_LEAVING_STANDBY; + info->state = CYGNUM_CAN_STATE_ACTIVE; + LPC2XXX_DBG_PRINT("ICR_WAKE_UP\n"); + } + + // + // Error Passive Interrupt -- this bit is set if the EPIE bit in CANIE is 1, and the CAN + // controller switches between Error Passive and Error Active mode in either + // direction. We have to check if the ERR bit is set in global status register. + // If it is set, then it is a switch to error passive else it is a switch to + // error active state + // + if (event & ICR_ERR_PASSIVE) + { + if (data.dword & GSR_ERR) + { + pevent->flags |= CYGNUM_CAN_EVENT_ERR_PASSIVE; + info->state = CYGNUM_CAN_STATE_ERR_PASSIVE; + } + else + { + info->state = CYGNUM_CAN_STATE_ACTIVE; + } + LPC2XXX_DBG_PRINT("ICR_ERR_PASSIVE\n"); + } + + // + // Arbitration Lost Interrupt -- this bit is set if the ALIE bit in CANIE is 1, and the + // CAN controller loses arbitration while attempting to transmit. + // + if (event & ICR_ARBITR_LOST) + { + pevent->flags |= CYGNUM_CAN_EVENT_ARBITRATION_LOST; + LPC2XXX_DBG_PRINT("ICR_ARBITR_LOST\n"); + } + + // + // 1: Bus Error Interrupt -- this bit is set if the BEIE bit in CANIE is 1, and the CAN + // controller detects an error on the bus. + // + if (event & ICR_BUS_ERR) + { + pevent->flags |= CYGNUM_CAN_EVENT_BUS_OFF; + LPC2XXX_DBG_PRINT("ICR_BUS_ERR\n"); + } + } // if (event & CAN_MISC_INT) + + return res; +} + + +//=========================================================================== +// Kick transmitter +//=========================================================================== +static void lpc2xxx_can_start_xmit(can_channel* chan) +{ + cyg_uint32 regval; + CAN_DECLARE_INFO(chan); + + LPC2XXX_DBG_PRINT("start_xmit\n"); + + cyg_drv_dsr_lock(); + HAL_READ_UINT32(CAN_CTRL_IER(info), regval); + regval |= IER_TX1; // enable tx interrupt for tx buf 1 + HAL_WRITE_UINT32(CAN_CTRL_IER(info), regval); + cyg_drv_dsr_unlock(); + + // + // kick transmitter + // + chan->callbacks->xmt_msg(chan, 0); // Kick transmitter (if necessary) +} + + +//=========================================================================== +// Stop transmitter +//=========================================================================== +static void lpc2xxx_can_stop_xmit(can_channel* chan) +{ + cyg_uint32 regval; + CAN_DECLARE_INFO(chan); + + LPC2XXX_DBG_PRINT("stop_xmit\n"); + + cyg_drv_dsr_lock(); + HAL_READ_UINT32(CAN_CTRL_IER(info), regval); + regval &= ~IER_TX1; // disable tx interrupt for tx buf 1 + HAL_WRITE_UINT32(CAN_CTRL_IER(info), regval); + cyg_drv_dsr_unlock(); +} + + +//=========================================================================== +// Low level transmit interrupt handler +//=========================================================================== +static cyg_uint32 lpc2xxx_can_tx_ISR(cyg_vector_t vector, cyg_addrword_t data) +{ + can_channel *chan = (can_channel *)data; + cyg_uint32 regval; + lpc2xxx_can_info_t *info = (lpc2xxx_can_info_t *)chan->dev_priv; + + // + // The ISR only disables and acknowledges the TX interrupt + // any further processing is done by DSR + // + HAL_READ_UINT32(CAN_CTRL_IER(info), regval); + regval &= ~IER_TX1; // disable tx interrupt for tx buf 1 + HAL_WRITE_UINT32(CAN_CTRL_IER(info), regval); + + // + // Now read iput capture register - this clears all interrupt bits in this + // register and also acknowledges the interrupt. We then store the ICR value into + // the icr buffer in the channel info data + // + HAL_READ_UINT32(CAN_CTRL_ICR(info), regval); + info->icr |= (regval &~ ICR_RX); // do not handle RX events - this is done by RX ISR + cyg_drv_interrupt_acknowledge(vector); + LPC2XXX_DBG_PRINT("tx_ISR\n"); + return CYG_ISR_CALL_DSR; +} + + +//=========================================================================== +// High level transmit interrupt handler +//=========================================================================== +static void lpc2xxx_can_tx_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data) +{ + can_channel *chan = (can_channel *)data; + cyg_uint32 regval; + lpc2xxx_can_info_t *info = (lpc2xxx_can_info_t *)chan->dev_priv; + + // + // now receive the event - the events are stored in ICR field of CAN info + // we need to disable ISRs here because the ICR field will be accessed from + // several ISRs (TX ISR and global error ISR) + // + cyg_drv_isr_lock(); // disable ISR s + regval = info->icr; // read the event field + info->icr = 0; // clear the event fields - all events processed in getevent() + cyg_drv_isr_unlock(); // renable ISRs + +#ifdef CYGOPT_IO_CAN_TX_EVENT_SUPPORT + if (regval & (ICR_TX1 & CAN_MISC_INT)) +#else + if (regval & CAN_MISC_INT) +#endif + { + // + // Now receive the events - pass the event field to the getevent function + // + chan->callbacks->rcv_event(chan, ®val); + } + + // + // Now transmit next message and reenable interrupts + // + chan->callbacks->xmt_msg(chan, 0); // send next message + HAL_READ_UINT32(CAN_CTRL_IER(info), regval); + regval |= IER_TX1; // reenable interrupts for the message box that caused the DSR to run + HAL_WRITE_UINT32(CAN_CTRL_IER(info), regval); + + LPC2XXX_DBG_PRINT("tx_DSR\n"); +} + + +//=========================================================================== +// Low level receive interrupt handler +//=========================================================================== +static cyg_uint32 lpc2xxx_can_rx_ISR(cyg_vector_t vector, cyg_addrword_t data) +{ + cyg_uint32 regval; + CAN_DECLARE_CHAN(data); // declares chan pointer if required + CAN_DECLARE_INFO(chan); // declares info pointer if reuired + + // + // The ISR only disables and acknowledges the RX interrupt + // any further processing is done by DSR + // + HAL_READ_UINT32(CAN_CTRL_IER(info), regval); + regval &= ~IER_RX; // disable rx interrupt for rx buf 1 + HAL_WRITE_UINT32(CAN_CTRL_IER(info), regval); + cyg_drv_interrupt_acknowledge(vector); + LPC2XXX_DBG_PRINT("rx_ISR\n"); + + return CYG_ISR_CALL_DSR; +} + + +//=========================================================================== +// High level receive interrupt handler +//=========================================================================== +static void lpc2xxx_can_rx_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data) +{ + can_channel *chan = (can_channel *)data; + cyg_uint32 regval; + CAN_DECLARE_INFO(chan); + + // + // Read the event, release the receive buffer and then reenable the + // receive interrupt for next message + // + regval = ICR_RX; // indicate RX event to getevent() function + chan->callbacks->rcv_event(chan, ®val); + HAL_WRITE_UINT32(CAN_CTRL_CMR(info), CMR_RX_RELEASE_BUF); + HAL_READ_UINT32(CAN_CTRL_IER(info), regval); + regval |= IER_RX; + HAL_WRITE_UINT32(CAN_CTRL_IER(info), regval); +} + + +//=========================================================================== +// Err ISR handler +//=========================================================================== +static cyg_uint32 lpc2xxx_can_err_ISR(cyg_vector_t vector, cyg_addrword_t data) +{ + cyg_uint8 i = 0; + LPC2XXX_DBG_PRINT("err_ISR\n"); + + while (lpc2xxx_global_can_info.active_channels[i]) + { + cyg_uint32 regval; + lpc2xxx_can_info_t *info = (lpc2xxx_can_info_t *)lpc2xxx_global_can_info.active_channels[i++]->dev_priv; + + HAL_READ_UINT32(CAN_CTRL_IER(info), regval); + regval &= ~CAN_MISC_INT; // disable all interrupt sources that may cause this int + HAL_WRITE_UINT32(CAN_CTRL_IER(info), regval); + HAL_READ_UINT32(CAN_CTRL_ICR(info), regval); + info->icr |= (regval &~ ICR_RX); // do not handle RX intrupts - this is done by RX isr + } + + cyg_drv_interrupt_acknowledge(vector); + return CYG_ISR_CALL_DSR; +} + +//=========================================================================== +// Err ISR handler +//=========================================================================== +static void lpc2xxx_can_err_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data) +{ + cyg_uint8 i = 0; + LPC2XXX_DBG_PRINT("err_DSR\n"); + + while (lpc2xxx_global_can_info.active_channels[i]) + { + cyg_uint32 regval; + can_channel *chan = (can_channel *)lpc2xxx_global_can_info.active_channels[i++]; + lpc2xxx_can_info_t *info = (lpc2xxx_can_info_t *)chan->dev_priv; + + // + // now receive the event - the events are stored in ICR field of CAN info + // + cyg_drv_isr_lock(); // disable ISR s + regval = info->icr; // read the event field + info->icr = 0; // clear the event fields - all events processed in getevent() + cyg_drv_isr_unlock(); // renable ISRs + +#ifdef CYGOPT_IO_CAN_TX_EVENT_SUPPORT + if (regval & (ICR_TX1 & CAN_MISC_INT)) +#else + if (regval & CAN_MISC_INT) +#endif + { + chan->callbacks->rcv_event(chan, ®val); + } + // + // Now reenable interrupts + // + HAL_READ_UINT32(CAN_CTRL_IER(info), regval); + regval |= CAN_MISC_INT; // reenable interrupts for the message box that caused the DSR to run + HAL_WRITE_UINT32(CAN_CTRL_IER(info), regval); + } // while (lpc2xxx_global_can_info.active_channels[i]) +} + + +#ifdef CYGOPT_IO_CAN_RUNTIME_MBOX_CFG +//=========================================================================== +// Configure message boxes for reception of any CAN message +//=========================================================================== +static void lpc2xxx_can_config_rx_all(can_channel *chan) +{ + lpc2xxx_can_info_t *info = (lpc2xxx_can_info_t *)chan->dev_priv; + // + // First clear all acceptance filter entries and then insert the + // two RX all groups + // + lpc2xxx_can_config_rx_none(chan); +#ifdef CYGOPT_IO_CAN_STD_CAN_ID + lpc2xxx_can_accfilt_add(info, 0x000, 0x7FF, CYGNUM_CAN_ID_STD); +#endif // CYGOPT_IO_CAN_STD_CAN_ID +#ifdef CYGOPT_IO_CAN_EXT_CAN_ID + lpc2xxx_can_accfilt_add(info, 0x000, 0x1FFFFFFF, CYGNUM_CAN_ID_EXT); +#endif // CYGOPT_IO_CAN_EXT_CAN_ID + + info->flags |= INFO_FLAG_RX_ALL; +#ifdef CYGDBG_DEVS_CAN_LPC2XXX_DEBUG + lpc2xxx_can_accfilt_dbg_dump(); +#endif // CYGDBG_DEVS_CAN_LPC2XXX_DEBUG +} +#endif // CYGOPT_IO_CAN_RUNTIME_MBOX_CFG + + +#ifndef CYGOPT_IO_CAN_RUNTIME_MBOX_CFG +//=========================================================================== +// Configure message boxes for reception of any CAN message +//=========================================================================== +static void lpc2xxx_can_config_rx_all(can_channel *chan) +{ + lpc2xxx_can_info_t *info = (lpc2xxx_can_info_t *)chan->dev_priv; + + lpc2xxx_can_accfilt_simple_rx_all(); + info->flags |= INFO_FLAG_RX_ALL; + +#ifdef CYGDBG_DEVS_CAN_LPC2XXX_DEBUG + lpc2xxx_can_accfilt_dbg_dump(); +#endif // CYGDBG_DEVS_CAN_LPC2XXX_DEBUG +} +#endif // CYGOPT_IO_CAN_RUNTIME_MBOX_CFG + + +//--------------------------------------------------------------------------- +// EOF can_lpc2xxx.c
new file mode 100644 --- /dev/null +++ b/packages/devs/can/arm/lpc2xxx/current/tests/can_baudrates.c @@ -0,0 +1,232 @@ +//========================================================================== +// +// can_baudrates.c +// +// CAN test of all supported baudrates +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos 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 General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. +// at http://sources.redhat.com/ecos/ecos-license/ +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): Uwe Kindler +// Contributors: Uwe Kindler +// Date: 2007-06-26 +// Description: CAN LPC2xxx baudrate test +//####DESCRIPTIONEND#### + + +//=========================================================================== +// INCLUDES +//=========================================================================== +#include <pkgconf/system.h> + +#include <cyg/infra/testcase.h> // test macros +#include <cyg/infra/cyg_ass.h> // assertion macros +#include <cyg/infra/diag.h> + +// Package requirements +#if defined(CYGPKG_IO_CAN) && defined(CYGPKG_KERNEL) + +#include <pkgconf/kernel.h> +#include <cyg/io/io.h> +#include <cyg/io/canio.h> +#include <pkgconf/devs_can_loop.h> + + +// Package option requirements +#if defined(CYGFUN_KERNEL_API_C) + +#include <cyg/hal/hal_arch.h> // CYGNUM_HAL_STACK_SIZE_TYPICAL +#include <cyg/kernel/kapi.h> + + +#include "can_test_aux.inl" // include CAN test auxiliary functions +//=========================================================================== +// DATA TYPES +//=========================================================================== +typedef struct st_thread_data +{ + cyg_thread obj; + long stack[CYGNUM_HAL_STACK_SIZE_TYPICAL]; + cyg_handle_t hdl; +} thread_data_t; + + +//=========================================================================== +// LOCAL DATA +//=========================================================================== +cyg_thread_entry_t can0_thread; +thread_data_t can0_thread_data; +cyg_io_handle_t hCAN; + + +// +// The table of baudrates to test +// +static cyg_can_baud_rate_t baudrate_tbl[9] = +{ + CYGNUM_CAN_KBAUD_10, + CYGNUM_CAN_KBAUD_20, + CYGNUM_CAN_KBAUD_50, + CYGNUM_CAN_KBAUD_100, + CYGNUM_CAN_KBAUD_125, + CYGNUM_CAN_KBAUD_250, + CYGNUM_CAN_KBAUD_500, + CYGNUM_CAN_KBAUD_800, + CYGNUM_CAN_KBAUD_1000, +}; + +// +// String table forprinting supported baudrates +// +static char* baudrate_strings_tbl[9] = +{ + "10", + "20", + "50", + "100", + "125", + "250", + "500", + "800", + "1000", +}; + + +//=========================================================================== +// Thread 0 +//=========================================================================== +void can0_thread(cyg_addrword_t data) +{ + cyg_uint32 len; + cyg_can_event rx_event; + cyg_uint32 i; + cyg_can_info_t can_info; + + diag_printf("\n\nWhen the LPC2xxx driver selects a new baudrate then you need\n" + "to setup your hardware to the new baudrate and send one CAN\n" + "single CAN standard message.\n"); + // + // Test all supported baudrates + // + for (i = 0; i < 9; ++i) + { + diag_printf("\n\nBaudrate: %s Kbaud\n", baudrate_strings_tbl[i]); + can_info.baud = baudrate_tbl[i]; + len = sizeof(can_info); + if (ENOERR != cyg_io_set_config(hCAN, CYG_IO_SET_CONFIG_CAN_INFO, &can_info, &len)) + { + diag_printf("not supported\n"); + continue; + } + else + { + diag_printf("waiting for CAN message...\n"); + } + + len = sizeof(rx_event); + // + // First receive CAN event from real CAN hardware + // + len = sizeof(rx_event); + if (ENOERR != cyg_io_read(hCAN, &rx_event, &len)) + { + CYG_TEST_FAIL_FINISH("Error reading from channel 0"); + } + + if (rx_event.flags & CYGNUM_CAN_EVENT_RX) + { + print_can_msg(&rx_event.msg, "RX chan 1:"); + } // if (rx_event.flags & CYGNUM_CAN_EVENT_RX) + else + { + print_can_flags(rx_event.flags, ""); + CYG_TEST_FAIL_FINISH("Rx message expected"); + } + } // for (i = 0; i < 9; ++i) + + CYG_TEST_PASS_FINISH("CAN baudrate test OK"); +} + + +//=========================================================================== +// Entry point +//=========================================================================== +void cyg_start(void) +{ + CYG_TEST_INIT(); + + // + // open CAN device driver channel 1 + // + if (ENOERR != cyg_io_lookup(CYGPKG_DEVS_CAN_LPC2XXX_CAN0_NAME, &hCAN)) + { + CYG_TEST_FAIL_FINISH("Error opening CAN channel 0"); + } + + // + // create the main thread + // + cyg_thread_create(5, can0_thread, + (cyg_addrword_t) 0, + "can_tx_thread", + (void *) can0_thread_data.stack, + 1024 * sizeof(long), + &can0_thread_data.hdl, + &can0_thread_data.obj); + + cyg_thread_resume(can0_thread_data.hdl); + + cyg_scheduler_start(); +} +#else // CYGFUN_KERNEL_API_C +#define N_A_MSG "Needs kernel C API" +#endif + +#else // CYGPKG_IO_CAN && CYGPKG_KERNEL +#define N_A_MSG "Needs Kernel" +#endif + +#ifdef N_A_MSG +void +cyg_start( void ) +{ + CYG_TEST_INIT(); + CYG_TEST_NA(N_A_MSG); +} +#endif // N_A_MSG + +//--------------------------------------------------------------------------- +// EOF can_busload.c
new file mode 100644 --- /dev/null +++ b/packages/devs/can/arm/lpc2xxx/current/tests/can_busload.c @@ -0,0 +1,254 @@ +//========================================================================== +// +// can_busload.c +// +// CAN bus load test +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos 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 General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. +// at http://sources.redhat.com/ecos/ecos-license/ +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): Uwe Kindler +// Contributors: Uwe Kindler +// Date: 2007-06-26 +// Description: CAN bus load test +//####DESCRIPTIONEND#### + + +//=========================================================================== +// INCLUDES +//=========================================================================== +#include <pkgconf/system.h> + +#include <cyg/infra/testcase.h> // test macros +#include <cyg/infra/cyg_ass.h> // assertion macros +#include <cyg/infra/diag.h> + +// Package requirements +#if defined(CYGPKG_IO_CAN) && defined(CYGPKG_KERNEL) + +#include <pkgconf/kernel.h> +#include <cyg/io/io.h> +#include <cyg/io/canio.h> +#include <pkgconf/devs_can_loop.h> + + +// Package option requirements +#if defined(CYGFUN_KERNEL_API_C) + +#include <cyg/hal/hal_arch.h> // CYGNUM_HAL_STACK_SIZE_TYPICAL +#include <cyg/kernel/kapi.h> + +// We need two CAN channels +#if defined(CYGPKG_DEVS_CAN_LPC2XXX_CAN0) && defined(CYGPKG_DEVS_CAN_LPC2XXX_CAN1) + + +// The same baud rates are required because we send from one channel to the other one +#if CYGNUM_DEVS_CAN_LPC2XXX_CAN0_KBAUD == CYGNUM_DEVS_CAN_LPC2XXX_CAN1_KBAUD + + +// We need a large RX buffer +#ifdef CYGNUM_DEVS_CAN_LPC2XXX_CAN0_QUEUESIZE_RX_1024 + +#include "can_test_aux.inl" // include CAN test auxiliary functions +//=========================================================================== +// DATA TYPES +//=========================================================================== +typedef struct st_thread_data +{ + cyg_thread obj; + long stack[CYGNUM_HAL_STACK_SIZE_TYPICAL]; + cyg_handle_t hdl; +} thread_data_t; + + +//=========================================================================== +// LOCAL DATA +//=========================================================================== +cyg_thread_entry_t can0_thread; +thread_data_t can0_thread_data; +cyg_io_handle_t hCAN_Tbl[2]; + + +//=========================================================================== +// Thread 0 +//=========================================================================== +void can0_thread(cyg_addrword_t data) +{ + cyg_uint32 len; + cyg_can_message tx_msg; + cyg_can_event rx_event; + cyg_uint32 i; + cyg_uint32 rx_msg_cnt = 0; + + + // + // Prepeare message - we use a data length of 0 bytes here. Each received message + // causes an iterrupt. The shortest message is a 0 data byte message. This will generate + // the highest interrupt rate + // + CYG_CAN_MSG_SET_PARAM(tx_msg, 0, CYGNUM_CAN_ID_STD, 0, CYGNUM_CAN_FRAME_DATA); + + // + // Now send 1024 CAN messages as fast as possible to stress the receiver of CAN + // channel 1 + // + for (i = 0; i< 1024; ++i) + { + tx_msg.id = i; + len = sizeof(tx_msg); + if (ENOERR != cyg_io_write(hCAN_Tbl[1], &tx_msg, &len)) + { + CYG_TEST_FAIL_FINISH("Error writing to channel 0"); + } + } + + // + // Now try to receive all 1024 CAN messages. If all messages are received + // and no overrun occured then the message processing is fast enought + // + while (1) + { + len = sizeof(rx_event); + // + // First receive CAN event from real CAN hardware + // + len = sizeof(rx_event); + if (ENOERR != cyg_io_read(hCAN_Tbl[0], &rx_event, &len)) + { + CYG_TEST_FAIL_FINISH("Error reading from channel 1"); + } + + if (rx_event.flags & CYGNUM_CAN_EVENT_RX) + { + print_can_msg(&rx_event.msg, "RX chan 1:"); + rx_msg_cnt++; + if (rx_msg_cnt == 1024) + { + CYG_TEST_PASS_FINISH("CAN load test OK"); + } + } // if (rx_event.flags & CYGNUM_CAN_EVENT_RX) + else + { + print_can_flags(rx_event.flags, ""); + if (rx_event.flags & CYGNUM_CAN_EVENT_OVERRUN_RX) + { + CYG_TEST_FAIL_FINISH("RX overrun for channel 1"); + } + + if (rx_event.flags & CYGNUM_CAN_EVENT_ERR_PASSIVE) + { + CYG_TEST_FAIL_FINISH("Channel 1 error passive event"); + } + + if (rx_event.flags & CYGNUM_CAN_EVENT_BUS_OFF) + { + CYG_TEST_FAIL_FINISH("Channel 1 bus off event"); + } + } + } // while (1) +} + + +//=========================================================================== +// Entry point +//=========================================================================== +void cyg_start(void) +{ + CYG_TEST_INIT(); + + // + // open CAN device driver channel 1 + // + if (ENOERR != cyg_io_lookup(CYGPKG_DEVS_CAN_LPC2XXX_CAN0_NAME, &hCAN_Tbl[0])) + { + CYG_TEST_FAIL_FINISH("Error opening CAN channel 0"); + } + + + // + // open CAN device driver channel 2 + // + if (ENOERR != cyg_io_lookup(CYGPKG_DEVS_CAN_LPC2XXX_CAN1_NAME, &hCAN_Tbl[1])) + { + CYG_TEST_FAIL_FINISH("Error opening CAN channel 1"); + } + + // + // create the main thread + // + cyg_thread_create(5, can0_thread, + (cyg_addrword_t) 0, + "can_tx_thread", + (void *) can0_thread_data.stack, + 1024 * sizeof(long), + &can0_thread_data.hdl, + &can0_thread_data.obj); + + cyg_thread_resume(can0_thread_data.hdl); + + cyg_scheduler_start(); +} +#else // CYGNUM_DEVS_CAN_LPC2XXX_CAN0_QUEUESIZE_RX_1024 +#define N_A_MSG "Channel 0 needs RX buffer size for 1024 events" +#endif + +#else // CYGNUM_DEVS_CAN_LPC2XXX_CAN0_KBAUD == CYGNUM_DEVS_CAN_LPC2XXX_CAN1_KBAUD +#define N_A_MSG "Baudrate of channel 0 and 1 need to be equal" +#endif + +#else // defined(CYGPKG_DEVS_CAN_LPC2XXX_CAN0) && defined(CYGPKG_DEVS_CAN_LPC2XXX_CAN1) +#define N_A_MSG "Needs support for CAN channel 1 and 2" +#endif + +#else // CYGFUN_KERNEL_API_C +#define N_A_MSG "Needs kernel C API" +#endif + +#else // CYGPKG_IO_CAN && CYGPKG_KERNEL +#define N_A_MSG "Needs Kernel" +#endif + +#ifdef N_A_MSG +void +cyg_start( void ) +{ + CYG_TEST_INIT(); + CYG_TEST_NA(N_A_MSG); +} +#endif // N_A_MSG + +//--------------------------------------------------------------------------- +// EOF can_busload.c
new file mode 100644 --- /dev/null +++ b/packages/devs/can/arm/lpc2xxx/current/tests/can_extended_cfg.c @@ -0,0 +1,252 @@ +//========================================================================== +// +// can_extended_cfg.c +// +// Test of extended CAN configuration keys for LPC2xxx CAN driver +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos 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 General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. +// at http://sources.redhat.com/ecos/ecos-license/ +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): Uwe Kindler +// Contributors: Uwe Kindler +// Date: 2006-06-20 +// Description: LPC2xxx CAN extended configuration test +//####DESCRIPTIONEND#### + + +//=========================================================================== +// INCLUDES +//=========================================================================== +#include <pkgconf/system.h> + +#include <cyg/infra/testcase.h> // test macros +#include <cyg/infra/cyg_ass.h> // assertion macros +#include <cyg/infra/diag.h> + +// Package requirements +#if defined(CYGPKG_IO_CAN) && defined(CYGPKG_KERNEL) + +#include <pkgconf/kernel.h> +#include <cyg/io/io.h> +#include <cyg/io/canio.h> + + +// Package option requirements +#if defined(CYGFUN_KERNEL_API_C) + +#include <cyg/hal/hal_arch.h> // CYGNUM_HAL_STACK_SIZE_TYPICAL +#include <cyg/kernel/kapi.h> + +// Package option requirements +#if defined(CYGOPT_IO_CAN_RUNTIME_MBOX_CFG) +#include "can_test_aux.inl" + +#if defined(CYGOPT_DEVS_CAN_LPC2XXX_RUNTIME_ACCFILT) +#include <cyg/io/can_lpc2xxx.h> + +//=========================================================================== +// DATA TYPES +//=========================================================================== +typedef struct st_thread_data +{ + cyg_thread obj; + long stack[CYGNUM_HAL_STACK_SIZE_TYPICAL]; + cyg_handle_t hdl; +} thread_data_t; + + +//=========================================================================== +// LOCAL DATA +//=========================================================================== +cyg_thread_entry_t can0_thread; +thread_data_t can0_thread_data; +cyg_io_handle_t hCAN0; + + +//=========================================================================== +// READER THREAD +//=========================================================================== +void can0_thread(cyg_addrword_t data) +{ + cyg_uint32 len; + cyg_can_event rx_event; + cyg_can_filtergroup_cfg acc_filt_grp; + cyg_can_msgbuf_cfg msgbox_cfg; + + // + // First we reset message buffer configuration - this is mandatory bevore starting + // message buffer runtime configuration. This call clears/frees all message buffers + // The CAN controller cannot receive any further CAN message after this call + // + msgbox_cfg.cfg_id = CYGNUM_CAN_MSGBUF_RESET_ALL; + len = sizeof(msgbox_cfg); + if (ENOERR != cyg_io_set_config(hCAN0, CYG_IO_SET_CONFIG_CAN_MSGBUF ,&msgbox_cfg, &len)) + { + CYG_TEST_FAIL_FINISH("Error resetting message buffer configuration of /dev/can0"); + } + + // + // Now we setup two different acceptance filter groups. Acceptance filter + // groups are not part of the CAN I/O layer and are a LPC2xxx specific + // feature. You should not use appcetance filter groups if you would like + // to code portable eCos CAN applications + // +#ifdef CYGOPT_IO_CAN_STD_CAN_ID + acc_filt_grp.ext = CYGNUM_CAN_ID_STD; + acc_filt_grp.lower_id_bound = 0x100; + acc_filt_grp.upper_id_bound = 0x110; + len = sizeof(acc_filt_grp); + + if (ENOERR != cyg_io_set_config(hCAN0, CYG_IO_SET_CONFIG_LPC2XXX_ACCFILT_GROUP ,&acc_filt_grp, &len)) + { + CYG_TEST_FAIL_FINISH("Error adding filter group to /dev/can0"); + } +#endif // CYGOPT_IO_CAN_STD_CAN_ID + +#ifdef CYGOPT_IO_CAN_EXT_CAN_ID + acc_filt_grp.ext = CYGNUM_CAN_ID_EXT; + acc_filt_grp.lower_id_bound = 0x2000; + acc_filt_grp.upper_id_bound = 0x2200; + len = sizeof(acc_filt_grp); + + if (ENOERR != cyg_io_set_config(hCAN0, CYG_IO_SET_CONFIG_LPC2XXX_ACCFILT_GROUP ,&acc_filt_grp, &len)) + { + CYG_TEST_FAIL_FINISH("Error adding filter group to /dev/can0"); + } +#endif // CYGOPT_IO_CAN_STD_CAN_ID + + diag_printf("\n\nNow try to send CAN messages. The device should only\n" + "receive standard messages identifiers in the range of 0x100\n" + "to 0x110 and/or extended identifiers in the range 0x2000 to\n" + "0x2200. As soon as a standard message with ID 0x110 or an\n" + "extended message with ID 0x2200 arrives, the test finishes\n\n"); + + while (1) + { + len = sizeof(rx_event); + + if (ENOERR != cyg_io_read(hCAN0, &rx_event, &len)) + { + CYG_TEST_FAIL_FINISH("Error reading from /dev/can0"); + } + else + { + print_can_flags(rx_event.flags, ""); + + if (rx_event.flags & CYGNUM_CAN_EVENT_RX) + { + print_can_msg(&rx_event.msg, ""); +#ifdef CYGOPT_IO_CAN_STD_CAN_ID + if (rx_event.msg.id == 0x110) + { + CYG_TEST_PASS_FINISH("LPC2xxx CAN message filter group test OK"); + } +#endif // CYGOPT_IO_CAN_STD_CAN_ID + +#ifdef CYGOPT_IO_CAN_EXT_CAN_ID + if (rx_event.msg.id == 0x2200) + { + CYG_TEST_PASS_FINISH("LPC2xxx CAN message filter group test OK"); + } +#endif // CYGOPT_IO_CAN_EXT_CAN_ID + + if (((rx_event.msg.id > 0x110) && (rx_event.msg.id < 0x2000)) + || (rx_event.msg.id > 0x2200)) + { + CYG_TEST_FAIL_FINISH("Received CAN identifier outside filter group bounds"); + } + } + } + } // while (1) +} + + + +void +cyg_start(void) +{ + CYG_TEST_INIT(); + + // + // open CAN device driver + // + if (ENOERR != cyg_io_lookup("/dev/can0", &hCAN0)) + { + CYG_TEST_FAIL_FINISH("Error opening /dev/can0"); + } + + // + // create the thread that accesses the CAN device driver + // + cyg_thread_create(4, can0_thread, + (cyg_addrword_t) 0, + "can0_thread", + (void *) can0_thread_data.stack, + 1024 * sizeof(long), + &can0_thread_data.hdl, + &can0_thread_data.obj); + + cyg_thread_resume(can0_thread_data.hdl); + + cyg_scheduler_start(); +} + +#else // CYGOPT_DEVS_CAN_LPC2XXX_EXTENDED_CFG_KEYS +#define N_A_MSG "Needs support for extended LPC2xxx CAN configuration keys" +#endif + +#else // CYGOPT_IO_CAN_RUNTIME_MBOX_CFG +#define N_A_MSG "Needs CAN message box runtime confuguration support" +#endif + +#else // CYGFUN_KERNEL_API_C +#define N_A_MSG "Needs kernel C API" +#endif + +#else // CYGPKG_IO_CAN && CYGPKG_KERNEL +#define N_A_MSG "Needs Kernel" +#endif + +#ifdef N_A_MSG +void +cyg_start( void ) +{ + CYG_TEST_INIT(); + CYG_TEST_NA(N_A_MSG); +} +#endif // N_A_MSG + +// EOF flexcan_remote.c
new file mode 100644 --- /dev/null +++ b/packages/devs/can/arm/lpc2xxx/current/tests/can_multichan_rx.c @@ -0,0 +1,339 @@ +//========================================================================== +// +// can_multichan_rx.c +// +// CAN RX test for multiple CAN channels +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos 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 General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. +// at http://sources.redhat.com/ecos/ecos-license/ +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): Uwe Kindler +// Contributors: Uwe Kindler +// Date: 2007-06-26 +// Description: CAN RX test for multiple CAN controller channels +//####DESCRIPTIONEND#### + + +//=========================================================================== +// INCLUDES +//=========================================================================== +#include <pkgconf/system.h> + +#include <cyg/infra/testcase.h> // test macros +#include <cyg/infra/cyg_ass.h> // assertion macros +#include <cyg/infra/diag.h> + +// Package requirements +#if defined(CYGPKG_IO_CAN) && defined(CYGPKG_KERNEL) + +#include <pkgconf/kernel.h> +#include <cyg/io/io.h> +#include <cyg/io/canio.h> + + +// Package option requirements +#if defined(CYGFUN_KERNEL_API_C) + +#include <cyg/hal/hal_arch.h> // CYGNUM_HAL_STACK_SIZE_TYPICAL +#include <cyg/kernel/kapi.h> + +// Package option requirements +#if defined(CYGOPT_IO_CAN_SUPPORT_NONBLOCKING) + +#include "can_test_aux.inl" // include CAN test auxiliary functions +//=========================================================================== +// DATA TYPES +//=========================================================================== +typedef struct st_thread_data +{ + cyg_thread obj; + long stack[CYGNUM_HAL_STACK_SIZE_TYPICAL]; + cyg_handle_t hdl; +} thread_data_t; + + +//=========================================================================== +// LOCAL DATA +//=========================================================================== +cyg_thread_entry_t can_thread; +thread_data_t can0_thread_data; +cyg_io_handle_t hCAN_Tbl[4]; + + +//=========================================================================== +// Setup acceptance filter +//=========================================================================== +void can_setup_channel(cyg_io_handle_t hCAN, unsigned char Channel) +{ + cyg_uint32 len; + cyg_can_msgbuf_cfg msgbox_cfg; + cyg_uint8 i; + cyg_uint32 blocking; + + // + // First we reset message buffer configuration - this is mandatory bevore starting + // message buffer runtime configuration. This call clears/frees all message buffers + // The CAN controller cannot receive any further CAN message after this call + // + msgbox_cfg.cfg_id = CYGNUM_CAN_MSGBUF_RESET_ALL; + len = sizeof(msgbox_cfg); + if (ENOERR != cyg_io_set_config(hCAN, CYG_IO_SET_CONFIG_CAN_MSGBUF ,&msgbox_cfg, &len)) + { + CYG_TEST_FAIL_FINISH("Error resetting message buffer configuration of /dev/can0"); + } + + // + // Now setup 10 message filters for this channel + // + for (i = 0; i < 10; ++i) + { + cyg_can_filter rx_filter; + + rx_filter.cfg_id = CYGNUM_CAN_MSGBUF_RX_FILTER_ADD; + rx_filter.msg.id = Channel * 0x100 + i; + rx_filter.msg.ext = CYGNUM_CAN_ID_STD; + + len = sizeof(rx_filter); + if (ENOERR != cyg_io_set_config(hCAN, CYG_IO_SET_CONFIG_CAN_MSGBUF ,&rx_filter, &len)) + { + CYG_TEST_FAIL_FINISH("Error writing config"); + } + else if (CYGNUM_CAN_MSGBUF_NA == rx_filter.handle) + { + CYG_TEST_FAIL_FINISH("Error setting up message filter"); + } + } + + // + // Now set driver into nonblocking mode because the receiver thread will + // receive the messages from all channels + // + blocking = 0; + len = sizeof(blocking); + if (ENOERR != cyg_io_set_config(hCAN, CYG_IO_SET_CONFIG_READ_BLOCKING ,&blocking, &len)) + { + CYG_TEST_FAIL_FINISH("Error setting channel into nonblocking mode"); + } + + // + // If timeouts are supported we need to setup a timeout value of 0 because + // the driver should return immediatelly if no message is available + // +#ifdef CYGOPT_IO_CAN_SUPPORT_TIMEOUTS + cyg_can_timeout_info_t timeouts; + + timeouts.rx_timeout = 0; + timeouts.tx_timeout = 0; + len = sizeof(timeouts); + if (ENOERR != cyg_io_set_config(hCAN, CYG_IO_SET_CONFIG_CAN_TIMEOUT ,&timeouts, &len)) + { + CYG_TEST_FAIL_FINISH("Error setting timeout for channel"); + } +#endif +} + + +//=========================================================================== +// READER THREAD +//=========================================================================== +void can_thread(cyg_addrword_t data) +{ + cyg_uint32 len; + cyg_uint8 i = 0; + + // + // Check that all cannels have the same baudrate + // +#ifdef CYGPKG_DEVS_CAN_LPC2XXX_CAN0 + can_setup_channel(hCAN_Tbl[0], 0); +#endif + +#ifdef CYGPKG_DEVS_CAN_LPC2XXX_CAN1 + can_setup_channel(hCAN_Tbl[1], 1); +#endif + +#ifdef CYGPKG_DEVS_CAN_LPC2XXX_CAN2 + can_setup_channel(hCAN_Tbl[2], 2); +#endif + +#ifdef CYGPKG_DEVS_CAN_LPC2XXX_CAN3 + can_setup_channel(hCAN_Tbl[3], 3); +#endif + + diag_printf("\n\nThis test uses all available CAN channels for reception\n" + "of CAN standard messages. The following messages will be received:\n\n"); + + for (i = 0; i < 4; ++i) + { + if (hCAN_Tbl[i]) + { + diag_printf("CAN channel %d: msg: 0x%x - 0x%x\n", i, i * 0x100, i * 0x100 + 9); + } + } + + diag_printf("\n\nYou can stop this test by sending a message with ID 0xX10\n"); + + while (1) + { + for (i = 0; i < 4; ++i) + { + if (hCAN_Tbl[i]) + { + Cyg_ErrNo ret; + cyg_can_event rx_event; + + len = sizeof(rx_event); + ret = cyg_io_read(hCAN_Tbl[i], &rx_event, &len); + if ((ret == -EAGAIN) || (ret == -EINTR)) + { + continue; + } + + if (ENOERR != ret) + { + CYG_TEST_FAIL_FINISH("Error reading from channel"); + } + else + { + diag_printf("Channel %d events: ", i); + print_can_flags(rx_event.flags, ""); + if (rx_event.flags & CYGNUM_CAN_EVENT_RX) + { + print_can_msg(&rx_event.msg, ""); + if ((rx_event.msg.id & 9) == 9) + { + CYG_TEST_PASS_FINISH("LPC2xxx CAN multi channel RX test OK"); + } + } // if (rx_event.flags & CYGNUM_CAN_EVENT_RX) + } + } // if (hCAN_Tbl[i]) + } // for (i = 0; i < 4; ++i) + } // while (1) +} + + + +void +cyg_start(void) +{ + CYG_TEST_INIT(); + +#ifdef CYGPKG_DEVS_CAN_LPC2XXX_CAN0 + // + // open CAN device driver + // + if (ENOERR != cyg_io_lookup(CYGPKG_DEVS_CAN_LPC2XXX_CAN0_NAME, &hCAN_Tbl[0])) + { + CYG_TEST_FAIL_FINISH("Error opening CAN channel 0"); + } +#else + hCAN_Tbl[0] = 0; +#endif + +#ifdef CYGPKG_DEVS_CAN_LPC2XXX_CAN1 + // + // open CAN device driver + // + if (ENOERR != cyg_io_lookup(CYGPKG_DEVS_CAN_LPC2XXX_CAN1_NAME, &hCAN_Tbl[1])) + { + CYG_TEST_FAIL_FINISH("Error opening CAN channel 1"); + } +#else + hCAN_Tbl[1] = 0; +#endif + +#ifdef CYGPKG_DEVS_CAN_LPC2XXX_CAN2 + // + // open CAN device driver + // + if (ENOERR != cyg_io_lookup(CYGPKG_DEVS_CAN_LPC2XXX_CAN2_NAME, &hCAN_Tbl[2])) + { + CYG_TEST_FAIL_FINISH("Error opening CAN channel 2"); + } +#else + hCAN_Tbl[2] = 0; +#endif + +#ifdef CYGPKG_DEVS_CAN_LPC2XXX_CAN3 + // + // open CAN device driver + // + if (ENOERR != cyg_io_lookup(CYGPKG_DEVS_CAN_LPC2XXX_CAN3_NAME, &hCAN_Tbl[3])) + { + CYG_TEST_FAIL_FINISH("Error opening CAN channel 3"); + } +#else + hCAN_Tbl[3] = 0; +#endif + + // + // create the thread that accesses the CAN device driver + // + cyg_thread_create(4, can_thread, + (cyg_addrword_t) 0, + "can0_thread", + (void *) can0_thread_data.stack, + 1024 * sizeof(long), + &can0_thread_data.hdl, + &can0_thread_data.obj); + + cyg_thread_resume(can0_thread_data.hdl); + + cyg_scheduler_start(); +} +#else // CYGOPT_IO_CAN_SUPPORT_NONBLOCKING +#define N_A_MSG "Needs support for nonblocking calls" +#endif + +#else // CYGFUN_KERNEL_API_C +#define N_A_MSG "Needs kernel C API" +#endif + +#else // CYGPKG_IO_CAN && CYGPKG_KERNEL +#define N_A_MSG "Needs Kernel" +#endif + +#ifdef N_A_MSG +void +cyg_start( void ) +{ + CYG_TEST_INIT(); + CYG_TEST_NA(N_A_MSG); +} +#endif // N_A_MSG + +//--------------------------------------------------------------------------- +// EOF can_multichan_rx.c
new file mode 100644 --- /dev/null +++ b/packages/devs/can/arm/lpc2xxx/current/tests/can_multichan_tx.c @@ -0,0 +1,292 @@ +//========================================================================== +// +// can_multichan_tx.c +// +// CAN TX test for multiple CAN channels +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos 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 General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. +// at http://sources.redhat.com/ecos/ecos-license/ +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): Uwe Kindler +// Contributors: Uwe Kindler +// Date: 2007-06-26 +// Description: CAN TX test for multiple CAN controller channels +//####DESCRIPTIONEND#### + + +//=========================================================================== +// INCLUDES +//=========================================================================== +#include <pkgconf/system.h> + +#include <cyg/infra/testcase.h> // test macros +#include <cyg/infra/cyg_ass.h> // assertion macros +#include <cyg/infra/diag.h> + +// Package requirements +#if defined(CYGPKG_IO_CAN) && defined(CYGPKG_KERNEL) + +#include <pkgconf/kernel.h> +#include <cyg/io/io.h> +#include <cyg/io/canio.h> + + +// Package option requirements +#if defined(CYGFUN_KERNEL_API_C) + +#include <cyg/hal/hal_arch.h> // CYGNUM_HAL_STACK_SIZE_TYPICAL +#include <cyg/kernel/kapi.h> + + +//=========================================================================== +// DATA TYPES +//=========================================================================== +typedef struct st_thread_data +{ + cyg_thread obj; + long stack[CYGNUM_HAL_STACK_SIZE_TYPICAL]; + cyg_handle_t hdl; +} thread_data_t; + + +//=========================================================================== +// LOCAL DATA +//=========================================================================== +cyg_thread_entry_t can_thread; +thread_data_t can0_thread_data; +cyg_io_handle_t hCAN_Tbl[4]; + + +//=========================================================================== +// READER THREAD +//=========================================================================== +void can_thread(cyg_addrword_t data) +{ + cyg_uint32 len; + cyg_can_message tx_msg; + cyg_can_info_t can_info; + cyg_can_baud_rate_t baud; + cyg_uint8 i = 0; + cyg_uint8 j = 0; + + // + // Check that all cannels have the same baudrate + // +#ifdef CYGPKG_DEVS_CAN_LPC2XXX_CAN0 + len = sizeof(can_info); + if (ENOERR != cyg_io_get_config(hCAN_Tbl[0], CYG_IO_GET_CONFIG_CAN_INFO, &can_info, &len)) + { + CYG_TEST_FAIL_FINISH("Error reading baudrate of CAN channel 0"); + } + else + { + baud = can_info.baud; + ++i; + } +#endif + +#ifdef CYGPKG_DEVS_CAN_LPC2XXX_CAN1 + len = sizeof(can_info); + if (ENOERR != cyg_io_get_config(hCAN_Tbl[1], CYG_IO_GET_CONFIG_CAN_INFO, &can_info, &len)) + { + CYG_TEST_FAIL_FINISH("Error reading baudrate of CAN channel 1"); + } + else + { + if (i && (baud != can_info.baud)) + { + CYG_TEST_FAIL_FINISH("Error - different baudrates for CAN channel 0 and 1"); + } + baud = can_info.baud; + ++i; + } +#endif + +#ifdef CYGPKG_DEVS_CAN_LPC2XXX_CAN2 + len = sizeof(can_info); + if (ENOERR != cyg_io_get_config(hCAN_Tbl[2], CYG_IO_GET_CONFIG_CAN_INFO, &can_info, &len)) + { + CYG_TEST_FAIL_FINISH("Error reading baudrate of CAN channel 2"); + } + else + { + if (i && (baud != can_info.baud)) + { + CYG_TEST_FAIL_FINISH("Error - different baudrates for CAN channel 1 and 2"); + } + baud = can_info.baud; + ++i; + } +#endif + +#ifdef CYGPKG_DEVS_CAN_LPC2XXX_CAN3 + len = sizeof(can_info); + if (ENOERR != cyg_io_get_config(hCAN_Tbl[3], CYG_IO_GET_CONFIG_CAN_INFO, &can_info, &len)) + { + CYG_TEST_FAIL_FINISH("Error reading baudrate of CAN channel 3"); + } + else + { + if (i && (baud != can_info.baud)) + { + CYG_TEST_FAIL_FINISH("Error - different baudrates for CAN channel 2 and 3"); + } + baud = can_info.baud; + ++i; + } +#endif + + diag_printf("\n\nYou should no receive 4 CAN messages from each active CAN channel\n"); + + // + // Now each CAN channel sends 10 CAN messages + // + for (i = 0; i < 4; ++i) + { + if (hCAN_Tbl[i]) + { + CYG_CAN_MSG_SET_PARAM(tx_msg, i * 0x100, CYGNUM_CAN_ID_STD, 4, CYGNUM_CAN_FRAME_DATA); + tx_msg.data.dwords[0] = 0; + tx_msg.data.dwords[1] = 0; + char err_msg[64]; + diag_snprintf(err_msg, sizeof(err_msg), "Error sending TX using CAN channel %d", i); + for (j = 0; j < 4; ++j) + { + tx_msg.id = i * 0x100 + j; + tx_msg.data.bytes[0] = j; + len = sizeof(tx_msg); + if (ENOERR != cyg_io_write(hCAN_Tbl[i], &tx_msg, &len)) + { + CYG_TEST_FAIL_FINISH(err_msg); + } + } + } // if (hCAN_Tbl[i]) + } // for (i = 0; i < 4; ++i) + + CYG_TEST_PASS_FINISH("LPC2xxx CAN multi channel TX test OK"); +} + + + +void +cyg_start(void) +{ + CYG_TEST_INIT(); + +#ifdef CYGPKG_DEVS_CAN_LPC2XXX_CAN0 + // + // open CAN device driver + // + if (ENOERR != cyg_io_lookup(CYGPKG_DEVS_CAN_LPC2XXX_CAN0_NAME, &hCAN_Tbl[0])) + { + CYG_TEST_FAIL_FINISH("Error opening CAN channel 0"); + } +#else + hCAN_Tbl[0] = 0; +#endif + +#ifdef CYGPKG_DEVS_CAN_LPC2XXX_CAN1 + // + // open CAN device driver + // + if (ENOERR != cyg_io_lookup(CYGPKG_DEVS_CAN_LPC2XXX_CAN1_NAME, &hCAN_Tbl[1])) + { + CYG_TEST_FAIL_FINISH("Error opening CAN channel 1"); + } +#else + hCAN_Tbl[1] = 0; +#endif + +#ifdef CYGPKG_DEVS_CAN_LPC2XXX_CAN2 + // + // open CAN device driver + // + if (ENOERR != cyg_io_lookup(CYGPKG_DEVS_CAN_LPC2XXX_CAN2_NAME, &hCAN_Tbl[2])) + { + CYG_TEST_FAIL_FINISH("Error opening CAN channel 2"); + } +#else + hCAN_Tbl[2] = 0; +#endif + +#ifdef CYGPKG_DEVS_CAN_LPC2XXX_CAN3 + // + // open CAN device driver + // + if (ENOERR != cyg_io_lookup(CYGPKG_DEVS_CAN_LPC2XXX_CAN3_NAME, &hCAN_Tbl[3])) + { + CYG_TEST_FAIL_FINISH("Error opening CAN channel 3"); + } +#else + hCAN_Tbl[3] = 0; +#endif + + // + // create the thread that accesses the CAN device driver + // + cyg_thread_create(4, can_thread, + (cyg_addrword_t) 0, + "can0_thread", + (void *) can0_thread_data.stack, + 1024 * sizeof(long), + &can0_thread_data.hdl, + &can0_thread_data.obj); + + cyg_thread_resume(can0_thread_data.hdl); + + cyg_scheduler_start(); +} + + +#else // CYGFUN_KERNEL_API_C +#define N_A_MSG "Needs kernel C API" +#endif + +#else // CYGPKG_IO_CAN && CYGPKG_KERNEL +#define N_A_MSG "Needs Kernel" +#endif + +#ifdef N_A_MSG +void +cyg_start( void ) +{ + CYG_TEST_INIT(); + CYG_TEST_NA(N_A_MSG); +} +#endif // N_A_MSG + +//--------------------------------------------------------------------------- +// EOF can_multichan_tx.c
new file mode 100644 --- /dev/null +++ b/packages/devs/can/arm/lpc2xxx/current/tests/can_rx_tx.c @@ -0,0 +1,349 @@ +//========================================================================== +// +// can_rx_tx.c +// +// CAN RX / TX test +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos 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 General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. +// at http://sources.redhat.com/ecos/ecos-license/ +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): Uwe Kindler +// Contributors: Uwe Kindler +// Date: 2007-06-26 +// Description: CAN RX/TX test for 2 CAN channels +//####DESCRIPTIONEND#### + + +//=========================================================================== +// INCLUDES +//=========================================================================== +#include <pkgconf/system.h> + +#include <cyg/infra/testcase.h> // test macros +#include <cyg/infra/cyg_ass.h> // assertion macros +#include <cyg/infra/diag.h> + +// Package requirements +#if defined(CYGPKG_IO_CAN) && defined(CYGPKG_KERNEL) + +#include <pkgconf/kernel.h> +#include <cyg/io/io.h> +#include <cyg/io/canio.h> +#include <pkgconf/devs_can_loop.h> + + +// Package option requirements +#if defined(CYGFUN_KERNEL_API_C) + +#include <cyg/hal/hal_arch.h> // CYGNUM_HAL_STACK_SIZE_TYPICAL +#include <cyg/kernel/kapi.h> + +// We need two CAN channels +#if defined(CYGPKG_DEVS_CAN_LPC2XXX_CAN0) && defined(CYGPKG_DEVS_CAN_LPC2XXX_CAN1) + + +// The same baud rates are required because we send from one channel to the other one +#if CYGNUM_DEVS_CAN_LPC2XXX_CAN0_KBAUD == CYGNUM_DEVS_CAN_LPC2XXX_CAN1_KBAUD + + +// We need the loop can driver +#if defined(CYGPKG_DEVS_CAN_LOOP_CAN0) && defined(CYGPKG_DEVS_CAN_LOOP_CAN1) + +#include "can_test_aux.inl" // include CAN test auxiliary functions +//=========================================================================== +// DATA TYPES +//=========================================================================== +typedef struct st_thread_data +{ + cyg_thread obj; + long stack[CYGNUM_HAL_STACK_SIZE_TYPICAL]; + cyg_handle_t hdl; +} thread_data_t; + + +//=========================================================================== +// LOCAL DATA +//=========================================================================== +cyg_thread_entry_t can_tx_thread; +thread_data_t can0_thread_data; +cyg_thread_entry_t can_rx_thread; +thread_data_t can1_thread_data; +cyg_io_handle_t hCAN_Tbl[2]; +cyg_io_handle_t hLoopCAN_Tbl[2]; + + +//=========================================================================== +// Thread 0 +//=========================================================================== +void can_rx_thread(cyg_addrword_t data) +{ + cyg_uint32 len; + cyg_can_event rx_event; + cyg_can_event loop_rx_event; + cyg_uint32 msg_cnt = 0; + cyg_uint8 i; + + while (msg_cnt < 0xF0) + { + + // + // First receive CAN event from real CAN hardware + // + len = sizeof(rx_event); + if (ENOERR != cyg_io_read(hCAN_Tbl[1], &rx_event, &len)) + { + CYG_TEST_FAIL_FINISH("Error reading from channel 1"); + } + + if (rx_event.flags & CYGNUM_CAN_EVENT_RX) + { + print_can_msg(&rx_event.msg, "RX chan 1:"); + } // if (rx_event.flags & CYGNUM_CAN_EVENT_RX) + else + { + print_can_flags(rx_event.flags, ""); + } + + // + // Now receive CAN event from loop CAN driver + // + len = sizeof(loop_rx_event); + if (ENOERR != cyg_io_read(hLoopCAN_Tbl[1], &loop_rx_event, &len)) + { + CYG_TEST_FAIL_FINISH("Error reading from loop channel 1"); + } + + if (rx_event.flags & CYGNUM_CAN_EVENT_RX) + { + print_can_msg(&rx_event.msg, "RX loop 1:"); + } // if (rx_event.flags & CYGNUM_CAN_EVENT_RX) + else + { + print_can_flags(rx_event.flags, ""); + } + + // + // Chaeck message ID and DLC of HW CAN message and CAN message from loop driver + // booth should be the same + // + if (rx_event.msg.id != loop_rx_event.msg.id) + { + CYG_TEST_FAIL_FINISH("Received message IDs of hw CAN channel and loop CAN channel are not equal"); + } + + if (rx_event.msg.dlc != loop_rx_event.msg.dlc) + { + CYG_TEST_FAIL_FINISH("Received DLCs of hw CAN msg and loop CAN msg are not equal"); + } + + // + // Now check each data byte of the receive message + // + for (i = 0; i < rx_event.msg.dlc; ++i) + { + if (rx_event.msg.data.bytes[i] != loop_rx_event.msg.data.bytes[i]) + { + CYG_TEST_FAIL_FINISH("Data of hw CAN msg and loop CAN msg are not equal"); + } + + if (rx_event.msg.data.bytes[i] != (i + msg_cnt)) + { + CYG_TEST_FAIL_FINISH("CAN message contains unexpected data"); + } + } + + msg_cnt++; + } // while (1) + + CYG_TEST_PASS_FINISH("CAN rx/tx test OK"); +} + + +//=========================================================================== +// Thread 1 +//=========================================================================== +void can_tx_thread(cyg_addrword_t data) +{ + cyg_uint32 len; + cyg_can_message tx_msg; + cyg_uint32 msg_cnt = 0; + cyg_uint8 i; + + + CYG_CAN_MSG_SET_PARAM(tx_msg, 0, CYGNUM_CAN_ID_STD, 0, CYGNUM_CAN_FRAME_DATA); + + // + // Prepare CAN message with a known CAN state + // + for (i = 0; i < 8; ++i) + { + tx_msg.data.bytes[i] = i; + } + + while (msg_cnt < 0xF0) + { + tx_msg.id = msg_cnt; + len = sizeof(tx_msg); + if (ENOERR != cyg_io_write(hCAN_Tbl[0], &tx_msg, &len)) + { + CYG_TEST_FAIL_FINISH("Error writing to channel 0"); + } + + + tx_msg.id = msg_cnt; + len = sizeof(tx_msg); + if (ENOERR != cyg_io_write(hLoopCAN_Tbl[0], &tx_msg, &len)) + { + CYG_TEST_FAIL_FINISH("Error writing to channel 1"); + } + + // + // Increment data in each single data byte + // + for (i = 0; i < 8; ++i) + { + tx_msg.data.bytes[i] += 1; + } + + msg_cnt++; + tx_msg.dlc = (tx_msg.dlc + 1) % 9; + } // while (msg_cnt < 0x100) +} + + +//=========================================================================== +// Entry point +//=========================================================================== +void cyg_start(void) +{ + CYG_TEST_INIT(); + + // + // open CAN device driver channel 1 + // + if (ENOERR != cyg_io_lookup(CYGPKG_DEVS_CAN_LPC2XXX_CAN0_NAME, &hCAN_Tbl[0])) + { + CYG_TEST_FAIL_FINISH("Error opening CAN channel 0"); + } + + + // + // open CAN device driver channel 2 + // + if (ENOERR != cyg_io_lookup(CYGPKG_DEVS_CAN_LPC2XXX_CAN1_NAME, &hCAN_Tbl[1])) + { + CYG_TEST_FAIL_FINISH("Error opening CAN channel 1"); + } + + // + // open Loop CAN device driver channel 1 + // + if (ENOERR != cyg_io_lookup(CYGDAT_DEVS_CAN_LOOP_CAN0_NAME, &hLoopCAN_Tbl[0])) + { + CYG_TEST_FAIL_FINISH("Error opening CAN channel 0"); + } + + + // + // open Loop CAN device driver channel 2 + // + if (ENOERR != cyg_io_lookup(CYGDAT_DEVS_CAN_LOOP_CAN1_NAME, &hLoopCAN_Tbl[1])) + { + CYG_TEST_FAIL_FINISH("Error opening CAN channel 1"); + } + + + + + + // + // create the first thread that access the CAN device driver + // + cyg_thread_create(5, can_tx_thread, + (cyg_addrword_t) 0, + "can_tx_thread", + (void *) can0_thread_data.stack, + 1024 * sizeof(long), + &can0_thread_data.hdl, + &can0_thread_data.obj); + + // + // create the second thread that access the CAN device driver + // + cyg_thread_create(4, can_rx_thread, + (cyg_addrword_t) 0, + "can_rx_thread", + (void *) can1_thread_data.stack, + 1024 * sizeof(long), + &can1_thread_data.hdl, + &can1_thread_data.obj); + + cyg_thread_resume(can0_thread_data.hdl); + cyg_thread_resume(can1_thread_data.hdl); + + cyg_scheduler_start(); +} +#else // defined(CYGPKG_IO_CAN_LOOP_CAN0) && define (CYGPKG_IO_CAN_LOOP_CAN1) +#define N_A_MSG "Needs suuport for loop CAN device driver" +#endif + +#else // CYGNUM_DEVS_CAN_LPC2XXX_CAN0_KBAUD == CYGNUM_DEVS_CAN_LPC2XXX_CAN1_KBAUD +#define N_A_MSG "Baudrate of channel 0 and 1 need to be equal" +#endif + +#else // defined(CYGPKG_DEVS_CAN_LPC2XXX_CAN0) && defined(CYGPKG_DEVS_CAN_LPC2XXX_CAN1) +#define N_A_MSG "Needs support for CAN channel 1 and 2" +#endif + +#else // CYGFUN_KERNEL_API_C +#define N_A_MSG "Needs kernel C API" +#endif + +#else // CYGPKG_IO_CAN && CYGPKG_KERNEL +#define N_A_MSG "Needs Kernel" +#endif + +#ifdef N_A_MSG +void +cyg_start( void ) +{ + CYG_TEST_INIT(); + CYG_TEST_NA(N_A_MSG); +} +#endif // N_A_MSG + +//--------------------------------------------------------------------------- +// EOF can_rx_tx.c
new file mode 100644 --- /dev/null +++ b/packages/devs/can/arm/lpc2xxx/current/tests/can_test_aux.inl @@ -0,0 +1,148 @@ +//========================================================================== +// +// can_test_aux.inl +// +// CAN test auxiliary functions +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos 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 General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. +// at http://sources.redhat.com/ecos/ecos-license/ +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): Uwe Kindler +// Contributors: Uwe Kindler +// Date: 2005-08-07 +// Description: Auxiliary functions for CAN driver tests +//####DESCRIPTIONEND#### + + +//=========================================================================== +// PRINT CAN EVENT +//=========================================================================== +void print_can_msg(cyg_can_message *pmsg, char *pMsg) +{ + char *pmsg_str; + static char* msg_tbl[] = + { + "%s [ID:%03X] [RTR:%d] [EXT:%d] [DATA:]\n", + "%s [ID:%03X] [RTR:%d] [EXT:%d] [DATA:%02X]\n", + "%s [ID:%03X] [RTR:%d] [EXT:%d] [DATA:%02X %02X]\n", + "%s [ID:%03X] [RTR:%d] [EXT:%d] [DATA:%02X %02X %02X]\n", + "%s [ID:%03X] [RTR:%d] [EXT:%d] [DATA:%02X %02X %02X %02X]\n", + "%s [ID:%03X] [RTR:%d] [EXT:%d] [DATA:%02X %02X %02X %02X %02X]\n", + "%s [ID:%03X] [RTR:%d] [EXT:%d] [DATA:%02X %02X %02X %02X %02X %02X]\n", + "%s [ID:%03X] [RTR:%d] [EXT:%d] [DATA:%02X %02X %02X %02X %02X %02X %02X]\n", + "%s [ID:%03X] [RTR:%d] [EXT:%d] [DATA:%02X %02X %02X %02X %02X %02X %02X %02X]\n" + }; + + if (pmsg->rtr) + { + diag_printf("%s [ID:%03X] [RTR:%d] [EXT:%d] [DLC:%d]\n", + pMsg, + pmsg->id, + pmsg->rtr, + pmsg->ext, + pmsg->dlc); + + return; + } + + if (pmsg->dlc > 8) + { + pmsg_str = msg_tbl[8]; + } + else + { + pmsg_str = msg_tbl[pmsg->dlc]; + } + + diag_printf(pmsg_str, + pMsg, + pmsg->id, + pmsg->rtr, + pmsg->ext, + pmsg->data.bytes[0], + pmsg->data.bytes[1], + pmsg->data.bytes[2], + pmsg->data.bytes[3], + pmsg->data.bytes[4], + pmsg->data.bytes[5], + pmsg->data.bytes[6], + pmsg->data.bytes[7]); +} + + +//=========================================================================== +// PRINT CAN EVENT FLAGS +//=========================================================================== +void print_can_flags(cyg_uint16 flags, char *pMsg) +{ + char *pmsg_str; + cyg_uint8 i ; + static char* msg_tbl[] = + { + "RX ", + "TX ", + "WRX ", + "WTX ", + "ERRP ", + "BOFF ", + "OVRX ", + "OVTX ", + "CERR ", + "LSTY ", + "ESTY ", + "ALOS ", + "DEVC ", + "PHYF ", + "PHYH ", + "PHYL " + }; + i = 0; + while (flags && (i < 16)) + { + if (flags & 0x0001) + { + pmsg_str = msg_tbl[i]; + diag_printf(pmsg_str); + } + flags >>=1; + i++; + } + + diag_printf("\n"); +} + +//--------------------------------------------------------------------------- +// end of can_test_aux.inl
