Dual Display station - software description
From Zenitel Wiki
Contents
Coding conventions
Software configuration: Using #define settings
By setting or removing #define precompiler statements, pieces of code can be easily added or removed. One example of this is enabling or disabling all debug functionality in a release:
- #define DEBUG_ENABLED
All such statements are collected in the top of the file <main.h>.
Loading C strings from FLASH
The IAR C compiler for AVR processors is by default configured for reading strings from DATA segment (RAM). This requires the compiler to copy the strings from FLASH to RAM during initialisation/cstartup. After that, the stings can be used in normal "ANSI C style":
- printf("Hello World!");
Loading the strings to RAM is required for the strings to be manipulated during run-time. But by using special-designed functions instead of printf(), this can be avoided. Printing strings is done with a separate function, which loads text from FLASH only.
This could be achieved by pre-defining all strings:
- BYTE flash *string_example = "Hello World!";
... and it would be used this way:
- print_string(string_example);
This is not an easy code to read. Instead, the string could be used the normal way by making a few configurations:
- The CSTR segment containing strings is configured as a CODE segment (FLASH) in the linker file <lnk3s4kb_MODIFIED.xcl>.
- The function using the strings must cast the pointer from RAM to FLASH type. This is because the pointer is actually pointing at the correct address in FLASH, but the compiler still believes it is a RAM pointer. For example:
void print_string(BYTE *string_temp)
- {
- BYTE flash *string;
- string = (BYTE flash *)(WORD)string_temp;
- ...
- }
"string_temp" is an invalid RAM pointer, but the FLASH pointer "string" can now be used normally instead.
- In some cases, pre-defined strings are preferred if a string is used multiple times. But now the problem is vice versa: The function is expecting a RAM pointer, but the pointer is in this case actually FLASH. So we have to cast the pointer to RAM to be able to compile the code:
BYTE flash *string_example = "Hello World!"; print_string( (BYTE *)(WORD)string_example );
Code description
General program structure
After initialisation, the program enters an infinite loop in which all processes are executed. Some processes are executed every 10ms, while others run continuously.
To generate the 10ms intervals, TIMER0 interrupt is used. It increments a counter by 1 every 10ms. The main loop checks this counter, and executes the required processes if it is greater than 0. Also, the counter is decremented by 1.
The process states and timers are kept within the separate processes, and no high-level FSM is used. Global status flags are held in a bit-struct register "status".
Keyboard scanning
Located in file: | <main.c> |
Entry function: | scan_matrix_keyboard() |
Hardware
The station keyboard is an 8x4 matrix connected to PB1-PD3 (outputs) and PD4-PD7 (inputs).
Debounce routine
For each of the 32 keys there is a debounce register of 8 bits:
Key status: | 1 bit |
Key counter up: | 3 bits |
Key counter down: | 4 bits |
The keyboard scanning process is executed by 10ms intervals. For a keypress to be accepted, the key must be held for a specified amount of 10ms counts, counted by the up-counter. The key code is then sent to the AlphaCom digit sending process. Similarly, a key release also requires the key to be released for some time, counted by the down-counter. The key code 0x80 is then sent, telling the digit process to stop sending.
Key codes
The 32 keys are mapped this way:
Position | Key name: | Key code: (1) | Comment |
---|---|---|---|
COL1, ROW1 | KEY_1 | 0x01 | |
COL1, ROW2 | KEY_2 | 0x02 | |
COL1, ROW3 | KEY_3 | 0x03 | |
COL1, ROW4 | KEY_MENU | 0x2a | DAK 20 |
COL2, ROW1 | KEY_4 | 0x04 | |
COL2, ROW2 | KEY_5 | 0x05 | |
COL2, ROW3 | KEY_6 | 0x06 | |
COL2, ROW4 | KEY_UPARROW | 0x2b | DAK 21 |
COL3, ROW1 | KEY_7 | 0x07 | |
COL3, ROW2 | KEY_8 | 0x08 | |
COL3, ROW3 | KEY_9 | 0x09 | |
COL3, ROW4 | KEY_DOWNARROW | 0x2c | DAK 22 |
COL4, ROW1 | KEY_M | 0x35 | (2) |
COL4, ROW2 | KEY_0 | 0x0a | |
COL4, ROW3 | KEY_C | 0x5f | (3) |
COL4, ROW4 | KEY_NAME | 0x2d | DAK 23 |
COL5, ROW1 | KEY_DAK1 | Key codes depend on which DAK page is currently displayed. (4) | |
COL5, ROW2 | KEY_DAK2 | " | |
COL5, ROW3 | KEY_DAK3 | " | |
COL5, ROW4 | KEY_DAK4 | " | |
COL6, ROW1 | KEY_DAK5 | " | |
COL6, ROW2 | KEY_DAK6 | " | |
COL6, ROW3 | KEY_DAK7 | " | |
COL6, ROW4 | KEY_DAK8 | " | |
COL7, ROW1 | KEY_DAK9 | " | |
COL7, ROW2 | KEY_DAK10 | " | |
COL7, ROW3 | |||
COL7, ROW4 | KEY_PRIVACY | (No code) | Other signalling. |
COL8, ROW1 | |||
COL8, ROW2 | |||
COL8, ROW3 | |||
COL8, ROW4 |
(1): For the complete list of key codes and their signalling, see chapter [4.3 AlphaCom digit sending].
(2): The M-key code is not sent upon keypress. Instead, the global status "status.mkey" is set. Handset M-key will also set "status.mkey" when pressed.
(3): The C-key code is not sent upon keypress. Instead, the global status "status.ckey" is set.
(4): The DAK key codes depend on which DAK page is currently displayed on the upper LCD. There are 50 DAK key codes available, and by adding a prefix code (0x74) this amount is doubled. The key codes are according to the CRM IV station standard, which defines the following table:
DAK page displayed: | DAK keys: | Key codes: | Comment |
---|---|---|---|
0 | D0-D9 | 0x20-0x29 | |
1 | D10-D19 | 0x0a-0x13 | |
2 | D20-D29 | 0x2a-0x33 | Menu keys |
3 | D30-D39 | 0x60-0x69 | |
4 | D40-D49 | 0x6a-0x73 | |
5 | D50-D59 | (0x74+) 0x20-0x29 | |
6 | D60-D69 | (0x74+) 0x0a-0x13 | |
7 | D70-D79 | (0x74+) 0x2a-0x33 | |
8 | D80-D89 | (0x74+) 0x60-0x69 | |
9 | D90-D99 | (0x74+) 0x6a-0x73 |
Other input pins
There are some extra inputs that are also scanned by the keyboard process:
- PE2: C-D loop sense (conversation/station LED)
- PC1: Handset OFF
- PC0: Handset M-key
AlphaCom digit sending
Located in file: | <main.c> |
Entry functions: <br\> | digit_process()<br\>put_digit_in_buffer() |
Digit transmission is processed by the function "digit_process()". The digits are sent by two signals:
- Tone frequencies
- * Sent on C-D wires (microphone). Polarity is inverted first.
- * Frequency range: 400Hz - 4000Hz.
- Current-signals
- * Sent on A-B wires (speaker).
- * Signal levels: (Note: The station itself consumes 10mA (1) at all times, which is included in the values below)
Handset OFF: 10mA (1) Handset ON: 13mA M-key: 24mA C-key: 50mA
(1): Due to high power consumption, the station is currently set to consume 13mA at all times. This is equal to "Handset ON" signal (13mA), and the "Handset OFF" signal (10mA) can never be signalled. This is configured in hardware, and does not affect the software.
Tone signalling (C-D wires)
Tones are transmitted through the following processor output pins:
- PE2 (TO): Tone frequency signal.
- PB4 (ID): ID signal. Set high when sending tone.
The ID pin is held high when sending a tone. The tone frequency is then set on the TO pin. When not sending a tone, the ID pin is low. The TO pin is then used for reflecting the privacy status of the station, determined by the flag "status.privacy": Low level means Open and high level means Private.
Current signalling (A-B wires)
A current-signal is always present on the A-B wires. The processor has three output lines controlling the current:
- PB7 (CP): C-key current
- PB6 (MP): M-key current
- PB5 (ONH): Handset ON current
In idle, the station signals "Handset ON" current (13mA). By pressing M- or C-key, or by lifting the handset, the current is changed accordingly. In software, this is indicated to <br\>"digit_process()" by the status flags "status.hsoff", "status.mkey" and "status.ckey", and does not require a key code to be processed through the key code buffer (see below).
Only one of the current levels may be signalled at a time, except short periods during transitions between current levels. Because there are several inputs specifying the current level, the final value is determined by the following priority:
- 1. "status.ckey" flag
- 2. Current level specified by a key code - see "Key codes" below.
- 3. "status.mkey" flag
- 4. "status.hsoff" flag (station is in idle, with handset ON or OFF)
Key codes
Key codes are sent through a key code buffer by the function "put_digit_in_buffer()". This buffer is read by "digit_process()", which then sends both tone and current signals according to the key code. Digits will be held for a minimum time, and released when a new key code is available in the buffer. Key codes with the bit "0x80" set will stop the digit transmission.