This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Hercules TI RM48L942ZWT GIO Port

Other Parts Discussed in Thread: HALCOGEN

Hi.

We are using the Hercules TI Board RM48L952ZWT. We would like to connect a 1-wire EEPROM to one of its GIO ports: spec sheet:

Q1. Since it is bidirectiontional, this GIO is acting as both an input and output port (and in the spec, open drain for this EEPROM required, etc.). In HalCoGen, in the GIO tab what is the best way to set it up so it can be a bidirectional pin?

Q2. What is the best way to set up a timer in usec on the RM48 Hercules board (microseconds) relative to the GIO to have it be very precise? Can it be done in C, or must it be done in assembly language? 7658.DS28EC20.pdfThe handshaking requires delays between toggling this GIO pin to communicate to the EEPROM for reading/writing, etc. and preciseness is important.

  • Hi Tammy,

    Interrupt rates in the 'us' range are high, and you'd load down if not overload the CPU by bit-banging this IMO. Plus the R4 isn't optimized for bit-banging like an 'M' it's designed for throughput and trades off some latency for this. So I'd look at hardware for an answer.

    Maxim has an application note www.maximintegrated.com/en/app-notes/index.mvp/id/214 entitled 'Using a UART to Implement a 1-Wire bus master'. I think the general idea is that the UART byte corresponds to the 1-wire 'bit' so the little pulse at the beginning of each 1-wire bit can be just the START bit of the UART and the number of 1's or 0's in the data payload can represent write-0 or write 1. Read then I likely accomplished by picking one of the bits (the one at the right sample time) out of the receive data - so each byte received would yield one 1-wire bit. I didn't actually read the appnote though so I'd encourage you to do that.

    Disappointingly our UART and our SPI both have open drain support - but they are documented to condition this on the pin being configured as GIO. So I think you will need an external device that converts to open drain, and also loop back the RX and TX pins.

    If that's a big problem the N2HET can actually have it's pins configured as open Drain and drive them this way from within the timer. That's *definitely* not HalCoGen black-box timer code though. However, I think Haixiao posted an example of a HET-UART on this forum and it could very well be an adaptation of that example. But this would require a little work. It wouldn't necessarily consume an entire HET though because 1-wire shouldn't take many instructions per loop - leaving time to implement PWMs / Captures.

    -Anthony
  • Hi Anthony. Thank you. What do you mean we should look at hardware for the answer if we want to do the bit-banging approach? Do we mean use a different EEPROM part?

    For the other options you described. It may be possible to use one HET pin? Can I do a search on Hauxuao's sample code or do you have a link so we can see if it is doable? For the UART approach - do you mean we would have to dedicate either SCI or SCILIN to this EEPROM, and then have external hardware to convert to open drain?
  • Hi Tammy,

    By hardware I meant HET or SCI/LIN peripherals rather than bit banging w. the CPU.

    Either HET or SCI/LIN can implement UART functions.  

    It should be easy to do 1-wire on HET I think.   Since the HET can drive in open drain mode you can use one HET pin.

    The HET loop is normally around 1.2us,  so if you shoot for normal 60us operation on 1-wire you could set the HET to count 50 or so loops to get a bit time (adjust this slightly based on your actual HET clock rate).

    You could put an outer counter that counts 'bits to transmit',  say 8 bits.  The CPU could write the data to transmit to the data buffer then initialize this counter and wait till it counts down indicating that the transfer is complete.

    The inner counter would count out the bit duration - as say 50 counts.

    Just need to write some code that sets the pin to 0 during counts 1-N where you pick "N" to match the length of the start pulse that the master generates.   Then somewhere before the 15us mark,  maybe on count 10, have the code use the SHIFT instruction of the HET to shift a bit onto the pin.   The bit then would be held for the remainder of the bit time.   

    Somewhere after the 15us mark,  maybe on count 30, have another instruction SHIFT in from the same pin.  Use two separate shift instructions.

    If you want to receive data instead of transmit, you would write '1's to the data of the transmit shift register so that it would be up to the receiver to pull the bit line low. 

    This should wind up being 12 or 13 HET instructions if you just write it out ...

    You've got 3 compares - one at the count of '1' when you always shift 0 out onto the pin.

    A 2nd compare at the count of ~10 where you shift transmit data out,

    A 3rd compare at the count of ~30 where you shift transmit receive data in.

    So each of these would be 

      CMP

      BR  (around shift) if not equal

      SHIFT

    and you'd use 9 instructions.    

    Then you just need a CNT instruction with max count of ~50 to count out the 60us period.

    And you need another counter of some sort (either CNT or DJZ) to count out bits/transfer (the outer loop)

    plus a 'go' instruction for the CPU to write to in order to kick off a transfer.    

    So the basic kernel to read/write a byte should be on the order of 12-13 HET instructions. 

    If the loop resolution period is 1.2us - that means you have another 100 or so HET instructions available so you can

    implement PWMs and captures on the same HET that is doing this 1-wire job.  

    You could also get fancy and add HTU or DMA requests to this code with some of the spare instructions and then the

    HET could transfer blocks at a time from a buffer in memory - without CPU intervention.. 

    And it will execute like clockwork. 

    Edit above... (transmit -> receive data in...)

  • Hi. Thank you. Yes I see what you mean. You wrote that "...Haixiao posted an example of a HET-UART ". Do you have a link? Or should I just do a search above on "Haixiao" and any other key words? Thank you Again.
  • Hi Tammy,

    A Google search on "HET UART" came up with this link:
    http://www.ti.com/mcu/docs/litabsmultiplefilelist.tsp?sectionId=96&tabId=1502&literatureNumber=spna097&docCategoryId=1&familyId=454

    Should be what you need.

    Regards,
    Sunil

  • Hi Sunil,   Thank you. I downloaded the zip and pdf now. The software package is provided for two different TMS470 devices, each implementing two different UART configurations.  Which one is closest fitting to the HET on the R48L952ZWT, in terms of similarities? What are the .het files, is it read in by another TI tool like HalCoGen? How should HalCoGen be configured for the HET pin if it is bidirectional communication?

    In one of the source A256_HET_UART_9600_Polling.c is function below with i.e. HET_TX_Shift_0, HET_TX_BitCtr_0, etc.  but there were no header files in the zip downloaded. Is this info in the .het file?

    void HetUARTPutByte(unsigned char Data)
    {
      unsigned int Tmp = Data;

      Tmp <<= 1;                                    // Shift in start bit (0)
      Tmp |= 0x00000200;                            // Add stop bit (1)

      HET_TX_Shift_0.memory.data_word = Tmp << 5;   // Load TX buffer
      HET_TX_BitCtr_0.memory.data_word = 10 << 5;   // Load bit count
      HET_TX_Start_0.memory.data_word = 0x1 << 5;   // Start TX
    }

    Thank you again.

  • Hi Tammy,

    I looked at the link Sunil posted - it's for the TMS470R1x family (ARM7TDMI based) and pretty old. Those products have the HET timer. We've since gone to NHET and then N2HET so you are two generations ahead.

    I think you could probably put the .het file contents into the HET IDE and assemble them, might need to fix up a syntax issue here or there but the code should still run.

    However, I think of this as just the idea since it's clear that Maxim explains how to use a UART (like our SCI/LIN module) to talk on 1-wire.

    But a full UART like this appnote tries to implement is overkill I think. With 1-wire I don't think you need to transmit and receive asynchronously. In fact the receive seems keyed to the master sending a start pulse, and then sometime later the master samples the data pin. Very regularly.

    This is why I'd suggest something simpler as I outlined above. It's good to study the UART code, but I'd suggest writing your own.

    You might develop your program in these steps:

    1) Write code that generates 8 1-Wire clocks every time you trigger it from the CPU.
    You would have some code that skips the 1-wire HET code (branches around it) until the data field of the instruction is
    set to '1'. Look at Andreas's example for 470R1x HET to see how he does this with a DJZ instruction.

    This code would count out 8x 50 loop resolution clocks. During loops 1-10 it would drive a '0' on the pin.
    During loops 11-50 it would drive '1' on the pin.

    You will convert the drive 1 -> 3-state by configuring the HET GIO register for open drain mode on that pin.

    2) Add some code that shifts one bit onto the pin during each of the 50 loops.
    It should start shifting at around bit time 10 or 11 and hold for the duration.
    This gives you 'write capability'

    Normally you would implement this with a SHIFT instruction, and before kicking off the transfer the CPU
    would write the data to shift into the data field of this shift reg.

    3) Add more code that shifts one bit in on every 15th loop out of 50 . This should give you read capability on 1-wire.

    The assumption then is that to read, the CPU would write '1's since they just generate start bits and then 3-state,
    and read back the data captured by the code you add in step 3 - when it wants to read.
    So your CPU side driver would have to still 'transmit' 0xFF , wait for completion, and then read the result from step 3 to
    perform a 'read'.

    For a write you'd usually write the actual data value to the SHIFT instruction implementing a transmit buffer, and then
    throw away the receive data (or compare it agianst transmit data if you want to confirm that you sent what you think you did...)
  • Sorry about that. I found the example code projects that Haixiao had created for the N2HET. They are on this wiki page:
    processors.wiki.ti.com/.../RM46_CNCD

    You need to scroll down to the "Demo Software" section.

    I am not sure why they are on this page though, and probably what makes these examples hard to find.

    Regards,
    Sunil

  • Hi Anthoney and Sunil. Thank you. We have gotten confused on what the recommended approach is. is it possible just to start with i.e., a practical example even if it is only psuedocode just to get a visual (i.e., reading from EEPROM using NHET approach of sample code) and your guidance Anthoney. So we have the sample code from TI here on NHET and I2C:  3113.N2HET_EMU_I2C.zip

    and with EEPROM with GIO here: 6813.owpd310r2.zip

    So, looking at some sample read a bit code: the original sample code had reading a bit as :

    uint8 owTouchBit(int portnum, uint8 sendbit)

    {

      unsigned char result;

      portnum = 0;

      //timing critical, so I'll disable interrupts here

      EA = 0;

      // start timeslot.

      OW_PORT = 0;

      usDelay(5);

      // send bit out.

      OW_PORT = sendbit;

      usDelay(10);

      // sample result @ 15 us.

      result = OW_PORT;

      usDelay(60);

      // timeslot done.

      OW_PORT = 1;

      usDelay(5);

      //restore interrupts

      EA = 1;

      return result;

    }

    //--------------------------------------------------------------------------

    // Reset all of the devices on the 1-Wire Net and return the result.

    //

    // 'portnum'    - number 0 to MAX_PORTNUM-1.  This number is provided to

    //                indicate the symbolic port number.

    //

    // Returns: TRUE(1):  presense pulse(s) detected, device(s) reset

    //          FALSE(0): no presense pulses detected

    //

    uint8 owTouchReset(int portnum)

    {

      unsigned char result;

      portnum = 0;

      // Code from appnote 126.

      OW_PORT = 0; // drive bus low.

      usDelay(500); // 500-(3% error) ~= 480 us

      OW_PORT = 1; // bus high.

      usDelay(125); // 125-(3% error) ~= 120 us

      result = !OW_PORT; // get presence detect pulse.

      usDelay(372); // 372-(3% error) ~= 360 us

      return result;

    }

    how would this code change using what you describe Anthony using the NHET registers for one pin with what both you describe Sunil and what Anthoney describes?  Can you just give a practical example for what you mean using one of the above test functions above to replace the "GIO" toggling code example with NHET? If we can just understand one (i.e., how to do a read via RM48 NHET) then we can implement the rest because the examples are confusing when comparing it to datasheet/app notes of RM48 NHETs. Thank you again.

    i.e., full test example below we tried to use (but it is also in zip file above we downloaded from website)

    /*

    *********************************************************************************************************

    * INCLUDE FILES

    *********************************************************************************************************

    */

    #include "sci.h"

    #include <cpu.h>

    #include <lib_def.h>

    /*

    *********************************************************************************************************

    * GLOBAL VARIABLES

    *********************************************************************************************************

    */

    // general command defines

    #define MAIN_WRITE_COMMAND_EPROM 0x0F // write scratchpad command

    #define STATUS_READ_PAGE_COMMAND_EPROM 0xAA // read scratchpad command

    #define STATUS_WRITE_COMMAND_EPROM 0x55 //copy scratchpad

    #define READ_MEMORY_COMMAND_EPROM 0xF0 //read memory

    #define MAIN_READ_PAGE_COMMAND_EPROM 0xA5 // extended read memory

    // Local defines

    #define SIZE_EPROM 2560

    #define PAGE_LENGTH_EPROM 32

    #define MAX_PORTNUM 1

    CPU_BOOLEAN owAccess(int portnum);

    CPU_BOOLEAN owBlock(int portnum, uint8 do_reset, unsigned char *tran_buf, uint8 tran_len);

    int getStartingAddressEPROM(uint8 bank, unsigned char *SNum);

    uint8 owTouchReset(int portnum);

    uint8 owTouchBit(int portnum, uint8 sendbit);

    uint8 owTouchByte(int portnum, uint8 sendbyte);

    /*

    *********************************************************************************************************

    * LOCAL FUNCTIONS

    *********************************************************************************************************

    */

    /**

    * Read memory in the current bank with no CRC checking (device or

    * data). The resulting data from this API may or may not be what is on

    * the 1-Wire device. It is recommends that the data contain some kind

    * of checking (CRC) like in the readPagePacketEPROM() method or have

    * the 1-Wire device provide the CRC as in readPageCRCEPROM(). readPageCRCEPROM()

    * however is not supported on all memory types, see 'hasPageAutoCRCEPROM()'.

    * If neither is an option then this method could be called more

    * then once to at least verify that the same thing is read consistantly.

    *

    * bank to tell what memory bank of the ibutton to use.

    * portnum the port number of the port being used for the

    * 1-Wire Network.

    * SNum the serial number for the part that the read is

    * to be done on.

    * str_add starting physical address

    * rd_cont if 'true' then device read is continued without

    * re-selecting. This can only be used if the new

    * read() continious where the last one led off

    * and it is inside a 'beginExclusive/endExclusive'

    * block.

    * buff byte array to place read data into

    * len length in bytes to read

    *

    * @return 'true' if the read was complete

    */

    CPU_BOOLEAN readEPROM(uint8 bank, int portnum, unsigned char *SNum, int str_add,

    uint8 rd_cont, unsigned char *buff, int len)

    {

    int i;

    int start_pg, end_pg, num_bytes, pg;

    unsigned char raw_buf[256];

    // check if read exceeds memory

    if ((str_add + len) >= SIZE_EPROM)

    {

    return (DEF_FAIL);

    }

    // see if need to access the device

    if (!rd_cont)

    {

    // select the device

    if (!owAccess(portnum))

    {

    return (DEF_FAIL);

    }

    // build start reading memory block

    raw_buf[0] = READ_MEMORY_COMMAND_EPROM;

    raw_buf[1] = (str_add + getStartingAddressEPROM(bank,SNum)) & 0xFF;

    raw_buf[2] = (((str_add + getStartingAddressEPROM(bank,SNum)) & 0xFFFF) >> 8)

    & 0xFF;

    raw_buf[3] = 0xFF;

    // check if get a 1 byte crc in a normal read.

    if(SNum[0] == 0x09)

    num_bytes = 4;

    else

    num_bytes = 3;

    // do the first block for command, address

    if(!owBlock(portnum,FALSE,raw_buf,num_bytes))

    {

    return (DEF_FAIL);

    }

    }

    // pre-fill readBuf with 0xFF

    for (i=0;i<len;i++)

    buff[i] = 0xFF;

    // send second block to read data, return result

    if(!owBlock(portnum,FALSE,buff,len))

    {

    return (DEF_FAIL);

    }

     

    return (DEF_OK);

    }

     

    /**

    * Get the byte command for reading the page with crc.

    *

    * bank to tell what memory bank of the ibutton to use.

    * SNum the serial number for the part.

    *

    * @return the byte command for reading the page

    */

    unsigned char readPageWithCRC(uint8 bank, unsigned char *SNum)

    {

    unsigned char readPage = MAIN_READ_PAGE_COMMAND_EPROM;

    return readPage;

    }

    /**

    * Query to get page length in bytes in current memory bank.

    *

    * bank to tell what memory bank of the ibutton to use.

    * SNum the serial number for the part.

    *

    * @return page length in bytes in current memory bank

    */

    uint8 getPageLengthEPROM(uint8 bank, unsigned char *SNum)

    {

    int len = PAGE_LENGTH_EPROM;

    return len;

    }

    //--------------------------------------------------------------------------

    // The 'owAccess' function resets the 1-Wire and sends a MATCH Serial

    // Number command followed by the current SerialNum code. After this

    // function is complete the 1-Wire device is ready to accept device-specific

    // commands.

    //

    // 'portnum' - number 0 to MAX_PORTNUM-1. This number is provided to

    // indicate the symbolic port number.

    //

    // Returns: TRUE (1) : reset indicates present and device is ready

    // for commands.

    // FALSE (0): reset does not indicate presence or echos 'writes'

    // are not correct.

    //

    CPU_BOOLEAN owAccess(int portnum)

    {

    unsigned char sendpacket[9];

    unsigned char i;

    unsigned char SerialNum[MAX_PORTNUM][8];

     

    // reset the 1-wire

    if (owTouchReset(portnum))

    {

    // create a buffer to use with block function

    // match Serial Number command 0x55

    sendpacket[0] = 0x55;

    // Serial Number

    for (i = 1; i < 9; i++)

    sendpacket[i] = SerialNum[portnum][i-1];

    // send/recieve the transfer buffer

    if (owBlock(portnum,FALSE,sendpacket,9))

    {

    // verify that the echo of the writes was correct

    for (i = 1; i < 9; i++)

    if (sendpacket[i] != SerialNum[portnum][i-1])

    return FALSE;

    if (sendpacket[0] != 0x55)

    {

    return (DEF_FAIL);

    }

    else

    return (DEF_OK);

    }

    }

    // reset or match echo failed

    return (DEF_FAIL);

    }

    //--------------------------------------------------------------------------

    // The 'owBlock' transfers a block of data to and from the

    // 1-Wire Net with an optional reset at the begining of communication.

    // The result is returned in the same buffer.

    //

    // 'do_reset' - cause a owTouchReset to occure at the begining of

    // communication TRUE(1) or not FALSE(0)

    // 'tran_buf' - pointer to a block of unsigned

    // chars of length 'TranferLength' that will be sent

    // to the 1-Wire Net

    // 'tran_len' - length in bytes to transfer

    // Supported devices: all

    //

    // Returns: TRUE (1) : The optional reset returned a valid

    // presence (do_reset == TRUE) or there

    // was no reset required.

    // FALSE (0): The reset did not return a valid prsence

    // (do_reset == TRUE).

    //

    // The maximum tran_len is 160

    //

    CPU_BOOLEAN owBlock(int portnum, uint8 do_reset, unsigned char *tran_buf, uint8 tran_len)

    {

    unsigned char i;

    // check for a block too big

    if (tran_len > 160)

    {

    return (DEF_FAIL);

    }

    // check if need to do a owTouchReset first

    if (do_reset)

    {

    if (!owTouchReset(portnum))

    {

    return (DEF_FAIL);

    }

    }

    // send and receive the buffer

    for (i = 0; i < tran_len; i++)

    tran_buf[i] = (unsigned char)owTouchByte(portnum,tran_buf[i]);

    return (DEF_OK);

    }

    //--------------------------------------------------------------------------

    // Send 8 bits of communication to the 1-Wire Net and return the

    // result 8 bits read from the 1-Wire Net. The parameter 'sendbyte'

    // least significant 8 bits are used and the least significant 8 bits

    // of the result is the return byte.

    //

    // 'portnum' - number 0 to MAX_PORTNUM-1. This number is provided to

    // indicate the symbolic port number.

    // 'sendbyte' - 8 bits to send (least significant byte)

    //

    // Returns: 8 bits read from sendbyte

    //

    uint8 owTouchByte(int portnum, uint8 sendbyte)

    {

    unsigned char i;

    unsigned char result = 0;

    portnum = 0;

    for (i = 0; i < 8; i++)

    {

    result |= (owTouchBit(portnum,sendbyte & 1) << i);

    sendbyte >>= 1;

    }

    return result;

    }

    /**

    * Query to get the starting physical address of this bank. Physical

    * banks are sometimes sub-divided into logical banks due to changes

    * in attributes.

    *

    * bank to tell what memory bank of the ibutton to use.

    * SNum the serial number for the part.

    *

    * @return physical starting address of this logical bank.

    */

    int getStartingAddressEPROM(uint8 bank, unsigned char *SNum)

    {

    int addr = 0;

    switch(SNum[0])

    {

    case 0x0B:

    if(bank == 2)

    addr = 32;

    else if(bank == 3)

    addr = 64;

    else if(bank == 4)

    addr = 256;

    break;

    case 0x0F:

    if(bank == 2)

    addr = 32;

    else if(bank == 3)

    addr = 64;

    else if(bank == 4)

    addr = 256;

    break;

    case 0x13:

    if(bank == 2)

    addr = 32;

    else if(bank == 3)

    addr = 64;

    else if(bank == 4)

    addr = 256;

    break;

    default:

    addr = 0;

    break;

    }

    return addr;

    }

    //--------------------------------------------------------------------------

    // Send 1 bit of communication to the 1-Wire Net and return the

    // result 1 bit read from the 1-Wire Net. The parameter 'sendbit'

    // least significant bit is used and the least significant bit

    // of the result is the return bit.

    //

    // 'portnum' - number 0 to MAX_PORTNUM-1. This number is provided to

    // indicate the symbolic port number.

    // 'sendbit' - the least significant bit is the bit to send

    //

    // Returns: 0: 0 bit read from sendbit

    // 1: 1 bit read from sendbit

    //

    uint8 owTouchBit(int portnum, uint8 sendbit)

    {

    unsigned char result;

    portnum = 0;

     

    //timing critical, so I'll disable interrupts here

    EA = 0;

    // start timeslot.

    OW_PORT = 0;

    usDelay(5);

    // send bit out.

    OW_PORT = sendbit;

    usDelay(10);

    // sample result @ 15 us.

    result = OW_PORT;

    usDelay(60);

    // timeslot done.

    OW_PORT = 1;

    usDelay(5);

    //restore interrupts

    EA = 1;

    return result;

    }

    //--------------------------------------------------------------------------

    // Reset all of the devices on the 1-Wire Net and return the result.

    //

    // 'portnum' - number 0 to MAX_PORTNUM-1. This number is provided to

    // indicate the symbolic port number.

    //

    // Returns: TRUE(1): presense pulse(s) detected, device(s) reset

    // FALSE(0): no presense pulses detected

    //

    uint8 owTouchReset(int portnum)

    {

    unsigned char result;

    portnum = 0;

    // Code from appnote 126.

    OW_PORT = 0; // drive bus low.

    usDelay(500); // 500-(3% error) ~= 480 us

    OW_PORT = 1; // bus high.

    usDelay(125); // 125-(3% error) ~= 120 us

    result = !OW_PORT; // get presence detect pulse.

    usDelay(372); // 372-(3% error) ~= 360 us

    return result;

    }

  • Hi Tammy,

    What are your requirements from a system level.  If you can tolerate having the ARM CPU dedicated to 1-wire for the duration of the transfer - meaning it doesn't respond to interrupts during this time - then the simplest approach might be to fill in the files in the   'lib/general' folder that are 'todo' with halcogen calls to toggle the bits on the port.  You'd also need to have some sort of delay mechanism which might be as simple as using the PMU since the delays are very short.  It can be hard to count cycles on the R4 because the branch prediction logic and the flash pipeline can mean that the same code executed from different addresses or at different times may run more quickly or more slowly -   so the approach they take in the file like \lib\misc_micro\8051\LL8051.asm where they delay with NOPs wouldn't be recommended (IMO anyway).    But that's a bit of a fine detail. 

    If you can't tolerate the CPU being otherwise 'offline' when you are communicating on the 1wire bus - then I would look at N2HET because it can implement the low level code that is in  \lib\misc_micro\8051\LL8051.asm  pretty well - I mean it's 'different' because you'd convert this sequential code into a state machine form when it is going to run on NHET - but it's the same low-level stuff that N2HET can do efficiently.    The calls to these functions probably then would still be in the \lib\genera\  folder and run on the Cortex CPU but when you go to fill in the 'todo' files in that folder and you have to fill out the stub for 'touch byte' this would simply be a write to HET RAM, a loop while you wait for the HET to complete, and then a read (if there is data to read back).     If you then put some sort of OS 'sleep' call in that stub so that the CPU can multitask while the code there is waiting for HET to complete - then I think that would unblock the CPU and you could do realtime work in parallel w. the 1-wire.     I'm not an expert on 1-wire though and I'm basing this on a very quick assessment that the timing within each bit is critical (which would be maintained for you by moving LL8051.asm into HET) but the time between successive bytes is not critical.  So if the CPU were interrupted for a 'long' time during the HET based approach the only thing that would happen is that traffic would slow on the 1-wire bus but you wouldn't have communication errors.   Of course that is a big assumption - and maybe some devices that you would want to might timeout if it's too long between bytes.   Don't know enough about that.  You'd think an EEPROM would be pretty lax in this respect though.

    By the way - there is always the option we haven't discussed which would be to use an I2C based device, since we have an I2C peripheral on the MCU.  I don't know how tied you are to this particular EEPROM though and / or if there is some other aspect of 1-wire that makes it desireable.   But if you are in a situation where 1-wire and I2C are 'equivalent' then I2C is probably easiest of all the options for the Hercules MCU.

  • Hi Thank you. Right now, we can disable/enable interrupts around the EEPROM so we will go to the toggle pin approach for now as you describe above. For the usec delay we need to implement, you wrote "You'd also need to have some sort of delay mechanism which might be as simple as using the PMU since the delays are very short.  It can be hard to count cycles on the R4 because the branch prediction logic and the flash pipeline can mean that the same code executed from different addresses or at different times may run more quickly or more slowly -   so the approach they take in the file like \lib\misc_micro\8051\LL8051.asm where they delay with NOPs wouldn't be recommended (IMO anyway)."

    Do you have a practical code example of using the PMU to use in a usec delay function?  We were looking in HalCoGen, but there isn't a separate tab for PMU.  Thank you again.

  • Hi Tammy,

    PMU I believe has pretty minimal configuration and it's done in the R4-MPU-PMU tab - it looks like you have the PMU counters enabled in the HalCoGen project you uploded recently.

    There's a file 'sys_pmu.h' and it's associated c file - with functions to manipulate the PMU.

    The PMU has a dedicated free running cycle counter. So I'd just do something like
    1) current_cycles = read of PMU cycle count
    2) delay_end = current_time + delay_cycles
    3) while ((delay_end - current_cycles) > 0); /* wait */

    You should check me on 3) because the cycle counter will rollover and so there will be some cases where current_cycles is 0xFFFF FFxx (something) but delay_end is 0x0000 00yy (something). Plus you want to treate these as unsigned to begin with. But I think if you treat as unsigned and subtract than it works for short delays even if they cross a rollover point. And you shouldn't need any 2Gigacycle delays :)

    -Anthony

    EDIT:  I should have mentioned that PMU is a coprocessor register.  This makes it nice and simple to read because you can reference it with a register to register copy instruction rather than having to load an address pointer from flash (which introduces a variable in timing, etc..)  

    But I think you may need to be in privileged mode when you execute the coprocessor data read.  And that may mean your task that does this has to be setup correctly for privilege in the RTOS.   If this becomes too much of a hassle then maybe reading the RTI would be better.  It'll be less accurate (since we're talking about delaying microseconds...) but you could take the same approach.

  • Hi Thank you. Since we want to use the NHET pin for bidirectional setting(to do the toggle), and getting (to read back the value of the pin) do we need to configure HalCoGen or manualyl configure the HET register a certain way to allow for both bidirectional functionality and either toggling or reading of the value of this pin? which HalCoGen function can we use for toggling and reading? Is it the standard GET/SET functions autogenerated will work for a pin that is bidirectional? 

  • h Anthony. I forgot to ask above also. What HalCoGen files do I find the functions that can control the PMU to be set up as a timer? The names of some of the functions and registers are not transparent, and I did not find a PMU section in datasheet spnu503 (the RM48x Technica Reference manual). Is there another datasheet on PMU?  Thank you again.

  • Hi Tammy,

    The PMU isn't documented in our TRM - it's part of the Cortex R4F and documented in ARM's TRM for the CPU itself - infocenter.arm.com/.../DDI0363E_cortexr4_r1p3_trm.pdf
    is the document you want. See Chapter 6 - Events and Performance Monitor

    HalCoGen functions are in the file sys_pmu.asm.

    You would mainly start the counters (_pmuStartCounters_), and to then read the counter call _pmuGetCycleCount_ Note that this funciton is a single instruction - so it would be a good candidate to try to inline. We don't have a compiler intrinsic yet for 'mrc' unfortunately - but this has been requested.

    I probably wouldn't reset this counter - I'd just take readings off of it and subtract the readings to get the interval. This way it can be read in different places in code and still provide valid intervals.
  • Hi. Thank you. I will look at the pdf now. So using _pmuGetCycleCount_ what pmu count value is equal to 1 usec? i.e.,

    #include "sys_pmu.h"

    ...

    volatile unsigned long cycles_PMU_start,cycles_PMU_end, cycles_PMU_measure, cycles_PMT_comp;
    volatile unsigned long cycles_PMU_code;
    ...
    void functionName (...)
    {
    _pmuInit_();
    _pmuEnableCountersGlobal_();
    _pmuSetCountEvent_(pmuCOUNTER0, PMU_CYCLE_COUNT);

    ...
    _pmuResetCounters_();
    _pmuStartCounters_(pmuCOUNTER0);

    // delay loop for 500 usec (microseconds)
    while (pmuCount <= ?)
    {
    pmuCount= _pmuGetCycleCount();
    }

    ....
  • Tammy,

    The cycle count is CPU cycles, and I think you are running the CPU at 220MHz?
    So one CPU cycle would be 4.5ns.
    In which case a count of 1000ns/4.5ns = 220 is 1us.
    Kind of cool that you can just count for the CPU frequency in MHz to get 1us ;)
  • Hi Thank you great. In HalCoGen when we set up the project based on RM48, it still had default settings i.e., under Clock Tree tabs GCLK-> 2xCortex-R4F = 200MHz, HCLK-> SYS = 200 MHz. We are using the Hercules RM48L952ZWT version of the RM48. Should we have changed the default from 200MHz to 220MHz? I did not find a tab where the CPU was set up at 200MHz? Where should we look for that in addition to what we see under the HalCoGen Clock Tree tab?
  • Hi Tammy,

    If you are running at 200MHz you can just use a delta cycle count of 200 to time out 1us.
    I wouldn't change the frequency of the MCU just because I assumed incorrectly that you were maxing out
    the CPU's frequency which for the '952 is 220MHz. But if you can get the job done at 200MHz that's fine and you'll consume a little less power.

    If you ever want to change the CPU frequency you can see the on the RM48L952ZWT->Clock Tree tab, the CPU clock is GCLK. (I've got your .hcg open now and yes it is 200MHz).

    If you want to bump this up or down you would click on PLL1 - which I'm saying because if you were to click the big GCM button you'd see that PLL1 is routed through the GCM to the CPU.

    The PLL tab has several dividers and a multipler - for 220 I'd just increase the multiplier from 150 to 165.
  • H I Thank you. If we wanted i.e., 500usec delay but we have to call the other pmu functions prior to looping, do we need to substract an offset to make up for those function calls for an accurate delay? i.e., if we want to delay 500usec, should we actually delay 490 usec to make up for the _pmuResetCounters_() and _pmuStartCounters_(pmuCOUNTER0) function calls? Does _pmuSetCountEvent_(pmuCOUNTER0, PMU_CYCLE_COUNT) need to be called each time as well when we want to use the PMU in this manner?


    (for example)
    void maniFunction(...)
    {
    ...
    bsp_NHET1_SET (TERMINAL_J17_NHET1_GIO,PIN_LOW);
    BSP_OS_TimeDlyUsec(500); // 500-(3% error) ~= 480 us

    bsp_NHET1_SET (TERMINAL_J17_NHET1_GIO,sendbit);
    BSP_OS_TimeDlyUsec(125); // 125-(3% error) ~= 120 us

    ....
    }


    void BSP_OS_TimeDlyUsec (CPU_INT32U dlyUsec)
    {
    // CPU is running at 200MHz, 1 cycle = 5nanosec, 1000ns/5n = 200 1 usec

    _pmuSetCountEvent_(pmuCOUNTER0, PMU_CYCLE_COUNT);
    _pmuResetCounters_();
    _pmuStartCounters_(pmuCOUNTER0);

    for (i = 0; i < dlyUsec; i++) // # of usecs
    {
    while (_pmuGetCycleCount() < 200) ; // loop for approx 1 usec
    }
    }


    Q2. If we set the NHET pin to be output so we can toggle it. Will the device it is connected to outside the RM48 still be able to also toggle it and change it's value, or do we have to manually via register in code change it back to being an input pin to allow the device to toggle it back? And keep flipflopping the NHET configuration as we go for this pin to be able to be used in both directions?
  • Hi Tammy,

    You certainly can adjust for the time to read the counter and it's probably a good idea to start out with 12 or so cycles less because it takes about 6 cycles for the 'mrc' instruction to execute. (please confirm this in the ARM TRM but it's what I remember). Alternatively you could do a bench test by toggling an IO pin in a loop with this delay method - and you'll find out a good number to use as an offset.

    What I still like about this method is that you'll minimize the error due to CPU execution time variability - you'll just have an initial and final error but it shouldn't grow proportionally to the duration of your delay as it would if you try to time based off instruction execution time alone.
    I think you will be able to get within 50ns of your 500us number for example.

    Sorry to not think about this but I was really liking the simplicity of the formula without this adjustment ;)

    Regarding your other questions - remember that there are 3 counters:
    1) 2 *event* counters that take different inputs selected from among a list
    2) a dedicated cycle counter that counts CPU cycles.

    I would use the dedicated CPU cycle counter - because that is what you want to count and it's easier to use. It also leaves the event counters free for measuring something else (maybe like time your CPU is stalled which you might be interested in later for improving performance...)

    You can use the sys_pmu.asm function to read this if you like:
    ;-------------------------------------------------------------------------------
    ; Get Cycle Count
    ; SourceId : PMU_SourceId_010
    ; DesignId : PMU_DesignId_010
    ; Requirements : HL_SR486

    .def _pmuGetCycleCount_
    .asmfunc

    _pmuGetCycleCount_

    mrc p15, #0, r0, c9, c13, #0
    bx lr

    .endasmfunc

    But you may want to just inline the 'mcr' instruction and avoid the function call / return overhead. This will be more accurate. Up to you though.

    If you inline it - I would try to make the compiler do that for you. Maybe create another empty C function that is declared w. the inline keyword and only has an ASM('mrc p15, #0, r0, c9, c13, #0') in the body.
    I believe if the Compiler inlines that you'll wind up w. the mrc and not the call/return branches. But you can ask the best way to do this on the compiler forum and you may get a better answer from one of the compiler experts.


    Last - you only need to call the _pmuStartCounters_ function once -- anywhere before you start using the cycle counter. You shouldn't need to reset the counter or stop it; and the cycle counter is hard-wired for CPU cycles so there is no event source to select.

    As long as you are always measuring intervals, i.e. t2-t1 then it doesn't matter if t1 is 0, 10000, 8000000, etc. So no point in resetting the cycle counter.
  • Tammy,

    I will answer Q2. You can configure the N2HET pin as output and also configure it to be an open-drain output. This means that the N2HET will only be able to output a low-level on this pin, and it will need to be pulled up using an external resistor.

    You need to ensure that the other circuit / IC driving this pin is also configured as an open-drain output.

    Regards,
    Sunil
  • Sunil

    Thanks - I missed that one.

    One thing - and I need to research this. Do you know if the DIN register of HET returns the DOUT register or the actual pin value. If it's returning the actual pin value - then there is no need to change the pin direction.

    If it doesn't but returns DOUT when DIR=1, then the bit-banging function would be a little different.

    -Anthony
  • Anthony,

    DIN always reflects the state of the pin.

    Regards,
    Sunil
  • Hi Sunil does that mean the external device (in our case eeprom), can toggle this pin freely as well (and the rm48 getting its value) after rm48 has set it as an output pin and toggled it, without having to put thhis pin in a different state to allow the external device to control it as well?
  • Hi Tammy,

    Yes, with both drivers configured as open-drain outputs they can each drive the pin without causing any damage to the pin itself, and without having to modify any other pin configuration. Of course you will need to implement some protocol for this communication to work properly. I see that this protocol is already defined in the EEPROM datasheet.

    Regards,
    Sunil
  • The link above cannot be opened. so I think the link below contains the same project and application notes from haixiao.weng

    e2e.ti.com/.../1042363