]> rtime.felk.cvut.cz Git - lincan.git/blobdiff - embedded/app/usbcan/lpc17xx_can.c
Some bugs related with transmission/reception fixed.
[lincan.git] / embedded / app / usbcan / lpc17xx_can.c
index 5565d8c346ada07eb00a7c1b06421b9b8b66094e..0d42658c16414049463e6df5265d64934ea25103 100644 (file)
@@ -1,56 +1,6 @@
 #include "can/lpc17xx_can.h"
 
 static void CAN_configPin();
-static void CAN_setBusTiming(struct canchip_t *chip);
-
-extern struct canhardware_t *hardware_p;
-
-//---------------------------------------------------------------------------------
-//---------------------------------------------------------------------------------
-
-
-static inline void can_write_register(struct canchip_t *chip, uint32_t data, uint32_t reg_offs){
-
-       uint32_t address = chip->chip_base_addr + reg_offs;
-       (*(volatile uint32_t*)(address)) = data;
-
-}
-
-static inline uint32_t can_read_register(struct canchip_t *chip, uint32_t reg_offs){
-
-       uint32_t address = chip->chip_base_addr + reg_offs;
-       return (*(volatile uint32_t*)(address));
-
-}
-
-//---------------------------------------------------------------------------------
-//---------------------------------------------------------------------------------
-
-
-// interrupt handler
-// for transmitting - irq when one message was transmitted (for check if another message is pending in can msg queue)
-// for receiving - irq when message was received and is available in Receive buffer
-
-void CAN_IRQHandler(){
-
-       uint32_t i;
-
-       struct canchip_t *chip;
-       chip = hardware_p->candevice[0]->chip[0];
-
-       i = can_read_register(chip, CAN_ICR_o);
-
-       if(i & (CAN_ICR_TI1 | CAN_ICR_RI))
-               lpc17xx_irq_handler(0, chip);
-
-       if(i & CAN_ICR_DOI)
-               can_write_register(chip, CAN_CMR_CDO, CAN_CMR_o);
-       
-       
-}
-
-//---------------------------------------------------------------------------------
-//---------------------------------------------------------------------------------
 
 
 // board can-lmc1 specific functions:
@@ -64,6 +14,7 @@ int can_lmc1_register(struct hwspecops_t *hwspecops){
        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;
 }
@@ -74,24 +25,42 @@ int can_lmc1_init_hw_data(struct candevice_t *candev){
        candev->nr_82527_chips=0;
        candev->nr_sja1000_chips=0;
        candev->nr_all_chips=1;
-       candev->flags |= 0;
+       candev->flags = 0;
 
        return 0;
 }
 
 int can_lmc1_init_chip_data(struct candevice_t *candev, int chipnr){
-       
-       lpc17xx_fill_chipspecops(candev->chip[chipnr]);
 
-       candev->chip[chipnr]->flags|= CHIP_IRQ_CUSTOM;
+       struct can_bittiming_const *btc;
+       struct can_lmc1_chip_data *chip_data;
 
-       //debug
+       // 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;
 }
 
@@ -101,15 +70,11 @@ int can_lmc1_init_obj_data(struct canchip_t *chip, int objnr){
 }
 
 void can_lmc1_write_register(unsigned data, unsigned long address){
-
-       printf("UNSUPPORTED NOW\n");
+       (*(volatile uint32_t*)(address)) = data;
 }
 
 unsigned can_lmc1_read_register(unsigned long address){
-
-       printf("UNSUPPORTED NOW\n");
-       return 0;
-
+       return (*(volatile uint32_t*)(address));
 }
 
 int can_lmc1_request_io(struct candevice_t *candev)
@@ -122,6 +87,10 @@ int can_lmc1_reset(struct candevice_t *candev)
        return 0;
 }
 
+int can_lmc1_program_irq(struct candevice_t *candev)
+{
+       return 0;
+}
 
 //---------------------------------------------------------------------------------
 //---------------------------------------------------------------------------------
@@ -129,18 +98,100 @@ int can_lmc1_reset(struct candevice_t *candev)
 
 // 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++<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;
@@ -149,9 +200,22 @@ int lpc17xx_pre_write_config(struct canchip_t *chip, struct msgobj_t *obj,
 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_register(chip, (CAN_CMR_TR | CAN_CMR_STB1), CAN_CMR_o); 
+       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;
 }
@@ -167,7 +231,7 @@ int lpc17xx_wakeup_tx(struct canchip_t *chip, struct msgobj_t *obj)
        while(!can_msgobj_test_and_set_fl(obj,TX_LOCK)){
                can_msgobj_clear_fl(obj,TX_REQUEST);
 
-               if (can_read_register(chip, CAN_SR_o) & CAN_SR_TBS1){
+               if (can_read_reg(chip, CAN_SR_o) & CAN_SR_TBS1){
                        obj->tx_retry_cnt=0;
                        lpc17xx_irq_write_handler(chip, obj);
                }
@@ -185,34 +249,43 @@ int lpc17xx_wakeup_tx(struct canchip_t *chip, struct msgobj_t *obj)
 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(can_read_register(chip, CAN_SR_o) & CAN_SR_RBS) {
-                       lpc17xx_read(chip,obj);
-                       obj->ret = 0;
-       }
+       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))) {
+               if ((can_msgobj_test_fl(obj,TX_PENDING)) || (can_msgobj_test_fl(obj,TX_REQUEST))) {
                        
-               can_msgobj_set_fl(obj,TX_REQUEST);
+                       can_msgobj_set_fl(obj,TX_REQUEST);
 
-               while(!can_msgobj_test_and_set_fl(obj,TX_LOCK)){
+                       while(!can_msgobj_test_and_set_fl(obj,TX_LOCK)){
 
-                       obj->ret=0;
-                       can_msgobj_clear_fl(obj,TX_REQUEST);
+                               obj->ret=0;
+                               can_msgobj_clear_fl(obj,TX_REQUEST);
 
-                       if (can_read_register(chip, CAN_SR_o) & CAN_SR_TBS1){
-                               obj->tx_retry_cnt=0;
-                               lpc17xx_irq_write_handler(chip, obj);
-                       }
+                               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_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;
@@ -223,6 +296,7 @@ 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){
@@ -271,13 +345,50 @@ void lpc17xx_read(struct canchip_t *chip, struct msgobj_t *obj) {
                canque_filter_msg2edges(obj->qends, &obj->rx_msg);
 
                // release Receive buffer
-               can_write_register(chip, CAN_CMR_RRB, CAN_CMR_o);
+               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);
 
@@ -296,6 +407,8 @@ int lpc17xx_register(struct chipspecops_t *chipspecops){
        chipspecops->wakeup_tx = lpc17xx_wakeup_tx;
        chipspecops->irq_handler = lpc17xx_irq_handler;
 
+       chipspecops->set_bittiming = lpc17xx_set_bittiming;
+
        return 0;       
 
 }
@@ -363,46 +476,44 @@ void CAN_recv(struct canchip_t *chip, canmsg_t* msg){
        uint32_t i;
 
        // read data lenght
-       msg->length = (can_read_register(chip, CAN_RFS_o)>>16) & 0xF;
+       msg->length = (can_read_reg(chip, CAN_RFS_o)>>16) & 0xF;
 
        // read identifier
-       msg->id = can_read_register(chip, CAN_RID_o);
+       msg->id = can_read_reg(chip, CAN_RID_o);
 
        // EXT frame
-       if(can_read_register(chip, CAN_RFS_o) & CAN_RFS_EXT)
+       if(can_read_reg(chip, CAN_RFS_o) & CAN_RFS_EXT)
                msg->flags |= MSG_EXT;
        else
                msg->flags &= ~MSG_EXT;
 
        
        // RTR frame
-       if(can_read_register(chip, CAN_RFS_o) & CAN_RFS_RTR)
+       if(can_read_reg(chip, CAN_RFS_o) & CAN_RFS_RTR)
                msg->flags |= MSG_RTR;
        else
                msg->flags &= ~MSG_RTR;
 
 
        // read data            
-       data = can_read_register(chip, CAN_RDA_o);              
+       data = can_read_reg(chip, CAN_RDA_o);           
        for(i=0; i<4; i++)
                msg->data[i] = (data>>(i*8)) & 0xFF;
 
-       data = can_read_register(chip, CAN_RDB_o);      
+       data = can_read_reg(chip, CAN_RDB_o);   
        for(i=4; i<8; i++)
                msg->data[i] = (data>>((i-4)*8)) & 0xFF;
 
 }
 
+
 void CAN_send(struct canchip_t *chip, canmsg_t* msg){
 
        volatile uint32_t data;
        volatile uint32_t can_tfi1;
-       uint32_t i;
-
-       // check status of TB1
-       while (!(can_read_register(chip, CAN_SR_o) & CAN_SR_TBS1)){}    
+       uint32_t i=0;
 
-       can_tfi1 = can_read_register(chip, CAN_TFI1_o);
+       can_tfi1 = can_read_reg(chip, CAN_TFI1_o);
 
        can_tfi1 &= ~0x000F0000;
        can_tfi1 |= (msg->length)<<16;
@@ -419,11 +530,11 @@ void CAN_send(struct canchip_t *chip, canmsg_t* msg){
        else
                can_tfi1 &= ~CAN_TFI1_RTR;
 
-       can_write_register(chip, can_tfi1, CAN_TFI1_o);
+       can_write_reg(chip, can_tfi1, CAN_TFI1_o);
 
 
        // write CAN ID
-       can_write_register(chip, msg->id, CAN_TID1_o);
+       can_write_reg(chip, msg->id, CAN_TID1_o);
 
 
        // write first 4 data bytes
@@ -431,83 +542,14 @@ void CAN_send(struct canchip_t *chip, canmsg_t* msg){
        for(i=0; i<4; i++)
                data |= (msg->data[i])<<(i*8);
 
-       can_write_register(chip, data, CAN_TDA1_o);
+       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_register(chip, data, CAN_TDB1_o);
-
-}
-
-void CAN_setBusTiming(struct canchip_t *chip){
-
-       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, SC->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;
-
-       res = PCLK_CAN / (chip->baudrate);
-
-
-       // calculation of tq_numb - number of time quantum (must be in <8,25>)
-       // tq_numb = TSEG1 + TSEG2 + 3
-
-       for(tq_numb=25; tq_numb>=8; tq_numb--){
-               
-               if ((res%tq_numb)==0){
-               
-                       // Baud Rate Prescaler. The PCLK clock is divided by (this value plus one) to produce the CAN clock.
-                       BRP = (res / tq_numb) - 1;
-
-                       // 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;
-
-                       // number of tq from Sync. point to the sample point (this value plus one)                      
-                       TSEG1 = tq_numb - (tq_numb/3) - 1;
-
-                       break;
-               }
-       }
-
-       can_write_register(chip, ((SAM<<23)|(TSEG2<<20)|(TSEG1<<16)|(SJW<<14)|(BRP<<0)), CAN_BTR_o);
+       can_write_reg(chip, data, CAN_TDB1_o);
 
 }
 
@@ -517,8 +559,6 @@ void CAN_init(struct canchip_t *chip){
        uint32_t pclksel0;
        uint32_t val;
        uint32_t i;
-       
-       printf("CAN INIT, baudrate: %d\n", chip->baudrate);
 
        // configure CAN1 pins 
        CAN_configPin();
@@ -539,25 +579,22 @@ void CAN_init(struct canchip_t *chip){
        SC->PCLKSEL0 = pclksel0;
        
        // enter reset mode
-       can_write_register(chip, 1, CAN_MOD_o);
+       can_write_reg(chip, 1, CAN_MOD_o);
 
        // disable all CAN interrupts
-       can_write_register(chip, 0, CAN_IER_o);
+       can_write_reg(chip, 0, CAN_IER_o);
 
        // reset value of Global Status Register (global controller status and error counters) 
-       can_write_register(chip, 0x3C, CAN_GSR_o);
+       can_write_reg(chip, 0x3C, CAN_GSR_o);
 
        // request command to release Rx, Tx buffer and clear data overrun 
-       can_write_register(chip, (CAN_CMR_AT | CAN_CMR_RRB | CAN_CMR_CDO), CAN_CMR_o);
+       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 = can_read_register(chip, CAN_ICR_o);
-
-       // set bus timing 
-       CAN_setBusTiming(chip);
+       tmp = can_read_reg(chip, CAN_ICR_o);
 
        // return to normal operating 
-       can_write_register(chip, 0, CAN_MOD_o);
+       can_write_reg(chip, 0, CAN_MOD_o);
 
 
        //--------------------------
@@ -585,12 +622,8 @@ void CAN_init(struct canchip_t *chip){
        // enable interrupt after transmit
        // enable receive interrupt
        // enable data overrun interrupt
-       can_write_register(chip, (CAN_IER_TIE1 | CAN_IER_RIE | CAN_IER_DOIE), CAN_IER_o);
+       can_write_reg(chip, (CAN_IER_TIE1 | CAN_IER_RIE | CAN_IER_DOIE), CAN_IER_o);
        
-       // enable CAN interrupt 
-       NVIC_EnableIRQ(CAN_IRQn);
-
-
        
 }