From e7ae67e64b835c1e6aff0b6126943235ad8c2c5f Mon Sep 17 00:00:00 2001 From: patacongo Date: Thu, 5 Jan 2012 20:39:56 +0000 Subject: [PATCH] Add logic to control CAN bit rate via the .config file git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@4266 42af7a65-404d-4744-a932-0658087f49c3 --- arch/arm/src/lpc17xx/lpc17_can.c | 173 ++++++++++++++++++++++++++- arch/arm/src/lpc17xx/lpc17_lowputc.c | 6 +- 2 files changed, 171 insertions(+), 8 deletions(-) diff --git a/arch/arm/src/lpc17xx/lpc17_can.c b/arch/arm/src/lpc17xx/lpc17_can.c index 00c00d918d..fd04c82693 100755 --- a/arch/arm/src/lpc17xx/lpc17_can.c +++ b/arch/arm/src/lpc17xx/lpc17_can.c @@ -96,6 +96,12 @@ # define canllvdbg(x...) #endif +/* Timing *******************************************************************/ +/* CAN clocking is provided at CCLK/4 (hardcoded in lpc17_caninitialize()) */ + +#define CAN_CCLK_DIVISOR 4 +#define CAN_CLOCK_FREQUENCY (LPC17_CCLK / CAN_CCLK_DIVISOR) + /**************************************************************************** * Private Types ****************************************************************************/ @@ -139,9 +145,15 @@ static int can_remoterequest(FAR struct can_dev_s *dev, uint16_t id); static int can_send(FAR struct can_dev_s *dev, FAR struct can_msg_s *msg); static bool can_txempty(FAR struct can_dev_s *dev); +/* CAN interrupts */ + static void can_interrupt(FAR struct can_dev_s *dev); static int can12_interrupt(int irq, void *context); +/* Initialization */ + +static int can_bittiming(struct up_dev_s *priv); + /**************************************************************************** * Private Data ****************************************************************************/ @@ -400,19 +412,30 @@ static void can_reset(FAR struct can_dev_s *dev) { FAR struct up_dev_s *priv = (FAR struct up_dev_s *)dev->cd_priv; uint32_t baud; + int ret; irqstate_t flags; canvdbg("CAN%d\n", priv->port); -#warning "BTR setting must be calculated from priv->baud" - baud = 0x25c003; flags = irqsave(); + /* Disable the CAN and stop ongong transmissions */ + can_putreg(priv, LPC17_CAN_MOD_OFFSET, CAN_MOD_RM); /* Enter Reset Mode */ can_putreg(priv, LPC17_CAN_IER_OFFSET, 0); /* Disable interrupts */ can_putreg(priv, LPC17_CAN_GSR_OFFSET, 0); /* Clear status bits */ can_putreg(priv, LPC17_CAN_CMR_OFFSET, CAN_CMR_AT); /* Abort transmission */ - can_putreg(priv, LPC17_CAN_BTR_OFFSET, baud); /* Set bit timing */ + + /* Set bit timing */ + + ret = can_bittiming(priv); + if (ret != OK) + { + candbg("ERROR: Failed to set bit timing: %d\n", ret); + } + + /* Restart the CAN */ + #ifdef CONFIG_CAN_LOOPBACK can_putreg(priv, LPC17_CAN_MOD_OFFSET, CAN_MOD_STM); /* Leave Reset Mode, enter Test Mode */ #else @@ -873,6 +896,146 @@ static int can12_interrupt(int irq, void *context) return OK; } +/**************************************************************************** + * Name: can_bittiming + * + * Description: + * Set the CAN bit timing register (BTR) based on the configured BAUD. + * + * The bit timing logic monitors the serial bus-line and performs sampling + * and adjustment of the sample point by synchronizing on the start-bit edge + * and resynchronizing on the following edges. + * + * Its operation may be explained simply by splitting nominal bit time into + * three segments as follows: + * + * 1. Synchronization segment (SYNC_SEG): a bit change is expected to occur + * within this time segment. It has a fixed length of one time quantum + * (1 x tCAN). + * 2. Bit segment 1 (BS1): defines the location of the sample point. It + * includes the PROP_SEG and PHASE_SEG1 of the CAN standard. Its duration + * is programmable between 1 and 16 time quanta but may be automatically + * lengthened to compensate for positive phase drifts due to differences + * in the frequency of the various nodes of the network. + * 3. Bit segment 2 (BS2): defines the location of the transmit point. It + * represents the PHASE_SEG2 of the CAN standard. Its duration is + * programmable between 1 and 8 time quanta but may also be automatically + * shortened to compensate for negative phase drifts. + * + * Pictorially: + * + * |<----------------- NOMINAL BIT TIME ----------------->| + * |<- SYNC_SEG ->|<------ BS1 ------>|<------ BS2 ------>| + * |<---- Tq ---->|<----- Tbs1 ------>|<----- Tbs2 ------>| + * + * Where + * Tbs1 is the duration of the BS1 segment + * Tbs2 is the duration of the BS2 segment + * Tq is the "Time Quantum" + * + * Relationships: + * + * baud = 1 / bit_time + * bit_time = Tq + Tbs1 + Tbs2 + * Tbs1 = Tq * ts1 + * Tbs2 = Tq * ts2 + * Tq = brp * Tpclk1 + * + * Where: + * Tpclk1 is the period of the APB clock (PCLK = CCLK / 4). + * + * Input Parameter: + * priv - A reference to the CAN block status + * + * Returned Value: + * Zero on success; a negated errno on failure + * + ****************************************************************************/ + +static int can_bittiming(struct up_dev_s *priv) +{ + uint32_t canbtr; + uint32_t brp; + uint32_t ts1; + uint32_t ts2; + uint32_t sjw; + + canllvdbg("CAN%d PCLK1: %d baud: %d\n", priv->port, CAN_CLOCK_FREQUENCY, priv->baud); + + /* Try to get 14 quanta in one bit_time. That is based on the idea that the ideal + * would be ts1=6 nd ts2=7 and (1 + ts1 + ts2) = 14. + * + * bit_time = Tq*(1 +ts1 + ts2) + * nquanta = bit_time/Tq + * nquanta = (1 +ts1 + ts2) + * + * bit_time = brp * Tpclk1 * (1 + ts1 + ts2) + * nquanta = bit_time / brp / Tpclk1 + * = PCLK1 / baud / brp + * brp = PCLK1 / baud / nquanta; + * + * Example: + * PCLK1 = 42,000,000 baud = 1,000,000 nquanta = 14 : brp = 3 + * PCLK1 = 42,000,000 baud = 700,000 nquanta = 14 : brp = 4 + */ + + canbtr = CAN_CLOCK_FREQUENCY / priv->baud; + if (canbtr < 14) + { + /* At the smallest brp value (1), there are already fewer bit times + * (CAN_CLOCK / baud) is already smaller than our goal. brp must be one + * and we need make some reasonalble guesses about ts1 and ts2. + */ + + brp = 1; + + /* In this case, we have to guess a good value for ts1 and ts2 */ + + ts1 = (canbtr - 1) >> 1; + ts2 = canbtr - ts1 - 1; + if (ts1 == ts2 && ts1 > 1 && ts2 < 16) + { + ts1--; + ts2++; + } + } + + /* Otherwise, nquanta is 14, ts1 is 6, ts2 is 7 and we calculate brp to + * achieve 14 quanta in the bit time + */ + + else + { + ts1 = 6; + ts2 = 7; + brp = (canbtr + 7) / 14; + DEBUGASSERT(brp >=1 && brp < 1024); + } + + sjw = 1; + + canllvdbg("TS1: %d TS2: %d BRP: %d SJW= %d\n", ts1, ts2, brp, sjw); + + /* Configure bit timing */ + + canbtr = ((brp - 1) << CAN_BTR_BRP_SHIFT) | + ((ts1 - 1) << CAN_BTR_TESG1_SHIFT) | + ((ts2 - 1) << CAN_BTR_TESG2_SHIFT) | + ((sjw - 1) << CAN_BTR_SJW_SHIFT); + +#ifdef CONFIG_CAN_SAM + /* The bus is sampled 3 times (recommended for low to medium speed buses + * to spikes on the bus-line). + */ + + canbtr |= CAN_BTR_SAM; +#endif + + canllvdbg("Setting CANxBTR= 0x%08x\n", canbtr); + can_putreg(priv, LPC17_CAN_BTR_OFFSET, canbtr); /* Set bit timing */ + return OK; +} + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -910,7 +1073,7 @@ FAR struct can_dev_s *lpc17_caninitialize(int port) can_putcommon(LPC17_SYSCON_PCONP, regval); /* Enable clocking to the CAN module (not necessary... already done - * in low level clock configuration logic. + * in low level clock configuration logic). */ regval = can_getcommon(LPC17_SYSCON_PCLKSEL0); @@ -937,7 +1100,7 @@ FAR struct can_dev_s *lpc17_caninitialize(int port) can_putcommon(LPC17_SYSCON_PCONP, regval); /* Enable clocking to the CAN module (not necessary... already done - * in low level clock configuration logic. + * in low level clock configuration logic). */ regval = can_getcommon(LPC17_SYSCON_PCLKSEL0); diff --git a/arch/arm/src/lpc17xx/lpc17_lowputc.c b/arch/arm/src/lpc17xx/lpc17_lowputc.c index 0bd265635d..1ea68287bc 100755 --- a/arch/arm/src/lpc17xx/lpc17_lowputc.c +++ b/arch/arm/src/lpc17xx/lpc17_lowputc.c @@ -1,8 +1,8 @@ /************************************************************************** * arch/arm/src/lpc17xx/lpc17_lowputc.c * - * Copyright (C) 2010-2011 Gregory Nutt. All rights reserved. - * Author: Gregory Nutt + * Copyright (C) 2010-2012 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -130,7 +130,7 @@ #define CONSOLE_FCR_VALUE (UART_FCR_RXTRIGGER_8 | UART_FCR_TXRST |\ UART_FCR_RXRST | UART_FCR_FIFOEN) -/* Select a CCLK divider to produce the UART PCLK. The stratey is to select the +/* Select a CCLK divider to produce the UART PCLK. The strategy is to select the * smallest divisor that results in an solution within range of the 16-bit * DLM and DLL divisor: *