char pointer(3): const

To have an idea what is __const__ in one char pointer declaration, read it from right to left.

  • const char *p
    • p is a pointer to a const char, it is same as “char const *p.
    • you can change the value of p and get it to point to different constant characters. But whatever p points to, you can not change the contents.
  • char * const p
    • p is a const pointer to a char
    • you are unable to change the pointer, but can change the contents.

Example 1

if it good to do:

norm = str;

norm = “AUTO”.

if you define norm this way:

const char *norm = “NSTC_J”;

char str[] = “PAL_M”;

However, both will fail with error “assignment of read-only variable ‘norm’”, if you define

char * const norm = “NSTC_J”,

Example 2

I have

struct norm_val {

    char *norm;

    Int reg_val;

};

struct norm_val set_norms[]= {

    {“AUTO”, 0}

    {“NTSC_J”, 0x10}

    {“PAL_M”, 0x40}

    {NULL, 0}

};

As we want to keep the contents of set_norms[i].norm unmodifiable, we need define “norm” as a pointer to a const char:  const char *norm;

At the same time, we don’t want set_norm[i].norm points to somewhere else, we need define “norm” as a const pointer:  char * const norm;

Moreover, we don’t want the entire content of set_norms[] being changed, either “norm” pointer, or “reg-val” value, we need define the set_norms as const (by doing this, we don’t need explicitly define “norm” to a const pointer).

struct norm_val {

    const char *norm;

    int reg_val;

} ;

const struct norm_val set_norms[]= {

    {“AUTO”, 0}

    {“NTSC_J”, 0x10}

    {“PAL_M”, 0x40}

    {NULL, 0}

};

Or

const struct {

    const char *norm;

    int reg_val;

} set_norms[]= {

    {“AUTO”, 0}

    {“NTSC_J”, 0x10}

    {“PAL_M”, 0x40}

    {NULL, 0}

};

C language basics(1): initalization of objects

There are two sorts of initialization:

  • at compiler time
    • The object with static duration: global variables, marked as static, extern, can only be initialized at compile time.
    • compile-time initialization can only be done using constant expressions.
  • at run time.
    • Any other object has automatic duration, and can only be initialized at run time.
    • can be done using any expressions.

“all or nothing” approach (to aggregate initializations): an attempt to explicitly initialize any part of the aggregate, will guarantee that the entire aggregate be initialized. The parts without an explicit initializer will be zero-initialized:

pointer: NULL.

Arithmetic type: positive zero. (for character type, c = 0 equals to c = ‘\0’) ‘\0’ is to emphasize the character nature of some expression.

Aggregate: every member is initialized based on its type.

Union: the first name number is initialized based on its type.

 

This rule explains this fact: “When you initialize a character array with a string, the system adds ‘\0’ to the end of the string, by default.”

If you define char str[4] = “123”, the compiler initializes str[3] to zero.

If you define char str[4] = “1234”, str[3] is ‘4’, and there is no ending ‘\0’.

If you define char str[4] = “12345”, the compiler will warn that “initializer-string for array of chars is too long”. The interesting facts are: strlen(str) is 5, sizeof(str) is 4, when you use printf(“%s”, str), it prints “1234”, without “5”.

But I don’t understand why 5 is still not there, even I print the chars one by one, printf(“%c”, str[4]) is nothing….

If I define char str[2][4] = {“12345”}, the compiler warns, strlen(str[0]) is 4, sizeof(str) is 8. Printf(“%s#%s”, str[0], sre[1]) will give you “1234#”.

If I define char str[2][4] = {“12345”, “123”}, warning, strlen(str[0]) = 7, sizeof(str) is 8. Printf(“%s#%s”, stre[9], str[1]) will give you “1234123#123);

String_5[2][4] = {“1234”};

String_5[1] is initialized the same as objects that have static storage duration.

References:

http://publications.gbdirect.co.uk/c_book/chapter6/initialization.html

Endianness

Endianness  affects how you store a 32-bit(4-byte) value into memory.

For example, you have 0x90 AB 12 CD,

In little endian, you store the least significant byte in the smallest address.

CD 12 AB 90

low————————————— high

In big endian, you store the most significant byte in the smallest address.

90 AB 12 CD

low————————————– high

How do you parse the individual bytes from a 32-bit word?

parse_yuv422 (uint32_t yuv)  // note this is for YUYV format, hence Y1 is the most significant byte

{                                                                                                                

        int32_t y1, u, y2, v;

#ifdef __LITTLEENDIAN__

        y1 = (yuv & 0x000000FF);

        u =  (yuv & 0x0000FF00) >> 8;

        y2 = (yuv & 0x00FF0000) >> 16;

        v  = (yuv & 0xFF000000) >> 24;

#else

        y1 = (yuv & 0xFF000000) >> 24;

        u  = (yuv & 0x00FF0000) >> 16;

        y2 = (yuv & 0x0000FF00) >> 8;

        v  = (yuv & 0x000000FF);

#endif

}

For little Endian, we can also cast &yuv to uint8_t *.

uint8_t *yuv_p = (uint8_t *)&yuv;

y1 = *yuv_p;

u = * (yuv_p + 1);

y2 = *(yuv_p + 2);

v = *(yuv_p + 3)

see the potential performance impact of ‘pointer v.s value”.

char pointer(2): passing a pointer as argument

The library provides APIs:

xxx_set_property_p(ctx, int property, void *value);

xxx_get_property_p(ctx, int property, void **value);

In the library,

struct buf {

    int index;

    int pointer;

}

struct context {

    void ** buf_pointers;   // point to the start of the pointers, which point to the application-allocated buffers

    struct buf * xxx_bufs;   // point to start of

    int planar_offsets[3];

    const char *norm;

}

int xxx_set_property_p(ctx, int property, void *value)

{

    ctx->buf_pointers = (void **) value;

    memcpy(ctx->planar_offsets, (int *)value, sizeof(int) * 3);

    ctx->norm = (char *) value;

}

ctx->xxx_bufs = (struct buf *)calloc(num_bufs, sizeof(struct buf));

ctx->xxx_bufs[i].pointer = ctx->buf_pointers[i];

int xxx_get_property_p(ctx, int property, void **value)

{

    *value = ctx->planar_offsets; // ?????

    *value = ctx->buf_pointers;

    *value = ctx->norm;

}

 

In the application,

void ** pointers = NULL;

int planar_offsets[3];

char norm[100] = “”;

pointers = calloc (nbufs, sizeof(void *));

capture_set_property_p(ctx, PROP_BUFS, pointers);

capture_set_property_p(ctx, PROP_PLANAR, offsets);

capture_set_property_p(ctx, PROP_NORM, norm);

 

capture_get_property_p(ctx, PROP_BUFS, (void **)&pointers);

capture_get_property_p(ctx, PROP_NORM, (void **)&norm);

 

 

 

 

 

 

 

 

 

 

 

 

 

}

There are two ways to pass the norm to the library:

Char norm[128] = “ntsc_j”;  // note: use a large size, not leave the size blank, as you might have overbound issue after a later modification of the array

or

Char *norm= “nstc_j”;

(the change of #1 content can be done by strcpy(norm, “pal_m”), the change of #2 content can be done by redirecting the pointer “norm = “pal_m”;

If the norm will be taken from the input, the #1 declaration & initialization has to be used).

 

Capture_set_property_p(_PROPERTY_NORM, norm);

 

 

Capture_set_property_p(property, void *value), sometimes, we keep a copy of the array, as it is mandatory to keep capture driver running; if it is optional, we reply on the pointers supplied by the user, without keep our own copy.

 

In this function, we cast void *value to (char *)value, and validate it.

 

Char *norm = NULL;

Capture_get_property_p(PROPERTY_NORM, (void **)&norm);

 

In the library,

Capture_get_property_p(property, void ** value)

{

*(char **)value = “ntsc_j”;  // return a const string

}

 

Char pointer (1): array v.s. pointer

3 common methods to declare and initialize a string:

  • char string_1[9] = “01234”;
    • declare string_1 as an array of 9 characters.
    • set the initial content of element [0] to [4].
    • the remaining elements [6],[7],[8] is initialized implicitly to zero.
    • we may later overwrite with other characters if we wish, via strcpy().
  • char string_2[  ]= ”01234”;
  • char *string_3   = “01234”;
    • create a little block of characters (somewhere in memory), and let the string_3 pointer point to it.
      • If we do “string_3 = “678””, printf(“%s”, string_3) will give you “678”.
    • we may reassign string_3 to point somewhere else, but we can’t modify the characters it points to.
      • If we do “strcpy(string_3, “567”), the program will crash.
      • If we do “string_3[0]=’5’, string_3[1]=’6’”, there is no crash, but it doesn’t work at all:  if you printf(“%c”, string3_[0]), it is still ‘0’.
      • If you initialize the pointer this way: char *string_3 = {‘1’, ‘2’, ‘\0’},  the compiler will give you a few warnings, like “initialization makes pointer from integer without a cast”, “excess elements in scalar initializer”;
      • if you do char *string_3={‘1’}, only the first warning still there.
    •  Be cautious of using “char *string = “1234””, it is dangerous/bad practice to use it, as the string is un-modifiable, and possibly you don’t reallize it until you encounter problems.

 Note: Either string_1, or string_2, or string_3, the compiler appends a ending/terminating char ‘\0’ automatically.

sizeof()               strlen()

string_1                  9                           5

string_2                  6                           5

string_3                  4                            5

 More notes:

  1. sizeof(*string_3) is 1.
  2. strlen(str) looks for ‘\0’, and return the length (not including ‘\0’).
  3. trcpy(str1, str2) copies str2 to str1, until ‘\0’ is reached, and then set the last index to ‘\0’.
  4. printf(%s”, str) either stops at ‘\0’, or stops at the number of the elements.
    • looks for ‘\0’, in order to decide the end of s string; at the same time,
    • if the sizeof the array is initialized bigger than the number of elements it was defined to, printf stops at the last element of the array.

More about the storage of a string

  1. The endianness doesn’t affect the storage of C string (character array), as in a C array, the address of consecutive array elements always increases: &a[i+1] is bigger than &[i].

Addvariant

You have source code, and want to build it. you need give the compiler the instructions on how to build the code: common.mk and Makefile.

An useful binary “addvariant” will help do the magic.

  • first, using the option “-i” creates the initial common.mk and Makefile in the current working directory.
  • second, add directories as you need, without supplying “-i” option.
  • Or the two steps can be combined together if you just need one level of directory.

 Example 1: addvariant -i OS/CPU/VARIANT nto arm dll.le.v7

This will:

  • Create a Makefile in the current directory, with contents:

LIST=OS CPU VARIANT

ifndef QRECURSE

QRECURSE=recurse.mk

ifdef QCONFIG

QRDIR=$(dir $(QCONFIG))

endif

endif

include $(QRDIR)$(QRECURSE)

  • create a common.mk

ifndef QCONFIG

QCONFIG=qconfig.mk

endif

include $(QCONFIG)

include $(MKFILES_ROOT)/qtargets.mk

  • Create nto-arm-dll.le.v7 directory, with Makefile in this directory says include ../common.mk”.

Example 2: addvariant -i OS

  • create a Makefile,

LIST=OS

ifndef QRECURSE

QRECURSE=recurse.mk

ifdef QCONFIG

QRDIR=$(dir $(QCONFIG))

endif

endif

include $(QRDIR)$(QRECURSE)

  • create a common.mk.
  • but no sub-directory created yet until addvariant nto arm dll.le.v7. this will create the directory nto, nto/arm, nto/arm/dll.le.v7, with Makefile inside each directory,
    • nto/Makefile: LIST= CPU;
    • nto/arm/Makefile: LIST=VARIANT;
    • nto/arm/dll.le.v7/Makefile: include ../../../common.mk
  • other directories will be added, if you do “addvariant nto x86 dll”, “addvariant nto arm dll.le.v7.hbas”, etc.
    • in the latter case, compiler will add CCFLAGS “-DVARIANT_dll –DVARIANT_le –DVARIANT_v7 -Dhbas”.
  • But “addvariant nto arm dll.le.v7.hbas adv7280m” gives an error: “too many directory levels specified”.
    • you can still add extra level of VARIANT manually, by:
      • add variant nto arm dll.le.v7.hbas first,
      • then create adv7280m folder under dll.le.v7.hbas,
      • create a makefile with “LIST=VARIANT” in dll.le.v7.hbas,
      • and put a makefile with “include ../../../../common.mk” in adv7280m folder.
      • The complier will use CCFLAGS “-DVARIANT_adv7280m -DVARIANT_dll –DVARIANT_le –DVARIANT_v7 –DVARAINT_hbas”.

note: Have a file “i2c.c”, stored in the variant directory, the compiler with choose this i2c.c to compile, instead of the one in the main directory.

More on the executable name and install path:

  • The default install directory is “lib/dll”, unless you add “INSTALLDIR=/usr/lib” in common.mk.
  • all the variant names, except dll, le, v7, etc, will be appended to the final name, by default.
    • e.g. in the last example above, the executable would be “adv728xm-adv7280m-hbas.so” (project-variant1-variant2.so)
    • If you have “NAME=$(IMAGE_PREF_SO)capture-decoder” in common.mk, you will have a library named “libcapture-decoder-adv7280m-hbas.so”
    • If you don’t like the atomatic appending, use:

DEC_VARIANTS := adv7280m adv7281m

EXTRA_SILENT_VARIANTS += $(DEC_VARIANTS)

  • You can combine variant names into a compound variant, using a period(.), dash(-) or slash(/) between the variants.
    • If you have a variant name “omap4-5”, the compiler will interpret it as “VARIANT_omap4” and “VARIANT_5”. Therefore, you have to use omap45. If you still want “omap4-5” to be a part of the library name, In common.mk,

 SOC_VARIANTS := j5 imx6 omap45

SOC_VARIANT_NAME = $(filter &(VARIANT_LIST), $(SOC_VARIANTS))

ifeq ($(SOC_VARIANT_NAME), omap45)

SOC_NAME=omap4-5

else

SOC_NAME=$(SOC_VARIANT_NAME)

Endif

 NAME=capture-$(SOC_NAME)

I2C (1) — how it works

History

I2C (inter IC) was invented by Philips Semiconductor (now named NXP). It is typically used to attach low-speed peripherals to processors/micro-controllers. Since 2006, no licensing fees are required to implement I2C protocol, but fees are still required to obtain I2C addresses, allocated by NXP.

I2C bus (SDA, SCL)

  • bidirectional
    • both outputting and inputting signals on the wire are possible (it can sense the voltage level of the wire).
  • open-drain, or open-collector
    • masters and slaves can only drive these lines low, or leave them open;
    • each line requires a pull-up resistor on it, to pull the line up to VCC, if no I2C device is pulling it down.

i2c_interface

more on pull-up resistor:

  • without a pull-up resistor, he device is not able to generate the I2C start condition.
    • sometimes, there is no external pull-up, but an internal pull-up can be enabled.
    • lack of pull-up will not damage either IC, as PNP transistor is being used.
  • resistor selection: start with 4.7K, and adjust down if necessary.
    • note: a small resistor might be too weak for I2C pull up (it might still work, depending on the I2 speed, etc).

I2C bus transaction

A master is the device which initiates a transfer, generates clock signals and terminates a transfer. A slave is the device addressed by a master.

  • all transactions begin with a START and are terminated by a STOP.
    • A HIGH to LOW transition on the SDA line while SCL is HIGH defines a START condition.
      A LOW to HIGH transition on the SDA line while SCL is HIGH defines a STOP condition.
  • Usually, slaves are in the idle condition, monitoring the SDA and SCLK lines for the start condition and the correct address.
  • Following the start condition, the 8 address bits are transferred  (one bit per clock cycle).
  • The slave,
    • responds by pulling the data line low during the ninth clock pulse( this is known as an acknowledge bit) if it recognizes its address, or
    • not-acknowledges(NACKs) the master by letting SDA be pulled high (come ack to the idle condition) if it is not its address.
  • Byte format. Every byte put on the SDA line must be eight bits long. The number of bytes that can be transmitted per transfer is unrestricted. Each byte must be followed by an Acknowledge bit. Data is transferred with the Most Significant Bit (MSB) first .

data-transfer

below shows an example of writing/read a single byte to/from a slave device (slave 7-bit address: 010000xb). Gray color means master controls SDA line while while color indicates slave controls SDA line.

write

read

NOTE:

  1. A read contains 2 bus transactions: the first one is used to send the register address (R/W bit is 0 for this one ); the future one(s) are used to receive data ( R/W bit is 1 in this case).
  2. for writes, the slave sends an ACK after every data byte is successfully received; for read, the master usually sends an ACK after every data byte is received. When the master wants to stop reading, it sends a NACK after the last data byte, to create a STOP condition on the bus.

I2C Arbitration and collision detection

Several I2C multi-masters can be connected to the same I2C bus and operate concurrently. By constantly monitoring SDA and SCL for start and stop conditions, they can determine whether the bus is currently idle or not. If the bus is busy, masters delay pending I2C transfers until a stop condition indicates that the bus is free again.

However, it may happen that two masters start a transfer at the same time. During the transfer, the masters constantly monitor SDA and SCL. If one of them detects that SDA is low when it should actually be high, it assumes that another master is active and immediately stops its transfer. This process is called arbitration.

Probed signal examples

Example #1: (the slave 0x70 doesn’t respond)

# irecv -n/dev/i2c0 -a0x70 -l1  //  0111 0000 –> 1110 0001 DCMD_I2C_SENDRECV, send STOP.

i2c0_SCLi2c0_SDA

example #2 (the slave 0x6c works well)

# irecv -n/dev/i2c4 -a0x6c -l1   // 0110 1100 –> 1101 1001   DCMD_I2C_SENDRECV, send STOP, too.

Data recvd: 15h  // 0001 0101

i2c4_SCLi2c4_SDA

Circuit basics(6):pull up & pull down

The purpose of pull up/down is to insure that a circuit has a default value (high, or low), given no other input:

a pull up resistor (connect to a power supply) pulls the line high;

a pull down resistor(connect to ground) pulls the line down.

    

Pull-up resistor                                    pull-down

http://www.seattlerobotics.org/encoder/mar97/basics.html

 Example
MON_INT_N[2] is connected to OM5_GPIO8_233. INT2 pad can be configured as active high; GPIO is configured as input (internal pull up/pull down disabled; high level detection).
open_drain_2 open_drain_1
 When MON drives 3.3V (high), the downside of R14130, closer to MON, is 2.5V due to 1.8V (R13993) pull-up.
Note: INT2 pad can be configured to open-drain, too. I assume that the “NB” resisters are there, in case the pad is configured as open drain?