#include "can/lpc17xx_can.h"
static void CAN_configPin();
-static void CAN_setBusTiming(uint32_t baudrate);
-//------------------------------------------
+
+// board can-lmc1 specific functions:
+
+int can_lmc1_register(struct hwspecops_t *hwspecops){
+
+ hwspecops->request_io = can_lmc1_request_io;
+ hwspecops->reset = can_lmc1_reset;
+ hwspecops->init_hw_data = can_lmc1_init_hw_data;
+ hwspecops->init_chip_data = can_lmc1_init_chip_data;
+ hwspecops->init_obj_data = can_lmc1_init_obj_data;
+ hwspecops->write_register = can_lmc1_write_register;
+ hwspecops->read_register = can_lmc1_read_register;
+ hwspecops->program_irq = can_lmc1_program_irq;
+
+ return 0;
+}
+
+int can_lmc1_init_hw_data(struct candevice_t *candev){
+
+ candev->res_addr=0;
+ candev->nr_82527_chips=0;
+ candev->nr_sja1000_chips=0;
+ candev->nr_all_chips=1;
+ candev->flags = 0;
+
+ return 0;
+}
+
+int can_lmc1_init_chip_data(struct candevice_t *candev, int chipnr){
+
+ struct can_lmc1_chip_data *chip_data;
+
+ // used CAN1 peripherial -> CAN1 registers base
+ candev->chip[chipnr]->chip_base_addr = CAN1_REGS_BASE;
+ // clock for CAN
+ candev->chip[chipnr]->clock = system_frequency / 4;
+
+ lpc17xx_fill_chipspecops(candev->chip[chipnr]);
+
+ candev->chip[chipnr]->chip_data=(void *)malloc(sizeof(struct can_lmc1_chip_data));
+ if (candev->chip[chipnr]->chip_data==NULL)
+ return -ENOMEM;
+
+
+ chip_data = (struct can_lmc1_chip_data*) candev->chip[chipnr]->chip_data;
+ chip_data->flags = 0;
+
+ return 0;
+}
+
+int can_lmc1_init_obj_data(struct canchip_t *chip, int objnr){
+
+ return 0;
+}
+
+void can_lmc1_write_register(unsigned data, unsigned long address){
+ (*(volatile uint32_t*)(address)) = data;
+}
+
+unsigned can_lmc1_read_register(unsigned long address){
+ return (*(volatile uint32_t*)(address));
+}
+
+int can_lmc1_request_io(struct candevice_t *candev)
+{
+ return 0;
+}
+
+int can_lmc1_reset(struct candevice_t *candev)
+{
+ return 0;
+}
+
+int can_lmc1_program_irq(struct candevice_t *candev)
+{
+ return 0;
+}
+
+//---------------------------------------------------------------------------------
+//---------------------------------------------------------------------------------
+
+
+// lpc17xx can chip specific functions:
+
+/*
+ * lpc17xx_baud_rate: - set communication parameters.
+ * @chip: pointer to chip state structure
+ * @rate: baud rate in Hz
+ * @clock: frequency of CAN clock in Hz
+ * @sjw: synchronization jump width (0-3) prescaled clock cycles
+ * @sampl_pt: sample point in % (0-100) sets (TSEG1+1)/(TSEG1+TSEG2+2) ratio
+ */
+int lpc17xx_baud_rate(struct canchip_t *chip, int rate, int clock, int sjw,
+ int sampl_pt, int flags)
+{
+ int best_error = 1000000000, error;
+ int best_tseg=0, best_brp=0, best_rate=0, brp=0;
+ int tseg=0, tseg1=0, tseg2=0;
+
+ struct can_bittiming_const btc_buff;
+ struct can_bittiming_const *btc = &btc_buff;
+
+ chip->chipspecops->get_bittiming_const(chip, btc);
+
+ /* tseg even = round down, odd = round up */
+ for (tseg=(0+0+2)*2; tseg<=(btc->tseg2_max+btc->tseg1_max+2)*2+1; tseg++) {
+ brp = clock/((1+tseg/2)*rate)+tseg%2;
+ if (brp == 0 || brp > 64)
+ continue;
+ error = rate - clock/(brp*(1+tseg/2));
+ if (error < 0)
+ error = -error;
+ if (error <= best_error) {
+ best_error = error;
+ best_tseg = tseg/2;
+ best_brp = brp-1;
+ best_rate = clock/(brp*(1+tseg/2));
+ }
+ }
+ if (best_error && (rate/best_error < 10)) {
+ CANMSG("baud rate %d is not possible with %d Hz clock\n",
+ rate, clock);
+ CANMSG("%d bps. brp=%d, best_tseg=%d, tseg1=%d, tseg2=%d\n",
+ best_rate, best_brp, best_tseg, tseg1, tseg2);
+ return -EINVAL;
+ }
+ tseg2 = best_tseg-(sampl_pt*(best_tseg+1))/100;
+ if (tseg2 < 0)
+ tseg2 = 0;
+ if (tseg2 > btc->tseg2_max)
+ tseg2 = btc->tseg2_max;
+ tseg1 = best_tseg-tseg2-2;
+ if (tseg1>btc->tseg1_max) {
+ tseg1 = btc->tseg1_max;
+ tseg2 = best_tseg-tseg1-2;
+ }
+
+ DEBUGMSG("Setting %d bps.\n", best_rate);
+ DEBUGMSG("brp=%d, best_tseg=%d, tseg1=%d, tseg2=%d, sampl_pt=%d\n",
+ best_brp, best_tseg, tseg1, tseg2,
+ (100*(best_tseg-tseg2)/(best_tseg+1)));
+
+
+ if(chip->chipspecops->set_bittiming(chip, ++best_brp, ++sjw, ++tseg1, ++tseg2) < 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+
+
+int lpc17xx_chip_config(struct canchip_t *chip){
+
+ CAN_init(chip);
+
+ if (!chip->baudrate)
+ chip->baudrate=1000000;
+
+ if (lpc17xx_baud_rate(chip,chip->baudrate,chip->clock,0,75,0))
+ return -ENODEV;
+
+ return 0;
+}
+
+int lpc17xx_pre_write_config(struct canchip_t *chip, struct msgobj_t *obj,
+ struct canmsg_t *msg)
+{
+ uint32_t i=0;
+
+ // check status of TB1
+ while (!(can_read_reg(chip, CAN_SR_o) & CAN_SR_TBS1)){
+ if(i++<MAX_TRANSMIT_WAIT_LOOPS)
+ continue;
+
+ // request command to abort transmission request
+ can_write_reg(chip, CAN_CMR_AT, CAN_CMR_o);
+ return 0;
+ }
+
+ CAN_send(chip, msg);
+
+ return 0;
+}
+
+int lpc17xx_send_msg(struct canchip_t *chip, struct msgobj_t *obj,
+ struct canmsg_t *msg)
+{
+ uint32_t i=0;
+
+ // write transmission request
+ can_write_reg(chip, (CAN_CMR_TR | CAN_CMR_STB1), CAN_CMR_o);
+
+ // check transmission complete status
+ while (!(can_read_reg(chip, CAN_SR_o) & CAN_SR_TCS1)){
+ if(i++<MAX_TRANSMIT_WAIT_LOOPS)
+ continue;
+
+ CANMSG("Abort transmission request\n");
+
+ // request command to abort transmission request
+ can_write_reg(chip, CAN_CMR_AT, CAN_CMR_o);
+ break;
+ }
+
+ return 0;
+}
+
+int lpc17xx_wakeup_tx(struct canchip_t *chip, struct msgobj_t *obj)
+{
+
+ can_preempt_disable();
+
+ can_msgobj_set_fl(obj,TX_PENDING);
+ can_msgobj_set_fl(obj,TX_REQUEST);
+
+ while(!can_msgobj_test_and_set_fl(obj,TX_LOCK)){
+ can_msgobj_clear_fl(obj,TX_REQUEST);
+
+ if (can_read_reg(chip, CAN_SR_o) & CAN_SR_TBS1){
+ obj->tx_retry_cnt=0;
+ lpc17xx_irq_write_handler(chip, obj);
+ }
+
+ can_msgobj_clear_fl(obj,TX_LOCK);
+ if(!can_msgobj_test_fl(obj,TX_REQUEST)) break;
+
+ }
+
+ can_preempt_enable();
+
+ return 0;
+}
+
+int lpc17xx_irq_handler(int irq, struct canchip_t *chip)
+{
+
+ uint32_t i;
+ struct msgobj_t *obj;
+ obj = chip->msgobj[0];
+
+ i = can_read_reg(chip, CAN_ICR_o);
+
+ if(i & (CAN_ICR_TI1 | CAN_ICR_RI)){
+
+ if(can_read_reg(chip, CAN_SR_o) & CAN_SR_RBS) {
+ lpc17xx_read(chip,obj);
+ obj->ret = 0;
+ }
+
+
+ if ((can_msgobj_test_fl(obj,TX_PENDING)) || (can_msgobj_test_fl(obj,TX_REQUEST))) {
+
+ can_msgobj_set_fl(obj,TX_REQUEST);
+
+ while(!can_msgobj_test_and_set_fl(obj,TX_LOCK)){
+
+ obj->ret=0;
+ can_msgobj_clear_fl(obj,TX_REQUEST);
+
+ if (can_read_reg(chip, CAN_SR_o) & CAN_SR_TBS1){
+ obj->tx_retry_cnt=0;
+ lpc17xx_irq_write_handler(chip, obj);
+ }
+
+ can_msgobj_clear_fl(obj,TX_LOCK);
+ if(!can_msgobj_test_fl(obj,TX_REQUEST)) break;
+
+ }
+ }
+
+ }
+ if(i & CAN_ICR_DOI)
+ can_write_reg(chip, CAN_CMR_CDO, CAN_CMR_o); // clear data overrun
+
+
+ return CANCHIP_IRQ_HANDLED;
+
+}
+
+void lpc17xx_irq_write_handler(struct canchip_t *chip, struct msgobj_t *obj)
+{
+ int cmd;
+
+
+ if(obj->tx_slot){
+ /* Do local transmitted message distribution if enabled */
+ if (processlocal){
+ /* fill CAN message timestamp */
+ can_filltimestamp(&obj->tx_slot->msg.timestamp);
+
+ obj->tx_slot->msg.flags |= MSG_LOCAL;
+ canque_filter_msg2edges(obj->qends, &obj->tx_slot->msg);
+ }
+ /* Free transmitted slot */
+ canque_free_outslot(obj->qends, obj->tx_qedge, obj->tx_slot);
+ obj->tx_slot=NULL;
+ }
+
+ can_msgobj_clear_fl(obj,TX_PENDING);
+ cmd=canque_test_outslot(obj->qends, &obj->tx_qedge, &obj->tx_slot);
+ if(cmd<0)
+ return;
+ can_msgobj_set_fl(obj,TX_PENDING);
+
+ if (chip->chipspecops->pre_write_config(chip, obj, &obj->tx_slot->msg)) {
+ obj->ret = -1;
+ canque_notify_inends(obj->tx_qedge, CANQUEUE_NOTIFY_ERRTX_PREP);
+ canque_free_outslot(obj->qends, obj->tx_qedge, obj->tx_slot);
+ obj->tx_slot=NULL;
+ return;
+ }
+ if (chip->chipspecops->send_msg(chip, obj, &obj->tx_slot->msg)) {
+ obj->ret = -1;
+ canque_notify_inends(obj->tx_qedge, CANQUEUE_NOTIFY_ERRTX_SEND);
+ canque_free_outslot(obj->qends, obj->tx_qedge, obj->tx_slot);
+ obj->tx_slot=NULL;
+ return;
+ }
+
+}
+
+void lpc17xx_read(struct canchip_t *chip, struct msgobj_t *obj) {
+
+
+ CAN_recv(chip, &obj->rx_msg);
+
+ // fill CAN message timestamp
+ can_filltimestamp(&obj->rx_msg.timestamp);
+
+ canque_filter_msg2edges(obj->qends, &obj->rx_msg);
+
+ // release Receive buffer
+ can_write_reg(chip, CAN_CMR_RRB, CAN_CMR_o);
+
+}
+
+int lpc17xx_set_bittiming(struct canchip_t *chip, int brp, int sjw, int tseg1, int tseg2){
+
+ uint8_t SAM = 0; // 0 = the bus is sampled once
+
+ if((--brp)<0)
+ return -EINVAL;
+
+ if((--sjw)<0)
+ return -EINVAL;
+
+ if((--tseg1)<0)
+ return -EINVAL;
+
+ if((--tseg2)<0)
+ return -EINVAL;
+
+
+ //debug print
+ CANMSG("BRP: %d, SJW: %d, TSEG1: %d, TSEG2: %d \n", brp+1, sjw+1, tseg1+1, tseg2+1);
+
+ can_disable_irq(chip->chip_irq);
+
+ // enter reset mode
+ can_write_reg(chip, 1, CAN_MOD_o);
+
+ // set bittiming register
+ can_write_reg(chip, ((SAM<<23)|(tseg2<<20)|(tseg1<<16)|(sjw<<14)|(brp<<0)), CAN_BTR_o);
+
+ // return to normal operating
+ can_write_reg(chip, 0, CAN_MOD_o);
+
+ can_enable_irq(chip->chip_irq);
+
+ return 0;
+}
+
+int lpc17xx_get_bittiming_const(struct canchip_t *chip, struct can_bittiming_const *btc) {
+ btc->tseg1_min = 1;
+ btc->tseg1_max = 16;
+ btc->tseg2_min = 1;
+ btc->tseg2_max = 8;
+ btc->sjw_max = 4;
+ btc->brp_min = 1;
+ btc->brp_max = 1024;
+ btc->brp_inc = 1;
+
+ return 0;
+}
+
+
+int lpc17xx_fill_chipspecops(struct canchip_t *chip){
+
+ chip->max_objects=1;
+ chip->chip_irq = CAN_IRQn;
+
+ lpc17xx_register(chip->chipspecops);
+
+ return 0;
+}
+
+int lpc17xx_register(struct chipspecops_t *chipspecops){
+
+ CANMSG("initializing lpc17xx can chip operations\n");
+
+ chipspecops->attach_to_chip = lpc17xx_attach_to_chip;
+ chipspecops->pre_read_config = lpc17xx_pre_read_config;
+ chipspecops->chip_config = lpc17xx_chip_config;
+ chipspecops->pre_write_config = lpc17xx_pre_write_config;
+ chipspecops->send_msg = lpc17xx_send_msg;
+ chipspecops->wakeup_tx = lpc17xx_wakeup_tx;
+ chipspecops->irq_handler = lpc17xx_irq_handler;
+
+ chipspecops->set_bittiming = lpc17xx_set_bittiming;
+ chipspecops->get_bittiming_const = lpc17xx_get_bittiming_const;
+
+ return 0;
+
+}
+
+
+int lpc17xx_attach_to_chip(struct canchip_t *chip){
+
+ return 0;
+}
+
+int lpc17xx_pre_read_config(struct canchip_t *chip, struct msgobj_t *obj){
+
+ return 1;
+}
+
+
+
+//---------------------------------------------------------------------------------
+//---------------------------------------------------------------------------------
+
static void CAN_configPin(){
uint8_t pinmode_od0_val = 0;
- pinsel0 = PINSEL0;
+ pinsel0 = PINCON->PINSEL0;
pinsel0 &= ~CAN1_RX_MASK;
pinsel0 &= ~CAN1_TX_MASK;
pinsel0 |= __val2mfld(CAN1_RX_MASK, pinsel0_val);
pinsel0 |= __val2mfld(CAN1_TX_MASK, pinsel0_val);
- PINSEL0 = pinsel0;
+ PINCON->PINSEL0 = pinsel0;
- pinmode0 = PINMODE0;
+ pinmode0 = PINCON->PINMODE0;
pinmode0 &= ~CAN1_RX_MASK;
pinmode0 &= ~CAN1_TX_MASK;
pinmode0 |= __val2mfld(CAN1_RX_MASK, pinmode0_val);
pinmode0 |= __val2mfld(CAN1_TX_MASK, pinmode0_val);
- PINMODE0 = pinmode0;
+ PINCON->PINMODE0 = pinmode0;
- pinmode_od0 = PINMODE_OD0;
+ pinmode_od0 = PINCON->PINMODE_OD0;
if (pinmode_od0_val){
pinmode_od0 |= CAN1_RX_BIT;
pinmode_od0 |= CAN1_TX_BIT;
pinmode_od0 &= ~CAN1_RX_BIT;
pinmode_od0 &= ~CAN1_TX_BIT;
}
- PINMODE_OD0 = pinmode_od0;
+ PINCON->PINMODE_OD0 = pinmode_od0;
}
-void CAN_send(canmsg_t* msg){
+void CAN_recv(struct canchip_t *chip, canmsg_t* msg){
- uint32_t data;
+ volatile uint32_t data;
uint32_t i;
- // check status of TB1
- while (!(CAN1SR & (1<<2))){}
-
- CAN1TFI1 &= ~0x000F0000;
- CAN1TFI1 |= (msg->length)<<16;
+ // read data lenght
+ msg->length = (can_read_reg(chip, CAN_RFS_o)>>16) & 0xF;
+
+ // read identifier
+ msg->id = can_read_reg(chip, CAN_RID_o);
// EXT frame
- if(msg->flags & MSG_EXT)
- CAN1TFI1 |= (1<<31);
+ if(can_read_reg(chip, CAN_RFS_o) & CAN_RFS_EXT)
+ msg->flags |= MSG_EXT;
else
- CAN1TFI1 &= ~(1<<31);
-
+ msg->flags &= ~MSG_EXT;
+
+
// RTR frame
- if(msg->flags & MSG_RTR)
- CAN1TFI1 |= (1<<30);
+ if(can_read_reg(chip, CAN_RFS_o) & CAN_RFS_RTR)
+ msg->flags |= MSG_RTR;
else
- CAN1TFI1 &= ~(1<<30);
+ msg->flags &= ~MSG_RTR;
- // write CAN ID
- CAN1TID1 = msg->id;
-
- // write first 4 data bytes
- data=0;
+ // read data
+ data = can_read_reg(chip, CAN_RDA_o);
for(i=0; i<4; i++)
- data |= (msg->data[i])<<(i*8);
-
- CAN1TDA1 = data;
+ msg->data[i] = (data>>(i*8)) & 0xFF;
- // write second 4 data bytes
-
- data=0;
+ data = can_read_reg(chip, CAN_RDB_o);
for(i=4; i<8; i++)
- data |= (msg->data[i])<<((i-4)*8);
- CAN1TDB1 = data;
+ msg->data[i] = (data>>((i-4)*8)) & 0xFF;
- // write transmission request
- CAN1CMR = 0x21;
-
-}
-
-void CAN_setBusTiming(uint32_t baudrate){
-
- uint32_t PCLK_CAN;
- uint32_t res;
-
- uint8_t tq_numb; // number of time quantum
- uint8_t TSEG1, TSEG2;
- uint8_t BRP;
- uint8_t SJW;
- uint8_t SAM;
- uint8_t div;
-
- // 0 = the bus is sampled once
- SAM = 0;
-
- // the Synchronization Jump Width (this value plus one)
- SJW = 3;
-
- // get clock divide for CAN1
- div = __mfld2val(PCLK_CAN1_MASK, PCLKSEL0);
- switch(div){
- case 0:
- div = 4;
- break;
- case 1:
- div = 1;
- break;
- case 2:
- div = 2;
- break;
- case 3:
- // only for CAN, for other peripherials '11' value means div=8
- div = 6;
- break;
- }
+}
- PCLK_CAN = system_frequency / div;
+void CAN_send(struct canchip_t *chip, canmsg_t* msg){
- res = PCLK_CAN / baudrate;
+ volatile uint32_t data;
+ volatile uint32_t can_tfi1;
+ uint32_t i=0;
+ can_tfi1 = can_read_reg(chip, CAN_TFI1_o);
- // calculation of tq_numb - number of time quantum (must be in <8,25>)
- // tq_numb = TSEG1 + TSEG2 + 3
+ can_tfi1 &= ~0x000F0000;
+ can_tfi1 |= (msg->length)<<16;
- for(tq_numb=25; tq_numb>=8; tq_numb--){
-
- if ((res%tq_numb)==0){
+ // EXT frame
+ if(msg->flags & MSG_EXT)
+ can_tfi1 |= CAN_TFI1_EXT;
+ else
+ can_tfi1 &= ~CAN_TFI1_EXT;
- // Baud Rate Prescaler. The PCLK clock is divided by (this value plus one) to produce the CAN clock.
- BRP = (res / tq_numb) - 1;
+ // RTR frame
+ if(msg->flags & MSG_RTR)
+ can_tfi1 |= CAN_TFI1_RTR;
+ else
+ can_tfi1 &= ~CAN_TFI1_RTR;
- // sync. segment allways 1 tq
- tq_numb--;
+ can_write_reg(chip, can_tfi1, CAN_TFI1_o);
- // number of tq from the sample point to the next nominal Sync. point (this value plus one)
- TSEG2 = (tq_numb/3) - 1;
- // number of tq from Sync. point to the sample point (this value plus one)
- TSEG1 = tq_numb - (tq_numb/3) - 1;
+ // write CAN ID
+ can_write_reg(chip, msg->id, CAN_TID1_o);
- break;
- }
- }
- CAN1BTR = ((SAM<<23)|(TSEG2<<20)|(TSEG1<<16)|(SJW<<14)|(BRP<<0));
+ // write first 4 data bytes
+ data=0;
+ for(i=0; i<4; i++)
+ data |= (msg->data[i])<<(i*8);
+
+ can_write_reg(chip, data, CAN_TDA1_o);
+
+ // write second 4 data bytes
+ data=0;
+ for(i=4; i<8; i++)
+ data |= (msg->data[i])<<((i-4)*8);
+
+ can_write_reg(chip, data, CAN_TDB1_o);
}
-void CAN_init(uint32_t baudrate)
-{
+void CAN_init(struct canchip_t *chip){
+
uint32_t tmp;
uint32_t pclksel0;
uint32_t val;
-
+ uint32_t i;
// configure CAN1 pins
CAN_configPin();
// turn on power and clock for CAN1
- PCONP |= PCCAN1;
+ SC->PCONP |= PCCAN1;
// set clock divide for CAN1
val = 0x00; // 00 PCLK_peripheral = CCLK/4
- pclksel0 = PCLKSEL0;
+ pclksel0 = SC->PCLKSEL0;
pclksel0 &= ~PCLK_CAN1_MASK;
pclksel0 &= ~PCLK_CAN2_MASK;
pclksel0 &= ~PCLK_ACF_MASK;
pclksel0 |= __val2mfld(PCLK_CAN1_MASK, val);
pclksel0 |= __val2mfld(PCLK_CAN2_MASK, val);
pclksel0 |= __val2mfld(PCLK_ACF_MASK, val);
- PCLKSEL0 = pclksel0;
+ SC->PCLKSEL0 = pclksel0;
// enter reset mode
- CAN1MOD = 1;
+ can_write_reg(chip, 1, CAN_MOD_o);
// disable all CAN interrupts
- CAN1IER = 0;
+ can_write_reg(chip, 0, CAN_IER_o);
// reset value of Global Status Register (global controller status and error counters)
- CAN1GSR = 0x3C;
+ can_write_reg(chip, 0x3C, CAN_GSR_o);
// request command to release Rx, Tx buffer and clear data overrun
- CAN1CMR = (1<<1)|(1<<2)|(1<<3);
+ can_write_reg(chip, (CAN_CMR_AT | CAN_CMR_RRB | CAN_CMR_CDO), CAN_CMR_o);
// read to clear interrupt pending in Interrupt Capture Register
- tmp = CAN1ICR;
-
- // set bus timing
- CAN_setBusTiming(baudrate);
+ tmp = can_read_reg(chip, CAN_ICR_o);
// return to normal operating
- CAN1MOD = 0;
+ can_write_reg(chip, 0, CAN_MOD_o);
+
+
+ //--------------------------
+
+ // Acceptance Filter Off Mode
+ CANAF_AFMR = 0x01;
+ // clear RAM masks
+ for (i = 0; i < 512; i++) {
+ CANAF_RAM->mask[i] = 0x00;
+ }
+
+ CANAF_SFF_sa = 0x00;
+ CANAF_SFF_GRP_sa = 0x00;
+ CANAF_EFF_sa = 0x00;
+ CANAF_EFF_GRP_sa = 0x00;
+ CANAF_ENDofTable = 0x00;
+
+ // Acceptance Filter Bypass Mode - all messages accepted
+ CANAF_AFMR = 0x02;
+
+ //--------------------------
+
+
+ // enable interrupt after transmit
+ // enable receive interrupt
+ // enable data overrun interrupt
+ can_write_reg(chip, (CAN_IER_TIE1 | CAN_IER_RIE | CAN_IER_DOIE), CAN_IER_o);
+
}