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 big endian, you store the most significant byte in the smallest address.

90 AB 12 CD

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

 

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

CD 12 AB 90

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

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 basics

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

  • Usually, the slaves are in the idle condition, monitoring the SDA and SCLK lines for the start condition and the correct transmitted address.
  • the clock signal is always generated by the current bus master.
  • start condition: a high to low transition on SDA while SCL remains high, then pulls the SCL low.
  • Following the start condition, the address bits are transferred from MSB down to LSB.
    • A logic 0 on the LSB of the first byte means a “write”; a logic 1 means a “read”.
    • The  first 8 clock cycles are used for slave address, then the 9th is for slave ACK.
    • The slave, if it recognizes its address, it responds by pulling the data line low during the ninth clock pulse – this is known as an acknowledge bit.
  • A data transfer is terminated by a stop condition: a low to high transition on SDA, while SCL is high.

data-transfer

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