Showing posts with label Programming. Show all posts
Showing posts with label Programming. Show all posts

2017-04-24

Replace Scilab with Julia?

As a programmer, I usually need to convert numbers between decimal and hexadecimal formats. Sometimes, I also need to define values in hexadecimal format. I have been using Scilab for 10 months. The format conversion issue still let me feel very inconvenient. In Scilab, I have to call dec2hex() and hex2dec() to convert format.

Today, I happen to know a new mathematic programming language, Julia. I think I should give it a try if it can solve my problem. So, I quickly download and install Julia. It only has command line interface. It's fine for me since I always open a console on desktop.


Now, let's try to declare variable in hexadecimal format. Great! It just works like C language.


I also found a very interesting thing. You can declare variable by using most UTF-8 characters and LaTex symbol. See the following example:


This is achieved by typing \mu-TAB and x\_i-TAB = 10. Where -TAB means to press the TAB key. It is really nice, right?

Well, Julia looks like a good mathematic tool for programmers like me. If you also think it is interesting then you should try it. You can get more information from https://julialang.org.

2016-08-20

New Approach to Implement MSP430 Battery Monitor

As we know, MSP430 doesn't have builtin battery monitor. Though the ADC module does have a dedicated channel to measure 1/2 VCC. But there is a critical issue with this approach.
  • The ADC module is usually used by other signal conversion task. It is difficult to switch ADC for channels with different sampling rate. In general, we only need to monitor battery voltage in a very slow frequency, for example, 1Hz. If the ADC module is working at 1KHz for converting other signal, it will be very inefficient to use "repeated-autoscan" mode to do both signal and battery voltage conversion.
So, it will be very useful to have a new approach to monitor battery without using ADC module. In MSP430FR68xx and 69xx series, there is a COMP_E module which is a very flexible voltage comparator. The idea is to utilize COMP_E to compare VCC with a reference voltage.

Well there is already a "shared reference" module which can generate 1.2V, 2.0V or 2.5V. Since MSP430 can work at 1.8V, the best choice of VREF voltage should be 1.2V. This will be the V- terminal of the comparator.

How about VCC input at V+ terminal? The simplest way is to use two resistors to divide VCC. But this method will continue to drain battery power when the system is shutdown. To overcome the side effect, we need to use a switch to turn off the resistor-divider when not in used. That means... we need two external resistors, one external MOSFET as switch and a pinout to control the switch... seems not good idea.

The ultimate solution is to use the resistor-ladder inside COMP_E module. The ladder can be used to divide VCC by 32 steps. So, we can get rough 100mV/step for 3.3V VCC and 62.5mV when VCC drops to 2.0V. The resolution looks good enough for battery monitoring purpose.

In FR68xx/FR69xx, the VREF output pin (VREF+) is the same as C1. C1 is the 2nd comparator input pin. So, we have to set REFOUT=1 so that the 1.2V reference is output at C1 which is the input of V- terminal of comparator amplifier. Then, set CEVREF as the output of VCC through resistor ladder. Let CEVREF outputs to V+ terminal of comparator amplifier.

Here is the code tested on MSP430FR6972IRGC:

void init()
{
    // Suppose we want to monitor if VCC < 2.3V.
    // 2.3V*(16+1)/32 = 1.22V
    // 2.3V*(15+1)/32 = 1.15V
    P1SEL0 |= BIT1;                 // VREF+ & C1 are at the same P1.1
    P1SEL1 |= BIT1;
    REFCTL0 |= REFOUT;              // Enable REFOUT. d
    CECTL0 = CEIMSEL_1|CEIMEN;      // C1 is set to V- terminal
    CECTL1 = CEPWRMD_1|CEFDLY_2;
    CECTL2 = CERS_1|CEREF1_16|CEREF0_16;    // CEREF(VCC) is set to V+ terminal
}

int checkVoltage()
{
    CECTL1 |= CEON;                 // Enable COMPE_E
    delay_us(1);                    // Wait for filter circuit to be stable
    int ceout = CECTL1 & CEOUT;     // Get result
    CECTL1 &= ~CEON;                // Disable COMP_E
    return ceout;
}
Now, we only need to call checkVoltage() once per second. It returns 1 if voltage is VCC > 2.3V and 0 if VCC < 2.3V. It is very easy to modify the above code to get the rough battery voltage by testing 32 ladder steps.

2016-05-04

C++ Reference Type

Compared to C, the new reference type in C++ make more efficient memory usage.

A reference of a variable/object is just like an alias name of the variable/object. Some newbies from C to C++ usually get confused because they used to think it as a pointer. So, they may ask questions like "Can I initialize a reference to NULL value?"

Well, for a reference of a integer variable. You initialise it to NULL means the original variable is assigned with 0. This seems meaningful. But how about an object? Assigning a NULL value to an object? It doesn't make sense.

To get the reference of a variable/object, use syntax "&". For example,
int x;
int& r = x;
r = 10;
Now r is a reference of x. "r = 10" is the same as "x = 10".

Call by Reference


When passing arguments to a function, in order to optimize memory usage, we can use pointer in C. But in C++, a better way could be Call by Reference. For example, if we want to swap the contents of two variables. In C, we can do:
void swap(int* x, int* y)
{
    int t;
    t = *x;
    *x = *y;
    *y = t;
}
But in C++:
void swap(int& x, int& y)
{
    int t;
    t = x;
    x = y;
    y = t;
}
It looks more clear and elegant, right?

2016-04-19

How Microsoft Make the World a Worse Place

"Microsoft has no taste" - Steve Jobs, 1995.

The computer world began from Unix. BSD, Linux and Mac can be considered as Unix-like. The opposite side is Windows, created by M$. Yes, money is major stuff Microsoft care about, instead of making the world a better place.

Originally, the computer operating environment is simple and unified. Directory root is "/". A path looks like /bin/program/subfolder. But Windows changed that. As you can see, disk is something like C:. Path is combined with "\" instead of "/". A full path is something like "C:\Programs\xxx\yyy" The worse part is: nothing is unified in Windows. For example, the network path becomes something like "\\myserver\xxx", no more something like "C:".

The ending of a string line is LF (line feed) in Unix. Microsoft changed that to LF+CR (Carry Return). If you have a file used in Linux or Mac and your colleague edits it in Windows. All LF characters might be converted to LF+CR. Then, it will make you crazy when you commit the file to Git or other version control systems. Git will tell you every single line is modified.

The issue of character encoding is the worse part. For internationalization and localization, now we have various Unicode encoding formats such as UTF-8, UTF-16 and UTF-32. Guess what Microsoft use? The answer is none! Microsoft uses wide character type (LPWSTR) which is two bytes per character. LPWSTR and UTF-16 are different things. UTF-16 is variable-length encoding while LPWSTR is fixed as two bytes.


Wide character (wchar_t or LPWSTR) can't represent all possible glyphs of languages in the world. So, Microsoft used something called "Code Page". In Windows, you have to set a Code Page for all the programs to correctly interpret the characters in a string. This is caused by WinAPIs that process string encoding based on system Code Page.

There isn't Code Page issue in Unix-like OSes. Both Linux and Mac OS X well support unicode encoding, especially UTF-8. UTF-8 is widely used in internet formats such as HTML, XML. An UTF-8 code unit is 8 bits (1 byte). So the type is char* in C or std:string in C++. For example, when opening a file by calling fopen(). The type of the path argument is char* and the API knows how to deal with it when it contains UTF-8 encoded characters. As you can see, the usage is unified, simple and easy to use no matter the input string is ASCII or UTF-8 encoded.

In summary, M$ have successfully make the computer world a worse place in the past two decades.

2016-04-12

UTF-8 Character in C

Talking about UNICODE, UTF-8 format is widely used. But there is not native supporting in C language. So, how to deal with it?

For C programming, we usually use one byte as a character (type char). UTF-8 is variable-length encoding, it also includes the ASCII characters from 0x00 to 0x7F. This feature makes it the best solution to be compatible with traditional text files.

In fact, the process is pretty simple. If we can convert variable-length encoding to fixed-length then it can be easily used by font rendering system. Here is my method to get an UTF-8 character from a string and converted as 32 bits UNICODE encoding.
uint32_t utf8_getc(char* s, char** sp)
{
    uint32_t unicode = 0;

    if((*s & 0x80) == 0){
        // ASCII code 0x00 - 0x7F
        uint32_t b0 = *s++;
        unicode = b0;
    }else if((*s & 0xE0) == 0xC0){
        // 2 bytes
        uint32_t b1 = (*s++ & 0x1F);
        uint32_t b0 = (*s++ & 0x3F);
        unicode = b0 | (b0<<6);
    }else if((*s & 0xF0) == 0xE0){
        // 3 bytes
        uint32_t b2 = (*s++ & 0x1F);
        uint32_t b1 = (*s++ & 0x3F);
        uint32_t b0 = (*s++ & 0x3F);
        unicode = b0 | (b1<<6) | (b2<<12);
    }else if((*s & 0xF8) == 0xF0){
        // 4 bytes
        uint32_t b3 = (*s++ & 0x1F);
        uint32_t b2 = (*s++ & 0x3F);
        uint32_t b1 = (*s++ & 0x3F);
        uint32_t b0 = (*s++ & 0x3F);
        unicode = b0 | (b1<<6) | (b2<<12) | (b3<<18);
    }else if((*s & 0xFC) == 0xF8){
        // 5 bytes
        uint32_t b4 = (*s++ & 0x1F);
        uint32_t b3 = (*s++ & 0x3F);
        uint32_t b2 = (*s++ & 0x3F);
        uint32_t b1 = (*s++ & 0x3F);
        uint32_t b0 = (*s++ & 0x3F);
        unicode = b0 | (b1<<6) | (b2<<12) | (b3<<18) | (b4<<24);
    }else if((*s & 0xFE) == 0xFC){
        // 6 bytes
        uint32_t b5 = (*s++ & 0x1F);
        uint32_t b4 = (*s++ & 0x3F);
        uint32_t b3 = (*s++ & 0x3F);
        uint32_t b2 = (*s++ & 0x3F);
        uint32_t b1 = (*s++ & 0x3F);
        uint32_t b0 = (*s++ & 0x3F);
        unicode = b0 | (b1<<6) | (b2<<12) | (b3<<18) | (b4<<24) | (b5<<30);
    }else{
        return 0;
    }

    if(sp)
        *sp = s;

    return unicode;
}
Where, s is pointing to the string with UTF-8 characters. And sp is the address of s i.e. sp points to s. Well, by updating sp to the next UTF-8 location, this routine can be used in a loop to process the whole string.

2016-03-30

Integer Data Types in C/C++

You may have the same experience, programmers usually define different type names for the same data type. For example, a 16 bits unsigned integer could be defined as UINT16, UINT16_T or uint16. This is confusing and making things complicated. After struggling for so many years, I decide to use the standard types defined in stdint.h.
int8_t
uint8_t
int16_t
uint16_t
int32_t
uint32_t
int64_t
uint64_t
A good C/C++ compiler should has defined stdint.h in RTS (run-time support) library based on the target CPU model.

Btw, it is also good to have unified syntax for all program codes. For example, a user defined type should always be ended with "_t".
typedef struct{
    uint8_t  x;
    uint8_t  y;
}mytype_t;
Where mytype_t is ended with "_t" to indicate that it is a user defined data type.

2016-03-29

Add Syntax Highlighting

Sometimes, it is necessary to share code on this website, so I add syntax highlighting capability by using SyntaxHighlighter. SyntaxHighlighter is capable of dealing with lots of programming languages, including my favourites C and C++.

Here is an example of C++ code snippet with line 27 highlighted.
#include &ltpos.h>

typedef enum
{
    S_SLEEP = 0,
    S_POWER,    // Normal POWER METER mode
    S_ENGINEER, // Engineering mode
    S_FACTORY,  // Engineering FACTORY mode
    S_AGENCY,   // Engineering AGENCY mode
    S_REPORT    // Engineering REPORT mode
}state_t;

class State: public pos::Task
{
private:
    state_t _state;
    state_t _toState;

    void gotoSleep(void);

public:
    int  idle_timer;

    static bool create();
    State();
    ~State() {}
    void run();
    state_t getState() { return _state; }
    void toState(state_t s) { _toState = s; }
};