Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
The case for CAN
#1
I have been working on various new machines, planters, fire engines, etc for years.
There never was any question, distributed computing was always used. At first 8085 types, then Atmel 328 was used, mainly due to the avalability of Arduino Nano boards and WinAVR.
The AVR does work, but debugging is sadly lacking. I have also used Raspi, and was disgusted with the bootup sequence. That is totally out of the question.
Why use such a horrible operating system on any machine? Nobody wants to wait that long for any machine to start working.
ARM boards became available, and I switched to STM32F103 initially, using Embitz. What a marvellous upgrade! Embitz and the EBmoitor is absolutely marvellous!
Why struggle with AVR? The STM32 units are cheap enough.
The first consideration then is what interconnection type to use between units.
I did use serial in the form of RS485. All micros I used have serial, and it is always easy to add another bit bang one. The problem with a RS485 setup is that it is a master - slave setup.
For fast response, the master has to poll often, resulting in micros spending time looking at serial, while neglecting its real work.
The STM32 units can receive serial without using processor time, but the problem of fast transmit response remain. DMA does not do it for short massages.
I have used Ethernet with the Raspis, disasterly. Those sockets are simply horrible in a moving and shaking machine.
Essentially, any master slave setup is not the best. It has to be multimaster, minimum wiring, and no collisions.
No wonder canbus is used so widely. It is really the answer.
It appears all the STM32 units has the provision for Canbus. It is easily applied, does not use processor time for receiving, and has numerous filters to block any unwanted messages.
To use the full Canbus specs, driver chips must be used, but canbus can even be used without that. See Siemens Appnote AP2921. Their one wire setup can  be extended simply by using LM393 comparators and higher voltage on the one wire. That will be the cheapest setup for longer connections.
Canbus will transmit when the bus becomes free, the processor only posts the txmessage.
Received messages are filtered and stored, the processor reads the messages when there is time available, no interrupts are required, and no unnecessary messages are received.
The filters only pass messages intended for the particular unit.
The initial code for a STM32F407 is
 //p1099 CAN master control register Page numbers refer to the STM32F407 reference manual
  CAN1->MCR &= (~(uint32_t)CAN_MCR_SLEEP); //wake up from sleep mode
  CAN1->MCR = (CAN_MCR_INRQ);       //p1083 init mode, disable auto retransmission//CAN_MCR_NART |
  uitime = uimillis + 500;
  while(!(CAN1->MSR & CAN_MSR_INAK)){assert(uimillis, <, uitime);} //p1083 wait for confirmation
 
Set up the baudrate

  //A good rule of thumb is to have your sampling point at 70-80% of the nominal bit time.
  //p1095 For 125Khz CAN: APB1 42Mhz;(APB1 clocked at 42 MHz)
  // quanta 1+9+4 = 14, 14 * 24 = 336, 42000000 / 336 = 125000
  // CAN Baudrate = 125Kbps (CAN clocked at 42 MHz) Prescale = 24

Code:
  CAN1->BTR  = 0x01 << 24; //p1108 CAN bit timing register  Resynchronization jump width
  CAN1->BTR |= 0x03 << 20; //TS2 tBS2 = tq x (TS2[2:0] + 1)p1108
  CAN1->BTR |= 0x08 << 16; //TS1 tBS1 = tq x (TS1[3:0] + 1)p1109
  CAN1->BTR |= 0x17;       //prescaler 24 p1109
  CAN1->FMR  = 0x0E << 8;   //p1116 CAN filter master register
This must be changed to the required speed and APB1 clock speed
Set up the receive filters

Code:
  CAN1->FMR  = 0x0E << 8;   //p1116 CAN filter master register
  CAN1->FMR |= 1;          //p1116 Initialization mode for the filters.
  CAN1->FM1R = 0;          //p1117 Filter mode register 1:32bit filters mask mode
  CAN1->FS1R  = 0x0FFFFFFF;//p1117 Single 32-bit scale configuration
  CAN1->FFA1R = 0x00000000;//p1118 Fifo assignment register
  CAN1->sFilterRegister[0].FR1 = 0x00B << 21;//p1091,1119 COMP
  CAN1->sFilterRegister[1].FR1 = 0x00D << 21;//p1091,1119 COMP
  CAN1->sFilterRegister[0].FR2 = 0x47F << 21;//p1091,1119 MASK
  CAN1->sFilterRegister[1].FR2 = 0x47F << 21;//p1091,1119 MASK
  CAN1->FA1R  = 0x00000001;//p1118 filter activation register filters 0 only
  CAN1->FMR   = 0x0E << 8; //p1116 CAN filter master register clear init bit
  CAN1->MCR &= ~(CAN_MCR_INRQ);// p1083 normal mode
  uitime = uimillis + 500;
  while(CAN1->MSR & CAN_MSR_INAK){assert(uimillis, <, uitime);};  //p1083 wait for confirmation

Set up a tx message struct

Code:
typedef struct
{ u16   id;                 // 11 bit identifier
  u8    data[8];            // Data field
  u8    len;                // Length of data field in bytes
  u8    format;             // 0 - STANDARD, 1- EXTENDED IDENTIFIER
  u8    type;               // 0 - DATA FRAME, 1 - REMOTE FRAME
}canmsg;
canmsg cantxmsg;

I found that most messages used were the 11bit id only, or id + 1. The full 8 bytes data was rarely used.
The transmit code:

Code:
  while(!(CAN1->TSR & (1 << 26)))//p1103 TME0 Mailbox empty
  {assert(uimillis, <, uitime);} //NAK?
  CAN1->sTxMailBox[0].TIR = 0;                     //p1110
  CAN1->sTxMailBox[0].TIR |= (txmsg->id << 21)|txmsg->type;
  CAN1->sTxMailBox[0].TDTR = (txmsg->len & 0x07);    //p1111
  if(0 == txmsg->len)
 { CAN1->sTxMailBox[0].TDLR = (txmsg->data[0]);
 }else if(1 == txmsg->len)
  { CAN1->sTxMailBox[0].TDLR = ((txmsg->data[1] << 8) |  (txmsg->data[0]));
  }else
  { CAN1->sTxMailBox[0].TDHR = ((txmsg->data[7] << 24) //p1112
                                 |(txmsg->data[6] << 16)
                                 |(txmsg->data[5] <<  8)
                                 |(txmsg->data[4]));
    CAN1->sTxMailBox[0].TDLR = ((txmsg->data[3] << 24)
                                 |(txmsg->data[2] << 16)
                                 |(txmsg->data[1] <<  8)
                                 |(txmsg->data[0]));
  }
  CAN1->sTxMailBox[0].TIR |= 0x01; //p1110 TXRQ

Receive is:

Code:
volatile u32 mail0 = 0;
    rxid = (((CAN1->sFIFOMailBox[0].RIR) >> 21) & 0x07ff);  //p1113 11 bits only (If you require the id in your code)
    datalen = (CAN1->sFIFOMailBox[0].RDTR) & 0x0F; //p1114
    //else if 0 datalen simply read CAN1->sFIFOMailBox[0].RIR
     mail0 = CAN1->sFIFOMailBox[0].RDLR;
      rxdata[0] = (mail0 & 0xFF);
      mail0 >>= 8;
      rxdata[1] = (mail0 & 0xFF);
      mail0 >>= 8;
      rxdata[2] = (mail0 & 0xFF);//p1115
      mail0 >>= 8;
      rxdata[3] = (mail0 & 0xFF);////p1115
      mail0 = CAN1->sFIFOMailBox[0].RDHR;
       rxdata[4] = (mail0 & 0xFF);////p1115
       mail0 >>= 8;
       rxdata[5] = (mail0 & 0xFF);////p1115
       mail0 >>= 8;
       rxdata[6] = (mail0 & 0xFF);////p1115
       mail0 >>= 8;
       rxdata[7] = (mail0 & 0xFF);//p1115
 Change as you like
The filters are not easy to envisage, but can easily be proven by this code:

Code:
  #define COMP 0x00D //change as required
  #define MASK 0x47F  //change as required
  u16 id= 0;
     for(id=0; id < 0x800; id++)
        {// printf("%d ",id);
            if ((id & MASK) == COMP)
           { printf("0x%03X\n", id);
           }
        }
Canbus is also easily tested in logic analysers.
I am not aware of anything that can beat canbus for interconnection.
I hope this post is of help to somebody.
Johan Smit
Reply
#2
Hi Johan,
Having started myself with the 8085 and companions I can only agree with your views. AVR is mis-designed and slow; RPI, Beaglebone and the like are totally unsuitable for serious embedded applications, one reason being the never ending bootup. Currently I happily use STM32F0 and F3, mainly. A forthcoming project uses CAN so thank you for the code snippets: I'm sure they will come in handy.
-e
Reply
#3
(03-09-2018, 11:07 PM)escalator Wrote: Hi Johan,
Having started myself with the 8085 and companions I can only agree with your views. AVR is mis-designed and slow; RPI, Beaglebone and the like are totally unsuitable for serious embedded applications, one reason being the never ending bootup. Currently I happily use STM32F0 and F3, mainly. A forthcoming project uses CAN so thank you for the code snippets: I'm sure they will come in handy.
-e
Hi Escalator,
My pleasure.
A part I did not mention is the availability and low cost of the ST-link V2.
That is really nice about STM32 (and STM8).
Also what I found very helpful in setting up PID motion control is STMStudio.
That makes it easy to view graphically the value of any register or variable.
Sometimes it is not easy to know the address of the register or variable. Then in debugger mode, right click on the item and choose memory view at .. Use that memory address in STMStudio. 
Johan Smit
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)