Merge branch 'wellenvogel:master' into master
This commit is contained in:
		
						commit
						6ea37c1afe
					
				| 
						 | 
					@ -0,0 +1,291 @@
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @section License
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The MIT License (MIT)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (c) 2017, Thomas Barth, barth-dev.de
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Permission is hereby granted, free of charge, to any person
 | 
				
			||||||
 | 
					 * obtaining a copy of this software and associated documentation
 | 
				
			||||||
 | 
					 * files (the "Software"), to deal in the Software without
 | 
				
			||||||
 | 
					 * restriction, including without limitation the rights to use, copy,
 | 
				
			||||||
 | 
					 * modify, merge, publish, distribute, sublicense, and/or sell copies
 | 
				
			||||||
 | 
					 * of the Software, and to permit persons to whom the Software is
 | 
				
			||||||
 | 
					 * furnished to do so, subject to the following conditions:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The above copyright notice and this permission notice shall be
 | 
				
			||||||
 | 
					 * included in all copies or substantial portions of the Software.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 | 
				
			||||||
 | 
					 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 | 
				
			||||||
 | 
					 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 | 
				
			||||||
 | 
					 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 | 
				
			||||||
 | 
					 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 | 
				
			||||||
 | 
					 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 | 
				
			||||||
 | 
					 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
				
			||||||
 | 
					 * SOFTWARE.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef __DRIVERS_CAN_REGDEF_H_
 | 
				
			||||||
 | 
					#define __DRIVERS_CAN_REGDEF_H_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					extern "C" {
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef enum  {
 | 
				
			||||||
 | 
						CAN_SPEED_100KBPS=100, 				/**< \brief CAN Node runs at 100kBit/s. */
 | 
				
			||||||
 | 
						CAN_SPEED_125KBPS=125, 				/**< \brief CAN Node runs at 125kBit/s. */
 | 
				
			||||||
 | 
						CAN_SPEED_250KBPS=250, 				/**< \brief CAN Node runs at 250kBit/s. */
 | 
				
			||||||
 | 
						CAN_SPEED_500KBPS=500, 				/**< \brief CAN Node runs at 500kBit/s. */
 | 
				
			||||||
 | 
						CAN_SPEED_800KBPS=800, 				/**< \brief CAN Node runs at 800kBit/s. */
 | 
				
			||||||
 | 
						CAN_SPEED_1000KBPS=1000				/**< \brief CAN Node runs at 1000kBit/s. */
 | 
				
			||||||
 | 
					}CAN_speed_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * \brief CAN frame type (standard/extended)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					typedef enum {
 | 
				
			||||||
 | 
						CAN_frame_std=0, 						/**< Standard frame, using 11 bit identifer. */
 | 
				
			||||||
 | 
						CAN_frame_ext=1 						/**< Extended frame, using 29 bit identifer. */
 | 
				
			||||||
 | 
					}CAN_frame_format_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * \brief CAN RTR
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					typedef enum {
 | 
				
			||||||
 | 
						CAN_no_RTR=0, 							/**< No RTR frame. */
 | 
				
			||||||
 | 
						CAN_RTR=1 								/**< RTR frame. */
 | 
				
			||||||
 | 
					}CAN_RTR_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** \brief Frame information record type */
 | 
				
			||||||
 | 
					typedef union{uint32_t U;					/**< \brief Unsigned access */
 | 
				
			||||||
 | 
						 struct {
 | 
				
			||||||
 | 
							uint8_t 			DLC:4;        	/**< \brief [3:0] DLC, Data length container */
 | 
				
			||||||
 | 
							unsigned int 		unknown_2:2;    /**< \brief \internal unknown */
 | 
				
			||||||
 | 
							CAN_RTR_t 			RTR:1;          /**< \brief [6:6] RTR, Remote Transmission Request */
 | 
				
			||||||
 | 
							CAN_frame_format_t 	FF:1;           /**< \brief [7:7] Frame Format, see# CAN_frame_format_t*/
 | 
				
			||||||
 | 
							unsigned int 		reserved_24:24;	/**< \brief \internal Reserved */
 | 
				
			||||||
 | 
						} B;
 | 
				
			||||||
 | 
					} CAN_FIR_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** \brief Start address of CAN registers */
 | 
				
			||||||
 | 
					#define MODULE_CAN              					((volatile CAN_Module_t    *)0x3ff6b000)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** \brief Get standard message ID */
 | 
				
			||||||
 | 
					#define _CAN_GET_STD_ID								(((uint32_t)MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.STD.ID[0] << 3) | \
 | 
				
			||||||
 | 
																		(MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.STD.ID[1] >> 5))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** \brief Get extended message ID */
 | 
				
			||||||
 | 
					#define _CAN_GET_EXT_ID   (((uint32_t)MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[0] << 21) | \
 | 
				
			||||||
 | 
																		(MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[1] << 13) | \
 | 
				
			||||||
 | 
																		(MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[2] << 5) | \
 | 
				
			||||||
 | 
																		(MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[3] >> 3 ))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** \brief Set standard message ID */
 | 
				
			||||||
 | 
					#define _CAN_SET_STD_ID(x)							MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.STD.ID[0] = ((x) >> 3);	\
 | 
				
			||||||
 | 
																		MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.STD.ID[1] = ((x) << 5);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** \brief Set extended message ID */
 | 
				
			||||||
 | 
					#define _CAN_SET_EXT_ID(x)							MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[0] = ((x) >> 21);	\
 | 
				
			||||||
 | 
																		MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[1] = ((x) >> 13);	\
 | 
				
			||||||
 | 
																		MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[2] = ((x) >> 5);	\
 | 
				
			||||||
 | 
																		MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.ID[3] = ((x) << 3);	\
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** \brief Interrupt status register */
 | 
				
			||||||
 | 
					typedef enum  {
 | 
				
			||||||
 | 
						__CAN_IRQ_RX=			BIT(0),					/**< \brief RX Interrupt */
 | 
				
			||||||
 | 
						__CAN_IRQ_TX=			BIT(1),					/**< \brief TX Interrupt */
 | 
				
			||||||
 | 
						__CAN_IRQ_ERR=			BIT(2),					/**< \brief Error Interrupt */
 | 
				
			||||||
 | 
						__CAN_IRQ_DATA_OVERRUN=	BIT(3),					/**< \brief Date Overrun Interrupt */
 | 
				
			||||||
 | 
						__CAN_IRQ_WAKEUP=		BIT(4),					/**< \brief Wakeup Interrupt */
 | 
				
			||||||
 | 
						__CAN_IRQ_ERR_PASSIVE=	BIT(5),					/**< \brief Passive Error Interrupt */
 | 
				
			||||||
 | 
						__CAN_IRQ_ARB_LOST=		BIT(6),					/**< \brief Arbitration lost interrupt */
 | 
				
			||||||
 | 
						__CAN_IRQ_BUS_ERR=		BIT(7),					/**< \brief Bus error Interrupt */
 | 
				
			||||||
 | 
					}__CAN_IRQ_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** \brief OCMODE options. */
 | 
				
			||||||
 | 
					typedef enum  {
 | 
				
			||||||
 | 
						__CAN_OC_BOM=			0b00,					/**< \brief bi-phase output mode */
 | 
				
			||||||
 | 
						__CAN_OC_TOM=			0b01,					/**< \brief test output mode */
 | 
				
			||||||
 | 
						__CAN_OC_NOM=			0b10,					/**< \brief normal output mode */
 | 
				
			||||||
 | 
						__CAN_OC_COM=			0b11,					/**< \brief clock output mode */
 | 
				
			||||||
 | 
					}__CAN_OCMODE_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * CAN controller (SJA1000).
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					typedef struct {
 | 
				
			||||||
 | 
						union{uint32_t U;								/**< \brief Unsigned access */
 | 
				
			||||||
 | 
						    struct {
 | 
				
			||||||
 | 
						        unsigned int RM:1; 						/**< \brief MOD.0 Reset Mode */
 | 
				
			||||||
 | 
						        unsigned int LOM:1;            			/**< \brief MOD.1 Listen Only Mode */
 | 
				
			||||||
 | 
						        unsigned int STM:1;                     /**< \brief MOD.2 Self Test Mode */
 | 
				
			||||||
 | 
						        unsigned int AFM:1;                   	/**< \brief MOD.3 Acceptance Filter Mode */
 | 
				
			||||||
 | 
						        unsigned int SM:1;            			/**< \brief MOD.4 Sleep Mode */
 | 
				
			||||||
 | 
						        unsigned int reserved_27:27;            /**< \brief \internal Reserved */
 | 
				
			||||||
 | 
						    } B;
 | 
				
			||||||
 | 
						} MOD;
 | 
				
			||||||
 | 
						union{uint32_t U;								/**< \brief Unsigned access */
 | 
				
			||||||
 | 
						    struct {
 | 
				
			||||||
 | 
						        unsigned int TR:1; 						/**< \brief CMR.0 Transmission Request */
 | 
				
			||||||
 | 
						        unsigned int AT:1;            			/**< \brief CMR.1 Abort Transmission */
 | 
				
			||||||
 | 
						        unsigned int RRB:1;                     /**< \brief CMR.2 Release Receive Buffer */
 | 
				
			||||||
 | 
						        unsigned int CDO:1;                   	/**< \brief CMR.3 Clear Data Overrun */
 | 
				
			||||||
 | 
						        unsigned int GTS:1;            			/**< \brief CMR.4 Go To Sleep */
 | 
				
			||||||
 | 
						        unsigned int reserved_27:27;            /**< \brief \internal Reserved */
 | 
				
			||||||
 | 
						    } B;
 | 
				
			||||||
 | 
						} CMR;
 | 
				
			||||||
 | 
						union{uint32_t U;								/**< \brief Unsigned access */
 | 
				
			||||||
 | 
						    struct {
 | 
				
			||||||
 | 
						        unsigned int RBS:1; 					/**< \brief SR.0 Receive Buffer Status */
 | 
				
			||||||
 | 
						        unsigned int DOS:1;            			/**< \brief SR.1 Data Overrun Status */
 | 
				
			||||||
 | 
						        unsigned int TBS:1;                     /**< \brief SR.2 Transmit Buffer Status */
 | 
				
			||||||
 | 
						        unsigned int TCS:1;                   	/**< \brief SR.3 Transmission Complete Status */
 | 
				
			||||||
 | 
						        unsigned int RS:1;            			/**< \brief SR.4 Receive Status */
 | 
				
			||||||
 | 
						        unsigned int TS:1;            			/**< \brief SR.5 Transmit Status */
 | 
				
			||||||
 | 
						        unsigned int ES:1;            			/**< \brief SR.6 Error Status */
 | 
				
			||||||
 | 
						        unsigned int BS:1;            			/**< \brief SR.7 Bus Status */
 | 
				
			||||||
 | 
						        unsigned int reserved_24:24;            /**< \brief \internal Reserved */
 | 
				
			||||||
 | 
						    } B;
 | 
				
			||||||
 | 
						} SR;
 | 
				
			||||||
 | 
						union{uint32_t U;								/**< \brief Unsigned access */
 | 
				
			||||||
 | 
						    struct {
 | 
				
			||||||
 | 
						        unsigned int RI:1; 						/**< \brief IR.0 Receive Interrupt */
 | 
				
			||||||
 | 
						        unsigned int TI:1;            			/**< \brief IR.1 Transmit Interrupt */
 | 
				
			||||||
 | 
						        unsigned int EI:1;                     	/**< \brief IR.2 Error Interrupt */
 | 
				
			||||||
 | 
						        unsigned int DOI:1;                   	/**< \brief IR.3 Data Overrun Interrupt */
 | 
				
			||||||
 | 
						        unsigned int WUI:1;            			/**< \brief IR.4 Wake-Up Interrupt */
 | 
				
			||||||
 | 
						        unsigned int EPI:1;            			/**< \brief IR.5 Error Passive Interrupt */
 | 
				
			||||||
 | 
						        unsigned int ALI:1;            			/**< \brief IR.6 Arbitration Lost Interrupt */
 | 
				
			||||||
 | 
						        unsigned int BEI:1;            			/**< \brief IR.7 Bus Error Interrupt */
 | 
				
			||||||
 | 
						        unsigned int reserved_24:24;            /**< \brief \internal Reserved */
 | 
				
			||||||
 | 
						    } B;
 | 
				
			||||||
 | 
						} IR;
 | 
				
			||||||
 | 
						union{uint32_t U;								/**< \brief Unsigned access */
 | 
				
			||||||
 | 
						    struct {
 | 
				
			||||||
 | 
						        unsigned int RIE:1; 					/**< \brief IER.0 Receive Interrupt Enable */
 | 
				
			||||||
 | 
						        unsigned int TIE:1;            			/**< \brief IER.1 Transmit Interrupt Enable */
 | 
				
			||||||
 | 
						        unsigned int EIE:1;                     /**< \brief IER.2 Error Interrupt Enable */
 | 
				
			||||||
 | 
						        unsigned int DOIE:1;                   	/**< \brief IER.3 Data Overrun Interrupt Enable */
 | 
				
			||||||
 | 
						        unsigned int WUIE:1;            		/**< \brief IER.4 Wake-Up Interrupt Enable */
 | 
				
			||||||
 | 
						        unsigned int EPIE:1;            		/**< \brief IER.5 Error Passive Interrupt Enable */
 | 
				
			||||||
 | 
						        unsigned int ALIE:1;            		/**< \brief IER.6 Arbitration Lost Interrupt Enable */
 | 
				
			||||||
 | 
						        unsigned int BEIE:1;            		/**< \brief IER.7 Bus Error Interrupt Enable */
 | 
				
			||||||
 | 
						        unsigned int reserved_24:24;            /**< \brief \internal Reserved  */
 | 
				
			||||||
 | 
						    } B;
 | 
				
			||||||
 | 
						} IER;
 | 
				
			||||||
 | 
					    uint32_t RESERVED0;
 | 
				
			||||||
 | 
						union{uint32_t U;								/**< \brief Unsigned access */
 | 
				
			||||||
 | 
						    struct {
 | 
				
			||||||
 | 
						        unsigned int BRP:6; 					/**< \brief BTR0[5:0] Baud Rate Prescaler */
 | 
				
			||||||
 | 
						        unsigned int SJW:2;            			/**< \brief BTR0[7:6] Synchronization Jump Width*/
 | 
				
			||||||
 | 
						        unsigned int reserved_24:24;            /**< \brief \internal Reserved  */
 | 
				
			||||||
 | 
						    } B;
 | 
				
			||||||
 | 
						} BTR0;
 | 
				
			||||||
 | 
						union{uint32_t U;								/**< \brief Unsigned access */
 | 
				
			||||||
 | 
						    struct {
 | 
				
			||||||
 | 
						        unsigned int TSEG1:4; 					/**< \brief BTR1[3:0] Timing Segment 1 */
 | 
				
			||||||
 | 
						        unsigned int TSEG2:3;            		/**< \brief BTR1[6:4] Timing Segment 2*/
 | 
				
			||||||
 | 
						        unsigned int SAM:1;            			/**< \brief BTR1.7 Sampling*/
 | 
				
			||||||
 | 
						        unsigned int reserved_24:24;            /**< \brief \internal Reserved  */
 | 
				
			||||||
 | 
						    } B;
 | 
				
			||||||
 | 
						} BTR1;
 | 
				
			||||||
 | 
						union{uint32_t U;								/**< \brief Unsigned access */
 | 
				
			||||||
 | 
						    struct {
 | 
				
			||||||
 | 
								unsigned int OCMODE:2; 					/**< \brief OCR[1:0] Output Control Mode, see # */
 | 
				
			||||||
 | 
								unsigned int OCPOL0:1;                  /**< \brief OCR.2 Output Control Polarity 0 */
 | 
				
			||||||
 | 
								unsigned int OCTN0:1;                   /**< \brief OCR.3 Output Control Transistor N0 */
 | 
				
			||||||
 | 
								unsigned int OCTP0:1;            		/**< \brief OCR.4 Output Control Transistor P0 */
 | 
				
			||||||
 | 
								unsigned int OCPOL1:1;            		/**< \brief OCR.5 Output Control Polarity 1 */
 | 
				
			||||||
 | 
								unsigned int OCTN1:1;            		/**< \brief OCR.6 Output Control Transistor N1 */
 | 
				
			||||||
 | 
								unsigned int OCTP1:1;            		/**< \brief OCR.7 Output Control Transistor P1 */
 | 
				
			||||||
 | 
								unsigned int reserved_24:24;            /**< \brief \internal Reserved  */
 | 
				
			||||||
 | 
						    } B;
 | 
				
			||||||
 | 
						} OCR;
 | 
				
			||||||
 | 
					    uint32_t RESERVED1[2];
 | 
				
			||||||
 | 
						union{uint32_t U;								/**< \brief Unsigned access */
 | 
				
			||||||
 | 
						    struct {
 | 
				
			||||||
 | 
								unsigned int ALC:8; 					/**< \brief ALC[7:0] Arbitration Lost Capture */
 | 
				
			||||||
 | 
								unsigned int reserved_24:24;            /**< \brief \internal Reserved  */
 | 
				
			||||||
 | 
						    } B;
 | 
				
			||||||
 | 
						} ALC;
 | 
				
			||||||
 | 
						union{uint32_t U;								/**< \brief Unsigned access */
 | 
				
			||||||
 | 
						    struct {
 | 
				
			||||||
 | 
								unsigned int ECC:8; 					/**< \brief ECC[7:0] Error Code Capture */
 | 
				
			||||||
 | 
								unsigned int reserved_24:24;            /**< \brief \internal Reserved  */
 | 
				
			||||||
 | 
						    } B;
 | 
				
			||||||
 | 
						} ECC;
 | 
				
			||||||
 | 
						union{uint32_t U;								/**< \brief Unsigned access */
 | 
				
			||||||
 | 
						    struct {
 | 
				
			||||||
 | 
								unsigned int EWLR:8; 					/**< \brief EWLR[7:0] Error Warning Limit */
 | 
				
			||||||
 | 
								unsigned int reserved_24:24;            /**< \brief \internal Reserved  */
 | 
				
			||||||
 | 
						    } B;
 | 
				
			||||||
 | 
						} EWLR;
 | 
				
			||||||
 | 
						union{uint32_t U;								/**< \brief Unsigned access */
 | 
				
			||||||
 | 
						    struct {
 | 
				
			||||||
 | 
								unsigned int RXERR:8; 					/**< \brief RXERR[7:0] Receive Error Counter */
 | 
				
			||||||
 | 
								unsigned int reserved_24:24;            /**< \brief \internal Reserved  */
 | 
				
			||||||
 | 
						    } B;
 | 
				
			||||||
 | 
						} RXERR;
 | 
				
			||||||
 | 
						union{uint32_t U;								/**< \brief Unsigned access */
 | 
				
			||||||
 | 
						    struct {
 | 
				
			||||||
 | 
								unsigned int TXERR:8; 					/**< \brief TXERR[7:0] Transmit Error Counter */
 | 
				
			||||||
 | 
								unsigned int reserved_24:24;            /**< \brief \internal Reserved  */
 | 
				
			||||||
 | 
						    } B;
 | 
				
			||||||
 | 
						} TXERR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    union {
 | 
				
			||||||
 | 
					        struct {
 | 
				
			||||||
 | 
					            uint32_t CODE[4];						/**< \brief Acceptance Message ID */
 | 
				
			||||||
 | 
					            uint32_t MASK[4];						/**< \brief Acceptance Mask */
 | 
				
			||||||
 | 
					            uint32_t RESERVED2[5];
 | 
				
			||||||
 | 
					        } ACC;										/**< \brief Acceptance filtering */
 | 
				
			||||||
 | 
					        struct {
 | 
				
			||||||
 | 
					        	CAN_FIR_t	FIR;						/**< \brief Frame information record */
 | 
				
			||||||
 | 
					        	union{
 | 
				
			||||||
 | 
									struct {
 | 
				
			||||||
 | 
										uint32_t ID[2];					/**< \brief Standard frame message-ID*/
 | 
				
			||||||
 | 
										uint32_t data[8];				/**< \brief Standard frame payload */
 | 
				
			||||||
 | 
										uint32_t reserved[2];
 | 
				
			||||||
 | 
									} STD;								/**< \brief Standard frame format */
 | 
				
			||||||
 | 
									struct {
 | 
				
			||||||
 | 
										uint32_t ID[4];					/**< \brief Extended frame message-ID*/
 | 
				
			||||||
 | 
										uint32_t data[8];				/**< \brief Extended frame payload */
 | 
				
			||||||
 | 
									} EXT;								/**< \brief Extended frame format */
 | 
				
			||||||
 | 
					        	}TX_RX;									/**< \brief RX/TX interface */
 | 
				
			||||||
 | 
					        }FCTRL;										/**< \brief Function control regs */
 | 
				
			||||||
 | 
					    } MBX_CTRL;										/**< \brief Mailbox control */
 | 
				
			||||||
 | 
						union{uint32_t U;								/**< \brief Unsigned access */
 | 
				
			||||||
 | 
						    struct {
 | 
				
			||||||
 | 
								unsigned int RMC:8; 					/**< \brief RMC[7:0] RX Message Counter */
 | 
				
			||||||
 | 
								unsigned int reserved_24:24;            /**< \brief \internal Reserved Enable */
 | 
				
			||||||
 | 
						    } B;
 | 
				
			||||||
 | 
						} RMC;
 | 
				
			||||||
 | 
						union{uint32_t U;								/**< \brief Unsigned access */
 | 
				
			||||||
 | 
						    struct {
 | 
				
			||||||
 | 
								unsigned int RBSA:8; 					/**< \brief RBSA[7:0] RX Buffer Start Address  */
 | 
				
			||||||
 | 
								unsigned int reserved_24:24;            /**< \brief \internal Reserved Enable */
 | 
				
			||||||
 | 
						    } B;
 | 
				
			||||||
 | 
						} RBSA;
 | 
				
			||||||
 | 
						union{uint32_t U;								/**< \brief Unsigned access */
 | 
				
			||||||
 | 
						    struct {
 | 
				
			||||||
 | 
								unsigned int COD:3; 					/**< \brief CDR[2:0] CLKOUT frequency selector based of fOSC*/
 | 
				
			||||||
 | 
								unsigned int COFF:1; 					/**< \brief CDR.3 CLKOUT off*/
 | 
				
			||||||
 | 
								unsigned int reserved_1:1; 				/**< \brief \internal Reserved */
 | 
				
			||||||
 | 
								unsigned int RXINTEN:1; 				/**< \brief CDR.5 This bit allows the TX1 output to be used as a dedicated receive interrupt output*/
 | 
				
			||||||
 | 
								unsigned int CBP:1; 					/**< \brief CDR.6 allows to bypass the CAN input comparator and is only possible in reset mode.*/
 | 
				
			||||||
 | 
								unsigned int CAN_M:1; 					/**< \brief CDR.7 If CDR.7 is at logic 0 the CAN controller operates in BasicCAN mode. If set to logic 1 the CAN controller operates in PeliCAN mode. Write access is only possible in reset mode*/
 | 
				
			||||||
 | 
								unsigned int reserved_24:24;            /**< \brief \internal Reserved  */
 | 
				
			||||||
 | 
						    } B;
 | 
				
			||||||
 | 
						} CDR;
 | 
				
			||||||
 | 
					    uint32_t IRAM[2];
 | 
				
			||||||
 | 
					}CAN_Module_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* __DRIVERS_CAN_REGDEF_H_ */
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,385 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					NMEA2000_esp32.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Copyright (c) 2015-2020 Timo Lappalainen, Kave Oy, www.kave.fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Permission is hereby granted, free of charge, to any person obtaining a copy of
 | 
				
			||||||
 | 
					this software and associated documentation files (the "Software"), to deal in
 | 
				
			||||||
 | 
					the Software without restriction, including without limitation the rights to use,
 | 
				
			||||||
 | 
					copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
 | 
				
			||||||
 | 
					Software, and to permit persons to whom the Software is furnished to do so,
 | 
				
			||||||
 | 
					subject to the following conditions:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The above copyright notice and this permission notice shall be included in all
 | 
				
			||||||
 | 
					copies or substantial portions of the Software.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 | 
				
			||||||
 | 
					INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 | 
				
			||||||
 | 
					PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 | 
				
			||||||
 | 
					HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 | 
				
			||||||
 | 
					CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
 | 
				
			||||||
 | 
					OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Inherited NMEA2000 object for ESP32 modules. See also NMEA2000 library.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Thanks to Thomas Barth, barth-dev.de, who has written ESP32 CAN code. To avoid extra
 | 
				
			||||||
 | 
					libraries, I implemented his code directly to the NMEA2000_esp32 to avoid extra
 | 
				
			||||||
 | 
					can.h library, which may cause even naming problem.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "driver/periph_ctrl.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "soc/dport_reg.h"
 | 
				
			||||||
 | 
					#include "NMEA2000_esp32.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//time to reinit CAN bus if the queue is full for that time
 | 
				
			||||||
 | 
					#define SEND_CANCEL_TIME 2000 
 | 
				
			||||||
 | 
					//reinit CAN bis if nothing send/received within this time
 | 
				
			||||||
 | 
					#define RECEIVE_REINIT_TIME 60000
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool tNMEA2000_esp32::CanInUse=false;
 | 
				
			||||||
 | 
					tNMEA2000_esp32 *pNMEA2000_esp32=0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ESP32Can1Interrupt(void *);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define ECDEBUG(fmt,args...) if(debugStream){debugStream->printf(fmt, ## args);}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//*****************************************************************************
 | 
				
			||||||
 | 
					tNMEA2000_esp32::tNMEA2000_esp32(gpio_num_t _TxPin,  
 | 
				
			||||||
 | 
					    gpio_num_t _RxPin,
 | 
				
			||||||
 | 
					    Print *dbg) :
 | 
				
			||||||
 | 
					    tNMEA2000(), IsOpen(false),
 | 
				
			||||||
 | 
					    speed(CAN_SPEED_250KBPS), TxPin(_TxPin), RxPin(_RxPin),
 | 
				
			||||||
 | 
					    RxQueue(NULL), TxQueue(NULL) {
 | 
				
			||||||
 | 
					      debugStream=dbg;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					//*****************************************************************************
 | 
				
			||||||
 | 
					bool tNMEA2000_esp32::CANSendFrame(unsigned long id, unsigned char len, const unsigned char *buf, bool /*wait_sent*/) {
 | 
				
			||||||
 | 
					  tCANFrame frame;
 | 
				
			||||||
 | 
					  unsigned long now=millis();
 | 
				
			||||||
 | 
					  if ( uxQueueSpacesAvailable(TxQueue)==0 ) {
 | 
				
			||||||
 | 
					    if (lastSend && (lastSend + SEND_CANCEL_TIME) < now){
 | 
				
			||||||
 | 
					        ECDEBUG("CanSendFrame Aborting and emptying queue\n");
 | 
				
			||||||
 | 
					        while (xQueueReceive(TxQueue,&frame,0)){}
 | 
				
			||||||
 | 
					        errReinit++;
 | 
				
			||||||
 | 
					        CAN_init(false);
 | 
				
			||||||
 | 
					        if ( uxQueueSpacesAvailable(TxQueue)==0 ) return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else{
 | 
				
			||||||
 | 
					      ECDEBUG("CanSendFrame queue full\n");
 | 
				
			||||||
 | 
					      return false; // can not send to queue
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  lastSend=now;
 | 
				
			||||||
 | 
					  frame.id=id;
 | 
				
			||||||
 | 
					  frame.len=len>8?8:len;
 | 
				
			||||||
 | 
					  memcpy(frame.buf,buf,len);
 | 
				
			||||||
 | 
					  CheckBusOff();
 | 
				
			||||||
 | 
					  ECDEBUG("CanSendFrame IntrCnt  %d\n",cntIntr);
 | 
				
			||||||
 | 
					  ECDEBUG("CanSendFrame Error TX/RX %d/%d\n",MODULE_CAN->TXERR.U,MODULE_CAN->RXERR.U);
 | 
				
			||||||
 | 
					  ECDEBUG("CanSendFrame Error Overrun %d\n",errOverrun);
 | 
				
			||||||
 | 
					  ECDEBUG("CanSendFrame Error Arbitration %d\n",errArb);
 | 
				
			||||||
 | 
					  ECDEBUG("CanSendFrame Error Bus %d\n",errBus);
 | 
				
			||||||
 | 
					  ECDEBUG("CanSendFrame Error Recovery %d\n",errRecovery);
 | 
				
			||||||
 | 
					  ECDEBUG("CanSendFrame ErrorCount %d\n",errCountTxInternal);
 | 
				
			||||||
 | 
					  ECDEBUG("CanSendFrame ErrorCancel %d\n",errCancelTransmit);
 | 
				
			||||||
 | 
					  ECDEBUG("CanSendFrame ErrorReinit %d\n",errReinit);
 | 
				
			||||||
 | 
					  ECDEBUG("CanSendFrame busOff=%d, errPassive=%d\n",MODULE_CAN->SR.B.BS,MODULE_CAN->SR.B.ES)
 | 
				
			||||||
 | 
					  xQueueSendToBack(TxQueue,&frame,0);  // Add frame to queue
 | 
				
			||||||
 | 
					  if ( MODULE_CAN->SR.B.TBS==0 ) {
 | 
				
			||||||
 | 
					    ECDEBUG("CanSendFrame: wait for ISR to send %d\n",frame.id);
 | 
				
			||||||
 | 
					    return true; // Currently sending, ISR takes care of sending
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if ( MODULE_CAN->SR.B.TBS==1 ) { // Check again and restart send, if is not going on
 | 
				
			||||||
 | 
					    xQueueReceive(TxQueue,&frame,0);
 | 
				
			||||||
 | 
					    ECDEBUG("CanSendFrame: send direct %d\n",frame.id);
 | 
				
			||||||
 | 
					    CAN_send_frame(frame);
 | 
				
			||||||
 | 
					    //return MODULE_CAN->TXERR.U < 127;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//*****************************************************************************
 | 
				
			||||||
 | 
					void tNMEA2000_esp32::InitCANFrameBuffers() {
 | 
				
			||||||
 | 
					    if (MaxCANReceiveFrames<10 ) MaxCANReceiveFrames=50; // ESP32 has plenty of RAM
 | 
				
			||||||
 | 
					    if (MaxCANSendFrames<10 ) MaxCANSendFrames=40;
 | 
				
			||||||
 | 
					    uint16_t CANGlobalBufSize=MaxCANSendFrames-4;
 | 
				
			||||||
 | 
					    MaxCANSendFrames=4;  // we do not need much libary internal buffer since driver has them.
 | 
				
			||||||
 | 
					    RxQueue=xQueueCreate(MaxCANReceiveFrames,sizeof(tCANFrame));
 | 
				
			||||||
 | 
					    TxQueue=xQueueCreate(CANGlobalBufSize,sizeof(tCANFrame));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    tNMEA2000::InitCANFrameBuffers(); // call main initialization
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//*****************************************************************************
 | 
				
			||||||
 | 
					bool tNMEA2000_esp32::CANOpen() {
 | 
				
			||||||
 | 
					    if (IsOpen) return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (CanInUse) return false; // currently prevent accidental second instance. Maybe possible in future.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pNMEA2000_esp32=this;
 | 
				
			||||||
 | 
					    IsOpen=true;
 | 
				
			||||||
 | 
					    CAN_init();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    CanInUse=IsOpen;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return IsOpen;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//*****************************************************************************
 | 
				
			||||||
 | 
					bool tNMEA2000_esp32::CANGetFrame(unsigned long &id, unsigned char &len, unsigned char *buf) {
 | 
				
			||||||
 | 
					  bool HasFrame=false;
 | 
				
			||||||
 | 
					  tCANFrame frame;
 | 
				
			||||||
 | 
					  CheckBusOff();
 | 
				
			||||||
 | 
					  unsigned long now=millis();
 | 
				
			||||||
 | 
					    //receive next CAN frame from queue
 | 
				
			||||||
 | 
					    if ( xQueueReceive(RxQueue,&frame, 0)==pdTRUE ) {
 | 
				
			||||||
 | 
					      HasFrame=true;
 | 
				
			||||||
 | 
					      id=frame.id;
 | 
				
			||||||
 | 
					      len=frame.len;
 | 
				
			||||||
 | 
					      memcpy(buf,frame.buf,frame.len);
 | 
				
			||||||
 | 
					      lastReceive=now;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else{
 | 
				
			||||||
 | 
					      if (lastReceive != 0 && (lastReceive + RECEIVE_REINIT_TIME) < now && (lastSend + RECEIVE_REINIT_TIME) < now){
 | 
				
			||||||
 | 
					        ECDEBUG("Noting received within %d ms, reinit",RECEIVE_REINIT_TIME);
 | 
				
			||||||
 | 
					        CAN_init(false);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return HasFrame;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//*****************************************************************************
 | 
				
			||||||
 | 
					void tNMEA2000_esp32::CAN_init(bool installIsr) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//Time quantum
 | 
				
			||||||
 | 
						double __tq;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // A soft reset of the ESP32 leaves it's CAN controller in an undefined state so a reset is needed.
 | 
				
			||||||
 | 
					  // Reset CAN controller to same state as it would be in after a power down reset.
 | 
				
			||||||
 | 
					  periph_module_reset(PERIPH_CAN_MODULE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //enable module
 | 
				
			||||||
 | 
					  DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_CAN_CLK_EN);
 | 
				
			||||||
 | 
					  DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_CAN_RST);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //configure RX pin
 | 
				
			||||||
 | 
						gpio_set_direction(RxPin,GPIO_MODE_INPUT);
 | 
				
			||||||
 | 
						gpio_matrix_in(RxPin,CAN_RX_IDX,0);
 | 
				
			||||||
 | 
						gpio_pad_select_gpio(RxPin);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //set to PELICAN mode
 | 
				
			||||||
 | 
						MODULE_CAN->CDR.B.CAN_M=0x1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//synchronization jump width is the same for all baud rates
 | 
				
			||||||
 | 
						MODULE_CAN->BTR0.B.SJW		=0x1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//TSEG2 is the same for all baud rates
 | 
				
			||||||
 | 
						MODULE_CAN->BTR1.B.TSEG2	=0x1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//select time quantum and set TSEG1
 | 
				
			||||||
 | 
						switch (speed) {
 | 
				
			||||||
 | 
							case CAN_SPEED_1000KBPS:
 | 
				
			||||||
 | 
								MODULE_CAN->BTR1.B.TSEG1	=0x4;
 | 
				
			||||||
 | 
								__tq = 0.125;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							case CAN_SPEED_800KBPS:
 | 
				
			||||||
 | 
								MODULE_CAN->BTR1.B.TSEG1	=0x6;
 | 
				
			||||||
 | 
								__tq = 0.125;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								MODULE_CAN->BTR1.B.TSEG1	=0xc;
 | 
				
			||||||
 | 
								__tq = ((float)1000/speed) / 16;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//set baud rate prescaler
 | 
				
			||||||
 | 
						MODULE_CAN->BTR0.B.BRP=(uint8_t)round((((APB_CLK_FREQ * __tq) / 2) - 1)/1000000)-1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Set sampling
 | 
				
			||||||
 | 
					     * 1 -> triple; the bus is sampled three times; recommended for low/medium speed buses     (class A and B) where filtering spikes on the bus line is beneficial
 | 
				
			||||||
 | 
					     * 0 -> single; the bus is sampled once; recommended for high speed buses (SAE class C)*/
 | 
				
			||||||
 | 
					    MODULE_CAN->BTR1.B.SAM	=0x1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //enable all interrupts
 | 
				
			||||||
 | 
					    MODULE_CAN->IER.U = 0xef;  // bit 0x10 contains Baud Rate Prescaler Divider (BRP_DIV) bit
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //no acceptance filtering, as we want to fetch all messages
 | 
				
			||||||
 | 
					    MODULE_CAN->MBX_CTRL.ACC.CODE[0] = 0;
 | 
				
			||||||
 | 
					    MODULE_CAN->MBX_CTRL.ACC.CODE[1] = 0;
 | 
				
			||||||
 | 
					    MODULE_CAN->MBX_CTRL.ACC.CODE[2] = 0;
 | 
				
			||||||
 | 
					    MODULE_CAN->MBX_CTRL.ACC.CODE[3] = 0;
 | 
				
			||||||
 | 
					    MODULE_CAN->MBX_CTRL.ACC.MASK[0] = 0xff;
 | 
				
			||||||
 | 
					    MODULE_CAN->MBX_CTRL.ACC.MASK[1] = 0xff;
 | 
				
			||||||
 | 
					    MODULE_CAN->MBX_CTRL.ACC.MASK[2] = 0xff;
 | 
				
			||||||
 | 
					    MODULE_CAN->MBX_CTRL.ACC.MASK[3] = 0xff;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //set to normal mode
 | 
				
			||||||
 | 
					    MODULE_CAN->OCR.B.OCMODE=__CAN_OC_NOM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //clear error counters
 | 
				
			||||||
 | 
					    MODULE_CAN->TXERR.U = 0;
 | 
				
			||||||
 | 
					    MODULE_CAN->RXERR.U = 0;
 | 
				
			||||||
 | 
					    (void)MODULE_CAN->ECC;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //clear interrupt flags
 | 
				
			||||||
 | 
					    (void)MODULE_CAN->IR.U;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //install CAN ISR
 | 
				
			||||||
 | 
					    if (installIsr) esp_intr_alloc(ETS_CAN_INTR_SOURCE,0,ESP32Can1Interrupt,NULL,NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //configure TX pin
 | 
				
			||||||
 | 
					    // We do late configure, since some initialization above caused CAN Tx flash
 | 
				
			||||||
 | 
					    // shortly causing one error frame on startup. By setting CAN pin here
 | 
				
			||||||
 | 
					    // it works right.
 | 
				
			||||||
 | 
					    gpio_set_direction(TxPin,GPIO_MODE_OUTPUT);
 | 
				
			||||||
 | 
					    gpio_matrix_out(TxPin,CAN_TX_IDX,0,0);
 | 
				
			||||||
 | 
					    gpio_pad_select_gpio(TxPin);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //Showtime. Release Reset Mode.
 | 
				
			||||||
 | 
					    MODULE_CAN->MOD.B.RM = 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//*****************************************************************************
 | 
				
			||||||
 | 
					void tNMEA2000_esp32::CAN_read_frame() {
 | 
				
			||||||
 | 
						tCANFrame frame;
 | 
				
			||||||
 | 
					  CAN_FIR_t FIR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//get FIR
 | 
				
			||||||
 | 
						FIR.U=MODULE_CAN->MBX_CTRL.FCTRL.FIR.U;
 | 
				
			||||||
 | 
					  frame.len=FIR.B.DLC>8?8:FIR.B.DLC;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Handle only extended frames
 | 
				
			||||||
 | 
					  if (FIR.B.FF==CAN_frame_ext) {  //extended frame
 | 
				
			||||||
 | 
					    //Get Message ID
 | 
				
			||||||
 | 
					    frame.id = _CAN_GET_EXT_ID;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //deep copy data bytes
 | 
				
			||||||
 | 
					    for( size_t i=0; i<frame.len; i++ ) {
 | 
				
			||||||
 | 
					      frame.buf[i]=MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.data[i];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //send frame to input queue
 | 
				
			||||||
 | 
					    xQueueSendToBackFromISR(RxQueue,&frame,0);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  //Let the hardware know the frame has been read.
 | 
				
			||||||
 | 
					  MODULE_CAN->CMR.B.RRB=1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//*****************************************************************************
 | 
				
			||||||
 | 
					void tNMEA2000_esp32::CAN_send_frame(tCANFrame &frame) {
 | 
				
			||||||
 | 
					  CAN_FIR_t FIR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  FIR.U=0;
 | 
				
			||||||
 | 
					  FIR.B.DLC=frame.len>8?8:frame.len;
 | 
				
			||||||
 | 
					  FIR.B.FF=CAN_frame_ext;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//copy frame information record
 | 
				
			||||||
 | 
						MODULE_CAN->MBX_CTRL.FCTRL.FIR.U=FIR.U;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  //Write message ID
 | 
				
			||||||
 | 
					  _CAN_SET_EXT_ID(frame.id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Copy the frame data to the hardware
 | 
				
			||||||
 | 
					  for ( size_t i=0; i<frame.len; i++) {
 | 
				
			||||||
 | 
					    MODULE_CAN->MBX_CTRL.FCTRL.TX_RX.EXT.data[i]=frame.buf[i];
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Transmit frame
 | 
				
			||||||
 | 
					  MODULE_CAN->CMR.B.TR=1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#define CAN_MAX_TX_RETRY 12
 | 
				
			||||||
 | 
					#define RECOVERY_RETRY_MS 1000
 | 
				
			||||||
 | 
					void tNMEA2000_esp32::CAN_bus_off_recovery(){
 | 
				
			||||||
 | 
					  unsigned long now=millis();
 | 
				
			||||||
 | 
					  if (recoveryStarted && (recoveryStarted + RECOVERY_RETRY_MS) > now ) return;
 | 
				
			||||||
 | 
					  ECDEBUG("CAN_bus_off_recovery started\n");
 | 
				
			||||||
 | 
					  recoveryStarted=now;
 | 
				
			||||||
 | 
					  errRecovery++;
 | 
				
			||||||
 | 
					  MODULE_CAN->CMR.B.AT=1; // abort transmission
 | 
				
			||||||
 | 
					  (void)MODULE_CAN->SR.U;
 | 
				
			||||||
 | 
					  MODULE_CAN->TXERR.U = 127; 
 | 
				
			||||||
 | 
					  MODULE_CAN->RXERR.U = 0; 
 | 
				
			||||||
 | 
					  MODULE_CAN->MOD.B.RM = 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void tNMEA2000_esp32::CheckBusOff(){
 | 
				
			||||||
 | 
					  //should we really recover here?
 | 
				
			||||||
 | 
					    if (MODULE_CAN->SR.B.BS){
 | 
				
			||||||
 | 
					        ECDEBUG("Bus off detected, trying recovery\n");      
 | 
				
			||||||
 | 
					        CAN_bus_off_recovery();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//*****************************************************************************
 | 
				
			||||||
 | 
					void tNMEA2000_esp32::InterruptHandler() {
 | 
				
			||||||
 | 
						//Interrupt flag buffer
 | 
				
			||||||
 | 
					  cntIntr++;
 | 
				
			||||||
 | 
						uint32_t interrupt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Read interrupt status and clear flags
 | 
				
			||||||
 | 
					    interrupt = (MODULE_CAN->IR.U & 0xff);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Handle TX complete interrupt
 | 
				
			||||||
 | 
					    //see http://uglyduck.vajn.icu/PDF/wireless/Espressif/ESP32/Eco_and_Workarounds_for_Bugs_in_ESP32.pdf, 3.13.4
 | 
				
			||||||
 | 
					    if ((interrupt & __CAN_IRQ_TX) != 0 || MODULE_CAN->SR.B.TBS) {  
 | 
				
			||||||
 | 
					      tCANFrame frame;
 | 
				
			||||||
 | 
					      if ( (xQueueReceiveFromISR(TxQueue,&frame,NULL)==pdTRUE) ) {
 | 
				
			||||||
 | 
					        CAN_send_frame(frame);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Handle RX frame available interrupt
 | 
				
			||||||
 | 
					    if ((interrupt & __CAN_IRQ_RX) != 0) {
 | 
				
			||||||
 | 
					    	CAN_read_frame();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Handle error interrupts.
 | 
				
			||||||
 | 
					    if ((interrupt & (__CAN_IRQ_ERR						//0x4
 | 
				
			||||||
 | 
					                     
 | 
				
			||||||
 | 
					                      | __CAN_IRQ_WAKEUP				//0x10
 | 
				
			||||||
 | 
					                      | __CAN_IRQ_ERR_PASSIVE			//0x20
 | 
				
			||||||
 | 
					                      
 | 
				
			||||||
 | 
					                     
 | 
				
			||||||
 | 
					        )) != 0) {
 | 
				
			||||||
 | 
					    	/*handler*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    //https://www.esp32.com/viewtopic.php?t=5010
 | 
				
			||||||
 | 
					    // Handle error interrupts.
 | 
				
			||||||
 | 
					    if (interrupt & __CAN_IRQ_DATA_OVERRUN ) {                  //0x08
 | 
				
			||||||
 | 
					        errOverrun++;
 | 
				
			||||||
 | 
					        MODULE_CAN->CMR.B.CDO=1;
 | 
				
			||||||
 | 
					        (void)MODULE_CAN->SR.U; // read SR after write to CMR to settle register changes
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (interrupt & __CAN_IRQ_ARB_LOST ) {                      //0x40
 | 
				
			||||||
 | 
					        errArb++;
 | 
				
			||||||
 | 
					        (void)MODULE_CAN->ALC.U; // must be read to re-enable interrupt
 | 
				
			||||||
 | 
					        errCountTxInternal++;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (interrupt & __CAN_IRQ_BUS_ERR ) {                       //0x80
 | 
				
			||||||
 | 
					        errBus++;
 | 
				
			||||||
 | 
					        (void)MODULE_CAN->ECC.U; // must be read to re-enable interrupt
 | 
				
			||||||
 | 
					        errCountTxInternal+=2;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (MODULE_CAN->TXERR.U == 0){
 | 
				
			||||||
 | 
					      recoveryStarted=0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (errCountTxInternal >= 2 *CAN_MAX_TX_RETRY){
 | 
				
			||||||
 | 
					        MODULE_CAN->CMR.B.AT=1; // abort transmission
 | 
				
			||||||
 | 
					        (void)MODULE_CAN->SR.U;
 | 
				
			||||||
 | 
					        errCountTxInternal=0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//*****************************************************************************
 | 
				
			||||||
 | 
					void ESP32Can1Interrupt(void *) {
 | 
				
			||||||
 | 
					  pNMEA2000_esp32->InterruptHandler();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,106 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					NMEA2000_esp32.h
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Copyright (c) 2015-2020 Timo Lappalainen, Kave Oy, www.kave.fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Permission is hereby granted, free of charge, to any person obtaining a copy of
 | 
				
			||||||
 | 
					this software and associated documentation files (the "Software"), to deal in
 | 
				
			||||||
 | 
					the Software without restriction, including without limitation the rights to use,
 | 
				
			||||||
 | 
					copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
 | 
				
			||||||
 | 
					Software, and to permit persons to whom the Software is furnished to do so,
 | 
				
			||||||
 | 
					subject to the following conditions:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The above copyright notice and this permission notice shall be included in all
 | 
				
			||||||
 | 
					copies or substantial portions of the Software.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 | 
				
			||||||
 | 
					INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 | 
				
			||||||
 | 
					PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 | 
				
			||||||
 | 
					HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 | 
				
			||||||
 | 
					CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
 | 
				
			||||||
 | 
					OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Inherited NMEA2000 object for ESP32 modules. See also NMEA2000 library.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Thanks to Thomas Barth, barth-dev.de, who has written ESP32 CAN code. To avoid extra
 | 
				
			||||||
 | 
					libraries, I implemented his code directly to the NMEA2000_esp32 to avoid extra
 | 
				
			||||||
 | 
					can.h library, which may cause even naming problem. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The library sets as default CAN Tx pin to GPIO 16 and CAN Rx pint to GPIO 4. If you
 | 
				
			||||||
 | 
					want to use other pins (I have not tested can any pins be used), add defines e.g.
 | 
				
			||||||
 | 
					#define ESP32_CAN_TX_PIN GPIO_NUM_34
 | 
				
			||||||
 | 
					#define ESP32_CAN_RX_PIN GPIO_NUM_35
 | 
				
			||||||
 | 
					before including NMEA2000_esp32.h or NMEA2000_CAN.h 
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef _NMEA2000_ESP32_H_
 | 
				
			||||||
 | 
					#define _NMEA2000_ESP32_H_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "freertos/FreeRTOS.h"
 | 
				
			||||||
 | 
					#include "freertos/queue.h"
 | 
				
			||||||
 | 
					#include "driver/gpio.h"
 | 
				
			||||||
 | 
					#include "NMEA2000.h"
 | 
				
			||||||
 | 
					#include "N2kMsg.h"
 | 
				
			||||||
 | 
					#include "ESP32_CAN_def.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef ESP32_CAN_TX_PIN
 | 
				
			||||||
 | 
					#define ESP32_CAN_TX_PIN GPIO_NUM_16
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#ifndef ESP32_CAN_RX_PIN
 | 
				
			||||||
 | 
					#define ESP32_CAN_RX_PIN GPIO_NUM_4
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class tNMEA2000_esp32 : public tNMEA2000
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					  bool IsOpen;
 | 
				
			||||||
 | 
					  static bool CanInUse;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
					  struct tCANFrame {
 | 
				
			||||||
 | 
					    uint32_t id; // can identifier
 | 
				
			||||||
 | 
					    uint8_t len; // length of data
 | 
				
			||||||
 | 
					    uint8_t buf[8];
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
						CAN_speed_t    speed;	
 | 
				
			||||||
 | 
					  gpio_num_t     TxPin;	
 | 
				
			||||||
 | 
					  gpio_num_t     RxPin;
 | 
				
			||||||
 | 
					  QueueHandle_t  RxQueue;
 | 
				
			||||||
 | 
					  QueueHandle_t  TxQueue;
 | 
				
			||||||
 | 
					  Print           *debugStream;
 | 
				
			||||||
 | 
					  int             errOverrun=0;
 | 
				
			||||||
 | 
					  int             errArb=0;
 | 
				
			||||||
 | 
					  int             errBus=0;
 | 
				
			||||||
 | 
					  int             errRecovery=0;
 | 
				
			||||||
 | 
					  int             errCountTxInternal=0;
 | 
				
			||||||
 | 
					  int             errCancelTransmit=0;
 | 
				
			||||||
 | 
					  int             errReinit=0;
 | 
				
			||||||
 | 
					  unsigned long   recoveryStarted=0;
 | 
				
			||||||
 | 
					  unsigned long   lastSend=0;
 | 
				
			||||||
 | 
					  unsigned long   lastReceive=0;
 | 
				
			||||||
 | 
					  int             cntIntr=0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
					  void CAN_read_frame(); // Read frame to queue within interrupt
 | 
				
			||||||
 | 
					  void CAN_send_frame(tCANFrame &frame); // Send frame
 | 
				
			||||||
 | 
					  void CAN_init(bool installIsr=true);
 | 
				
			||||||
 | 
					  void CAN_bus_off_recovery(); //recover from bus off
 | 
				
			||||||
 | 
					  void CheckBusOff();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
					  bool CANSendFrame(unsigned long id, unsigned char len, const unsigned char *buf, bool wait_sent=true);
 | 
				
			||||||
 | 
					  bool CANOpen();
 | 
				
			||||||
 | 
					  bool CANGetFrame(unsigned long &id, unsigned char &len, unsigned char *buf);
 | 
				
			||||||
 | 
					  virtual void InitCANFrameBuffers();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					  tNMEA2000_esp32(gpio_num_t _TxPin=ESP32_CAN_TX_PIN,  
 | 
				
			||||||
 | 
					    gpio_num_t _RxPin=ESP32_CAN_RX_PIN,
 | 
				
			||||||
 | 
					    Print *debugStream=NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void InterruptHandler();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					@ -21,7 +21,7 @@ platform = espressif32
 | 
				
			||||||
framework = arduino
 | 
					framework = arduino
 | 
				
			||||||
lib_deps = 
 | 
					lib_deps = 
 | 
				
			||||||
	ttlappalainen/NMEA2000-library @ ^4.17.2
 | 
						ttlappalainen/NMEA2000-library @ ^4.17.2
 | 
				
			||||||
	ttlappalainen/NMEA2000_esp32 @ ^1.0.3
 | 
						#ttlappalainen/NMEA2000_esp32 @ ^1.0.3
 | 
				
			||||||
	ttlappalainen/NMEA0183 @ ^1.7.1
 | 
						ttlappalainen/NMEA0183 @ ^1.7.1
 | 
				
			||||||
	bblanchon/ArduinoJson@^6.18.5
 | 
						bblanchon/ArduinoJson@^6.18.5
 | 
				
			||||||
	ottowinter/ESPAsyncWebServer-esphome@^2.0.1
 | 
						ottowinter/ESPAsyncWebServer-esphome@^2.0.1
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										24
									
								
								src/main.cpp
								
								
								
								
							
							
						
						
									
										24
									
								
								src/main.cpp
								
								
								
								
							| 
						 | 
					@ -14,6 +14,7 @@
 | 
				
			||||||
#include "GwAppInfo.h"
 | 
					#include "GwAppInfo.h"
 | 
				
			||||||
// #define GW_MESSAGE_DEBUG_ENABLED
 | 
					// #define GW_MESSAGE_DEBUG_ENABLED
 | 
				
			||||||
//#define FALLBACK_SERIAL
 | 
					//#define FALLBACK_SERIAL
 | 
				
			||||||
 | 
					//#define CAN_ESP_DEBUG
 | 
				
			||||||
const unsigned long HEAP_REPORT_TIME=2000; //set to 0 to disable heap reporting
 | 
					const unsigned long HEAP_REPORT_TIME=2000; //set to 0 to disable heap reporting
 | 
				
			||||||
#include <Arduino.h>
 | 
					#include <Arduino.h>
 | 
				
			||||||
#include "GwApi.h"
 | 
					#include "GwApi.h"
 | 
				
			||||||
| 
						 | 
					@ -27,7 +28,6 @@ const unsigned long HEAP_REPORT_TIME=2000; //set to 0 to disable heap reporting
 | 
				
			||||||
#define N2K_CERTIFICATION_LEVEL 0xff
 | 
					#define N2K_CERTIFICATION_LEVEL 0xff
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <NMEA2000_CAN.h>  // This will automatically choose right CAN library and create suitable NMEA2000 object
 | 
					 | 
				
			||||||
#include <ActisenseReader.h>
 | 
					#include <ActisenseReader.h>
 | 
				
			||||||
#include <Seasmart.h>
 | 
					#include <Seasmart.h>
 | 
				
			||||||
#include <N2kMessages.h>
 | 
					#include <N2kMessages.h>
 | 
				
			||||||
| 
						 | 
					@ -66,6 +66,17 @@ const unsigned long HEAP_REPORT_TIME=2000; //set to 0 to disable heap reporting
 | 
				
			||||||
#include "GwChannel.h"
 | 
					#include "GwChannel.h"
 | 
				
			||||||
#include "GwChannelList.h"
 | 
					#include "GwChannelList.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <NMEA2000_esp32.h>       // forked from https://github.com/ttlappalainen/NMEA2000_esp32
 | 
				
			||||||
 | 
					#ifdef FALLBACK_SERIAL
 | 
				
			||||||
 | 
					  #ifdef CAN_ESP_DEBUG
 | 
				
			||||||
 | 
					    #define CDBS &Serial
 | 
				
			||||||
 | 
					  #else
 | 
				
			||||||
 | 
					    #define CDBS NULL
 | 
				
			||||||
 | 
					  #endif
 | 
				
			||||||
 | 
					  tNMEA2000 &NMEA2000=*(new tNMEA2000_esp32(ESP32_CAN_TX_PIN,ESP32_CAN_RX_PIN,CDBS));
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					  tNMEA2000 &NMEA2000=*(new tNMEA2000_esp32());
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define MAX_NMEA2000_MESSAGE_SEASMART_SIZE 500
 | 
					#define MAX_NMEA2000_MESSAGE_SEASMART_SIZE 500
 | 
				
			||||||
| 
						 | 
					@ -212,7 +223,12 @@ void handleN2kMessage(const tN2kMsg &n2kMsg,int sourceId, bool isConverted=false
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  if (sourceId != N2K_CHANNEL_ID && sendOutN2k){
 | 
					  if (sourceId != N2K_CHANNEL_ID && sendOutN2k){
 | 
				
			||||||
    countNMEA2KOut.add(n2kMsg.PGN);
 | 
					    countNMEA2KOut.add(n2kMsg.PGN);
 | 
				
			||||||
    NMEA2000.SendMsg(n2kMsg);
 | 
					    if (NMEA2000.SendMsg(n2kMsg)){
 | 
				
			||||||
 | 
					      countNMEA2KOut.add(n2kMsg.PGN);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else{
 | 
				
			||||||
 | 
					      countNMEA2KOut.addFail(n2kMsg.PGN);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -622,8 +638,8 @@ void setup() {
 | 
				
			||||||
#ifdef FALLBACK_SERIAL
 | 
					#ifdef FALLBACK_SERIAL
 | 
				
			||||||
  fallbackSerial=true;
 | 
					  fallbackSerial=true;
 | 
				
			||||||
    //falling back to old style serial for logging
 | 
					    //falling back to old style serial for logging
 | 
				
			||||||
    Serial.begin(baud);
 | 
					    Serial.begin(115200);
 | 
				
			||||||
    Serial.printf("fallback serial enabled, error was %d\n",st);
 | 
					    Serial.printf("fallback serial enabled\n");
 | 
				
			||||||
    logger.prefix="FALLBACK:";
 | 
					    logger.prefix="FALLBACK:";
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
  userCodeHandler.startInitTasks(MIN_USER_TASK);
 | 
					  userCodeHandler.startInitTasks(MIN_USER_TASK);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue