X-Git-Url: http://rtime.felk.cvut.cz/gitweb/lincan.git/blobdiff_plain/ad1d46af5c8562e90a3f8564cf511213ff1f1745..e372a22696a079ea7f3bb00aa46e8a8281f1c49f:/embedded/app/usbcan/lpc17xx_can.c diff --git a/embedded/app/usbcan/lpc17xx_can.c b/embedded/app/usbcan/lpc17xx_can.c index eee3718..0d42658 100644 --- a/embedded/app/usbcan/lpc17xx_can.c +++ b/embedded/app/usbcan/lpc17xx_can.c @@ -1,9 +1,434 @@ #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_bittiming_const *btc; + 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; + + btc = &chip_data->btc; + + // set bittiming constants + 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 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_lmc1_chip_data *chip_data = (struct can_lmc1_chip_data*) chip->chip_data; + + struct can_bittiming_const *btc = &chip_data->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++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_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; + + 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(){ @@ -17,21 +442,21 @@ 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; @@ -40,172 +465,165 @@ static void CAN_configPin(){ 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; - - // write second 4 data bytes + msg->data[i] = (data>>(i*8)) & 0xFF; - 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; + + can_write_reg(chip, can_tfi1, CAN_TFI1_o); - // sync. segment allways 1 tq - tq_numb--; - // number of tq from the sample point to the next nominal Sync. point (this value plus one) - TSEG2 = (tq_numb/3) - 1; + // write CAN ID + can_write_reg(chip, msg->id, CAN_TID1_o); - // number of tq from Sync. point to the sample point (this value plus one) - TSEG1 = tq_numb - (tq_numb/3) - 1; - break; - } - } + // write first 4 data bytes + data=0; + for(i=0; i<4; i++) + data |= (msg->data[i])<<(i*8); - CAN1BTR = ((SAM<<23)|(TSEG2<<20)|(TSEG1<<16)|(SJW<<14)|(BRP<<0)); + 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); + }