Files
2017-05-07 20:48:04 -04:00

1541 lines
36 KiB
C

//////////////////////////////////////////////////////////////////////////////
//
// Filename: Command.c
// Version:
// Data:
//
// Author: Liu, Zemin
// Company: JYE Tech
//
//-----------------------------------------------------------------------------
//
// Target: STM32F103C8
// Tool chain: CodeSourcery G++
//
//-----------------------------------------------------------------------------
// Required files:
//
//-----------------------------------------------------------------------------
// Notes:
//
//
//-----------------------------------------------------------------------------
// Revision History:
//
///////////////////////////////////////////////////////////////////////////////
#include "stm32f10x.h"
#include "stm32f10x_conf.h"
#include "Common.h"
#include "Board.h"
#include "Command.h"
#include "libdso150.h"
#include "Screen.h"
#include "Eeprom.h"
// ===========================================================
// File Scope Global variables
// ===========================================================
//
const KeyScanCode KScanCodeTab[19] = {
// scan code key code key param
{0xFFEF, KC_SW1, '1'}, // 1
{0xFFDF, KC_SW2, '2'}, // 2
{0xFFBF, KC_SW3, '3'}, // 3
{0xFF7F, KC_SW4, '4'}, // 4
{0xFFEE, KC_SW1H, '5'}, // 5
{0xFFDE, KC_SW2H, '6'}, // 6
{0xFFBE, KC_SW3H, '7'}, // 7
{0xFF7E, KC_SW4H, '8'}, // 8
{0xFF6F, KC_SW1_SW4, 'A'}, // 8
{0xFF5F, KC_SW2_SW4, 'B'}, // 8
{0xFF3F, KC_SW3_SW4, 'C'}, // 8
{0xFF9E, KC_SW2_SW3H, 'D'}, // 8
{0xFFF7, KC_ENC_PB, 'P'}, // 8
{0xFFF6, KC_ENC_PBH, 'Q'}, // 8
{0xFFE7, KC_PB_SW1, 'a'}, // 8
{0xFFD7, KC_PB_SW2, 'b'}, // 8
{0xFFB7, KC_PB_SW3, 'c'}, // 8
{0xFF77, KC_PB_SW4, 'd'}, // 8
{0, 0, 0}
};
const U8 enc_table[16] = {
0, // 0000
1, // 0001
-1, // 0010
0, // 0011
0, // 0100
0, // 0101
0, // 0110
0, // 0111
0, // 1000
0, // 1001
0, // 1010
0, // 1011
0, // 1100
-1, // 1101
1, // 1110
0 // 1111
};
U16 AddOns; // Added features
KEYPAD Keypad;
U8 VSenPrev, CplPrev;
S16 Vmax, Vmin, Vavr, Vpp, Vrms;
float Freq;
float Cycle;
float PW;
float Duty;
U16 FreqUnit;
U16 CycleUnit;
U16 FreqStrLen = 0;
U16 CycleStrLen = 0;
U16 PwStrLen = 0;
U8 ReadingStr[22];
U16 Flags;
const float UnitRate[12] = {
1000.0, // VS_20V,
500.0, // VS_10V,
250.0, // VS_5V,
100.0, // VS_2V,
50.0, // VS_1V,
25.0, // VS_05V,
10.0, // VS_02V,
5.0, // VS_01V,
2.5, // VS_50mV,
1.0, // VS_20mV,
0.5, // VS_10mV,
0.25 // VS_5mV
};
const float SampleRate[TBMax - TBMin + 1] = {
(25.0/500.0), // TB_500s, // 20s
(25.0/200.0), // TB_200s, // 8s
(25.0/100.0), // TB_100s, // 4s
(25.0/50.0), // TB_50s, // 2s
(25.0/20.0), // TB_20s, // 1.25Hz 800ms
(25.0/10.0), // TB_10s, // 2.5Hz 400ms
(25.0/5.0), // TB_5s, // 5Hz 200ms
(25.0/2.0), // TB_2s, // 12.5Hz 80ms
(25.0/1.0), // TB_1s, // 25Hz 40ms
(25.0/0.5), // TB_05s, // 50Hz 20ms
(25.0/0.2), // TB_02s, // 125Hz 8ms
(25.0/0.1), // TB_01s, // 250Hz 4ms
(25.0/50.0) * 1000.0, // TB_50ms, // 500Hz 2ms
(25.0/20.0) * 1000.0, // TB_20ms, // 1.25K 800us
(25.0/10.0) * 1000.0, // TB_10ms, // 2.5K 400us
(25.0/5.0) * 1000.0, // TB_5ms, // 5K 200us
(25.0/2.0) * 1000.0, // TB_2ms, // 12.5K 80us
(25.0/1.0) * 1000.0, // TB_1ms, // 25K 40us
(25.0/0.5) * 1000.0, // TB_05ms, // 50K 20us
(25.0/0.2) * 1000.0, // TB_02ms, // 125K 8us
(25.0/0.1) * 1000.0, // TB_01ms, // 250K 4us
(25.0/50.0) * 1000000.0, // TB_50us, // 500K 2us
(25.0/20.0) * 1000000.0, // TB_20us, // 1.25M 0.8us **** used 1M instead. Interpolation is required to make 1.25M
(25.0/10.0) * 1000000.0, // TB_10us, // 2.5M 0.4us **** use Dual ADC mode to achieve 2M. Interpolation for 2.5M
// 16 // TB_5us, // 5M 0.2us
// 200, // TB_2us, //
// 200, // TB_1us,
// 200 // TB_05us,
// 200 // TB_max
};
U8 *UnitsStr[8] = {
(U8 *)"Hz",
(U8 *)"KHz",
(U8 *)"MHz",
(U8 *)"GHz",
(U8 *)"s",
(U8 *)"ms",
(U8 *)"us",
(U8 *)"ns"
};
// ===========================================================
// Function Definitions
// ===========================================================
//
void AppInit()
{
U16 tmp0;
// =============================
// Check EEPROM for valid settings
EE_ReadVariable(Addr_SettingStatus, &tmp0);
if(tmp0 == SettingStatus_Initialized) {
// Load saved settings
EE_ReadVariable(VirtAddVarTab[Addr_Vpos], &tmp0);
SetVPos(tmp0);
EE_ReadVariable(VirtAddVarTab[Addr_Vsen], &tmp0);
SetVSen(tmp0);
EE_ReadVariable(VirtAddVarTab[Addr_Cpl], &tmp0);
SetCpl(tmp0);
EE_ReadVariable(VirtAddVarTab[Addr_TimeBase], &tmp0);
SetTimeBase(tmp0);
EE_ReadVariable(VirtAddVarTab[Addr_TrigMode], &tmp0);
SetTrigMode(tmp0);
EE_ReadVariable(VirtAddVarTab[Addr_TrigEdge], &tmp0);
SetTrigEdge(tmp0);
EE_ReadVariable(VirtAddVarTab[Addr_TrigLvl], &tmp0);
SetTrigLvl(tmp0);
EE_ReadVariable(VirtAddVarTab[Addr_RecLen], &tmp0);
SetRecLen(tmp0);
EE_ReadVariable(VirtAddVarTab[Addr_HPos], &tmp0);
SetHPos(tmp0);
// EE_ReadVariable(VirtAddVarTab[Addr_VPosOfs], &tmp0);
EE_ReadVariable(VirtAddVarTab[Addr_VPosOfs00 + (GetVSen() - VSenMin)], &tmp0);
SetVPosOfs(tmp0);
EE_ReadVariable(VirtAddVarTab[Addr_AddOns], &AddOns);
}
else {
// Load default settings and initialize EEPROM
LoadDefault();
// Mark down EEPROM has been initialized
EE_WriteVariable(VirtAddVarTab[Addr_SettingStatus], SettingStatus_Initialized);
}
SetTrigPos(GetRecLen()/2);
SetTrigSen(10);
OutputVSen();
// =============================
// Note: DSO_Init() must be executed for proper running of the capture engine
DSO_Init();
// Misc initialization
TimerKeyScan = 1;
Keypad.KDebounceVal = KD_val;
FreqStrLen = 0;
CycleStrLen = 0;
PwStrLen = 0;
Flags = 0;
}
void LoadDefault(void)
{
U8 tmp0;
SetVPos(0);
EE_WriteVariable(VirtAddVarTab[Addr_Vpos], 0);
SetVSen(VS_05V);
EE_WriteVariable(VirtAddVarTab[Addr_Vsen], VS_05V);
SetCpl(CP_DC);
EE_WriteVariable(VirtAddVarTab[Addr_Cpl], CP_DC);
SetTimeBase(TB_1ms);
EE_WriteVariable(VirtAddVarTab[Addr_TimeBase], TB_1ms);
SetTrigMode(TM_Auto);
EE_WriteVariable(VirtAddVarTab[Addr_TrigMode], TM_Auto);
SetTrigEdge(TE_Falling);
EE_WriteVariable(VirtAddVarTab[Addr_TrigEdge], TE_Falling);
SetTrigLvl(0);
EE_WriteVariable(VirtAddVarTab[Addr_TrigLvl], 0);
SetRecLen(SampleBufSizeMax);
EE_WriteVariable(VirtAddVarTab[Addr_RecLen], SampleBufSizeMax);
SetHPos(GetRecLen()/2 - WDsize/2);
EE_WriteVariable(VirtAddVarTab[Addr_HPos], GetRecLen()/2 - WDsize/2);
SetVPosOfs(0);
EE_WriteVariable(VirtAddVarTab[Addr_VPosOfs], 0);
EE_WriteVariable(VirtAddVarTab[Addr_AddOns], 0);
tmp0 = VSenMin;
while(tmp0 <= VSenMax) {
EE_WriteVariable(VirtAddVarTab[Addr_VPosOfs00 + (tmp0 - VSenMin)], 0);
tmp0++;
}
}
void KeyProc(void)
{
switch(Keypad.KeyCode) {
case KC_SW1:
SelVertical();
default:
break;
case KC_SW2:
SelHorzontal();
break;
case KC_SW3:
SelTrigger();
break;
case KC_ENC_CW:
DoKeyInc();
break;
case KC_ENC_CCW:
DoKeyDec();
break;
case KC_SW4:
DoKeyOk();
break;
case KC_SW1H:
VPosAlign();
break;
case KC_SW2H:
CenterHPos();
break;
case KC_SW3H:
CenterTrigLevel();
break;
case KC_SW4H:
DoKeyOkH();
break;
case KC_PB_SW2:
SaveWaveform();
break;
case KC_PB_SW3:
LoadWaveform();
break;
case KC_SW2_SW3H:
LoadDefault();
GTimeout = 1;
case KC_ENC_PB:
if(BitTest(AddOns, (1 << AO_TestSigAmpDisp))) {
// Toggle amp
BitXor(AddOns, (1 << AO_TestSigAmp));
if(BitTest(AddOns, (1 << AO_TestSigAmp))) {
// Set to 0.1V
GPIOB->CRH = ((GPIO_CNF_AF_PP | GPIO_Mode_Out50M) << ((8 - 8)*4)) // Output, Trigger level
|((GPIO_CNF_GP_PP | GPIO_Mode_Out50M) << ((9 - 8)*4)) // Output, TFT_nRESET
|((GPIO_CNF_GP_PP | GPIO_Mode_Out50M) << ((10 - 8)*4)) // SCL
|((GPIO_CNF_Floating | GPIO_Mode_In) << ((11 - 8)*4)) // SDA
|((GPIO_CNF_GP_PP | GPIO_Mode_Out50M) << ((12 - 8)*4)) // AMPSEL (for test signal)
|((GPIO_CNF_IPU | GPIO_Mode_In) << ((13 - 8)*4)) //
|((GPIO_CNF_IPU | GPIO_Mode_In) << ((14 - 8)*4)) //
|((GPIO_CNF_IPU | GPIO_Mode_In) << ((15 - 8)*4)); //
// GPIOB->ODR &= ~(1 << 12);
Port_BitClr(GPIOB, (1 << 12));
}
else {
// Set to 3.3V
GPIOB->CRH = ((GPIO_CNF_AF_PP | GPIO_Mode_Out50M) << ((8 - 8)*4)) // Output, Trigger level
|((GPIO_CNF_GP_PP | GPIO_Mode_Out50M) << ((9 - 8)*4)) // Output, TFT_nRESET
|((GPIO_CNF_GP_PP | GPIO_Mode_Out50M) << ((10 - 8)*4)) // SCL
|((GPIO_CNF_Floating | GPIO_Mode_In) << ((11 - 8)*4)) // SDA
|((GPIO_CNF_Floating | GPIO_Mode_In) << ((12 - 8)*4)) // AMPSEL (for test signal)
|((GPIO_CNF_IPU | GPIO_Mode_In) << ((13 - 8)*4)) //
|((GPIO_CNF_IPU | GPIO_Mode_In) << ((14 - 8)*4)) //
|((GPIO_CNF_IPU | GPIO_Mode_In) << ((15 - 8)*4)); //
}
}
break;
case KC_ENC_PBH:
BitXor(AddOns, (1 << AO_TestSigAmpDisp));
if(!BitTest(AddOns, (1 << AO_TestSigAmpDisp))) {
// Clear amp display
FillRect(TestSigAmpx0, TestSigAmpy0, 14 * 8, 16, clBlack);
}
break;
}
}
void KeyScan(void)
{
U16 tmp1, tmp4;
U8 tmp2, tmp3;
Keypad.KScanBuf = NoKey;
// Set keypad ports to input with pull-ups
GPIOB->CRL = ((GPIO_CNF_IPU | GPIO_Mode_In) << (0*4)) // TFT port - D0, ENC_A
|((GPIO_CNF_IPU | GPIO_Mode_In) << (1*4)) // TFT port - D1, ENC_B
|((GPIO_CNF_IPU | GPIO_Mode_In) << (2*4)) // TFT port - D2
|((GPIO_CNF_IPU | GPIO_Mode_In) << (3*4)) // TFT port - D3, ENC_PB
|((GPIO_CNF_IPU | GPIO_Mode_In) << (4*4)) // TFT port - D4, SW2
|((GPIO_CNF_IPU | GPIO_Mode_In) << (5*4)) // TFT port - D5, SW3
|((GPIO_CNF_IPU | GPIO_Mode_In) << (6*4)) // TFT port - D6, SW4
|((GPIO_CNF_IPU | GPIO_Mode_In) << (7*4)); // TFT port - D7, SW5
Delay(10);
// Read buttons
tmp1 = (PB_Port & PB_Bits) | ~PB_Bits;
tmp4 = ENC_Port & ENC_Bits;
// Restore
GPIOB->CRL = ((GPIO_CNF_GP_PP | GPIO_Mode_Out50M) << (0*4)) // TFT port - D0, ENC_A
|((GPIO_CNF_GP_PP | GPIO_Mode_Out50M) << (1*4)) // TFT port - D1, ENC_B
|((GPIO_CNF_GP_PP | GPIO_Mode_Out50M) << (2*4)) // TFT port - D2
|((GPIO_CNF_GP_PP | GPIO_Mode_Out50M) << (3*4)) // TFT port - D3, ENC_PB
|((GPIO_CNF_GP_PP | GPIO_Mode_Out50M) << (4*4)) // TFT port - D4, SW2
|((GPIO_CNF_GP_PP | GPIO_Mode_Out50M) << (5*4)) // TFT port - D5, SW3
|((GPIO_CNF_GP_PP | GPIO_Mode_Out50M) << (6*4)) // TFT port - D6, SW4
|((GPIO_CNF_GP_PP | GPIO_Mode_Out50M) << (7*4)); // TFT port - D7, SW5
if(tmp1 != NoKey) {
BitSet(Keypad.Flags, (1 << KF_KeyHit));
Keypad.KScanBuf = tmp1;
}
// -- Debouncing
if((Keypad.KScanBuf == NoKey) || (Keypad.KScanBuf != Keypad.KScanCode)) {
if(BitSet(Keypad.Flags, (1 << KF_PressDetected))) {
KeyConvert((KeyScanCode *)KScanCodeTab, Keypad.KScanCode);
BitClr(Keypad.Flags, (1 << KF_PressDetected));
}
Keypad.KScanCode = Keypad.KScanBuf;
Keypad.KCount = 0;
Keypad.KHCount = 0;
Keypad.KTimeChk = KH_val;
BitClr(Keypad.Flags, (1 << KF_HoldDetected));
}
else {
Keypad.KCount++;
if(Keypad.KCount > Keypad.KDebounceVal) {
if(Keypad.KCount == Keypad.KDebounceVal + 3) {
Keypad.KCount = Keypad.KDebounceVal;
if(++Keypad.KHCount == Keypad.KTimeChk) {
// Key hold detected
BitClr(Keypad.Flags, (1 << KF_PressDetected));
if(!BitTest(Keypad.Flags, (1 << KF_HoldDetected))) {
KeyConvert((KeyScanCode *)KScanCodeTab, Keypad.KScanCode & 0xFFFE);
BitSet(Keypad.Flags, (1 << KF_HoldDetected));
Keypad.KTimeChk += 20;
}
else {
KeyConvert((KeyScanCode *)KScanCodeTab, Keypad.KScanCode);
// Change KTimeChk for key repeat
Keypad.KTimeChk += KR_Time;
}
}
}
}
else if(Keypad.KCount == Keypad.KDebounceVal) {
// Key push detected
BitSet(Keypad.Flags, (1 << KF_PressDetected));
}
}
// Process encoder
Keypad.EncState <<= 2;
Keypad.EncState |= tmp4;
tmp1 = enc_table[Keypad.EncState & 0x000F];
if(tmp1 == 1) {
Keypad.KeyCode = KC_ENC_CCW;
Keypad.KeyParam = 0;
}
if(tmp1 == 0xFF) {
Keypad.KeyCode = KC_ENC_CW;
Keypad.KeyParam = 1;
}
#define Threshold_High 0x0900
#define Threshold_Low 0x0300
// Read switch Cpl
tmp1 = ADC_Poll(ADC2, 5);
tmp2 = 0;
if(tmp1 > Threshold_High) {
tmp2 = 1;
}
else if(tmp1 < Threshold_Low) {
tmp2 = 2;
}
// Determine Cpl setting
if(tmp2 != CplPrev) {
SetCpl(tmp2);
CplPrev = tmp2;
UpdateDisp(Disp_Param);
}
}
void KeyConvert(KeyScanCode *KSCTab, U16 KSCode)
{
U16 tmp1;
while((tmp1 = *(U16 *)(KSCTab + 0))) {
if(tmp1 == KSCode) {
// -- Match found
Keypad.KeyCode = *(U8 *)((U8 *)KSCTab + 2);
Keypad.KeyCodeBuf = Keypad.KeyCode;
Keypad.KeyParam = *(U8 *)((U8 *)KSCTab + 3);
return;
}
else {
// -- Proceed to next entry
KSCTab = (KeyScanCode *)((U8 *)KSCTab + sizeof(KeyScanCode));
}
}
}
void DoKeyOk(void)
{
U8 tmp;
U8 tmpbuf[33];
U32 tmp1;
tmp = GetDsoStatus();
// Toggle HOLD state
BitXor(tmp, DSO_Hold);
if(BitTest(tmp, DSO_Hold)) {
// Set HOLD
SetHold();
// Stop capture
StopCapture();
}
else {
// Clear HOLD
ClrHold();
// Start capture at exit of HOLD
StartCapture();
}
UpdateDisp(Disp_Param);
}
void DoKeyInc(void)
{
S8 tmp0;
S16 tmp1;
switch(GetFocus()) {
case FC_VSen:
tmp0 = GetVSen();
tmp0++;
tmp0 = SetVSen(tmp0);
OutputVSen();
EE_WriteVariable(VirtAddVarTab[Addr_Vsen], tmp0);
// Update VPos offset
EE_ReadVariable(VirtAddVarTab[Addr_VPosOfs00 + (tmp0 - VSenMin)], &tmp1);
SetVPosOfs(tmp1);
break;
case FC_Timebase:
tmp0 = GetTimebase();
tmp0++;
tmp0 = SetTimeBase(tmp0);
EE_WriteVariable(VirtAddVarTab[Addr_TimeBase], tmp0);
if(tmp0 >= TB_20ms) {
// Restart capture
StartCapture();
}
else {
// Change sampling rate only
UpdateTimebase();
}
// Make key debounce time shorter for these TB's
if((tmp0 < TB_20ms) && (tmp0 > TB_1s)) {
Keypad.KDebounceVal = KD_val1;
}
else {
Keypad.KDebounceVal = KD_val;
}
default:
break;
case FC_TrigMode:
tmp0 = GetTrigMode();
tmp0++;
tmp0 = SetTrigMode(tmp0);
EE_WriteVariable(VirtAddVarTab[Addr_TrigMode], tmp0);
// Restart capture.
StartCapture();
break;
case FC_TrigEdge:
tmp0 = GetTrigEdge();
tmp0++;
tmp0 = SetTrigEdge(tmp0);
EE_WriteVariable(VirtAddVarTab[Addr_TrigEdge], tmp0);
break;
case FC_VPos:
tmp1 = GetVPos();
tmp1++;
tmp1 = SetVPos(tmp1);
EE_WriteVariable(VirtAddVarTab[Addr_Vpos], tmp1);
UpdateDisp(Disp_Trace);
break;
case FC_TrigLvl:
tmp1 = GetTrigLvl();
tmp1 += 2;
tmp1 = SetTrigLvl(tmp1);
EE_WriteVariable(VirtAddVarTab[Addr_TrigLvl], tmp1);
// OutputTLvl();
break;
case FC_HPos:
// Move waveform right
tmp1 = GetHPos();
tmp1--;
tmp1 = SetHPos(tmp1);
EE_WriteVariable(VirtAddVarTab[Addr_HPos], tmp1);
UpdateDisp(Disp_Trace);
break;
}
UpdateDisp(Disp_Param);
}
void DoKeyDec(void)
{
S8 tmp0;
S16 tmp1;
switch(GetFocus()) {
case FC_VSen:
tmp0 = GetVSen();
tmp0--;
tmp0 = SetVSen(tmp0);
OutputVSen();
EE_WriteVariable(VirtAddVarTab[Addr_Vsen], tmp0);
// Update VPos offset
EE_ReadVariable(VirtAddVarTab[Addr_VPosOfs00 + (tmp0 - VSenMin)], &tmp1);
SetVPosOfs(tmp1);
break;
case FC_Timebase:
tmp0 = GetTimebase();
tmp0--;
tmp0 = SetTimeBase(tmp0);
EE_WriteVariable(VirtAddVarTab[Addr_TimeBase], tmp0);
if(tmp0 >= TB_50ms) {
// Restart capture
StartCapture();
}
else {
// Change sampling rate only
UpdateTimebase();
}
default:
break;
case FC_TrigMode:
tmp0 = GetTrigMode();
tmp0--;
tmp0 = SetTrigMode(tmp0);
EE_WriteVariable(VirtAddVarTab[Addr_TrigMode], tmp0);
// Restart capture.
StartCapture();
break;
case FC_TrigEdge:
tmp0 = GetTrigEdge();
tmp0--;
tmp0 = SetTrigEdge(tmp0);
EE_WriteVariable(VirtAddVarTab[Addr_TrigEdge], tmp0);
break;
case FC_VPos:
tmp1 = GetVPos();
tmp1--;
tmp1 = SetVPos(tmp1);
EE_WriteVariable(VirtAddVarTab[Addr_Vpos], tmp1);
UpdateDisp(Disp_Trace);
break;
case FC_TrigLvl:
tmp1 = GetTrigLvl();
tmp1 -= 2;
tmp1 = SetTrigLvl(tmp1);
EE_WriteVariable(VirtAddVarTab[Addr_TrigLvl], tmp1);
// OutputTLvl();
break;
case FC_HPos:
// Move waveform left
tmp1 = GetHPos();
tmp1++;
tmp1 = SetHPos(tmp1);
EE_WriteVariable(VirtAddVarTab[Addr_HPos], tmp1);
UpdateDisp(Disp_Trace);
break;
}
UpdateDisp(Disp_Param);
}
void DoKeyOkH(void)
{
BitXor(AddOns, (1 << AO_MeasurementOn));
EE_WriteVariable(VirtAddVarTab[Addr_AddOns], AddOns);
if(!BitTest(AddOns, (1 << AO_MeasurementOn))) {
// Clear
FillRect(VoltageReadingx0, VoltageReadingy0, 100, 80, clBlack);
FillRect(FreqReadingx0, FreqReadingy0, 140, 70, clBlack);
}
}
void DoKeyIncH(void)
{
S16 tmp1;
switch(GetFocus()) {
case FC_VPos:
tmp1 = GetVPos();
tmp1 += 10;
SetVPos(tmp1);
break;
case FC_TrigLvl:
tmp1 = GetTrigLvl();
tmp1 += 40;
SetTrigLvl(tmp1);
break;
case FC_HPos:
// Move waveform right
tmp1 = GetHPos();
tmp1 -= 20;
SetHPos(tmp1);
break;
}
UpdateDisp(Disp_Param);
}
void DoKeyDecH(void)
{
S16 tmp1;
switch(GetFocus()) {
case FC_VPos:
tmp1 = GetVPos();
tmp1 -= 10;
SetVPos(tmp1);
break;
case FC_TrigLvl:
tmp1 = GetTrigLvl();
tmp1 -= 40;
SetTrigLvl(tmp1);
break;
case FC_HPos:
// Move waveform left
tmp1 = GetHPos();
tmp1 += 20;
SetHPos(tmp1);
break;
}
UpdateDisp(Disp_Param);
}
void DoKeySelH(void)
{
}
void SelVertical(void)
{
U8 tmp;
tmp = GetFocus();
if(tmp != FC_VSen) {
tmp = FC_VSen;
}
else {
tmp = FC_VPos;
}
SetFocus(tmp);
UpdateDisp(Disp_Param);
}
void SelHorzontal(void)
{
U8 tmp;
tmp = GetFocus();
if(tmp != FC_Timebase) {
tmp = FC_Timebase;
}
else {
tmp = FC_HPos;
}
SetFocus(tmp);
UpdateDisp(Disp_Param);
}
void SelTrigger(void)
{
U8 tmp;
tmp = GetFocus();
switch(tmp) {
case FC_TrigMode:
tmp = FC_TrigLvl;
break;
case FC_TrigEdge:
tmp = FC_TrigMode;
break;
case FC_TrigLvl:
tmp = FC_TrigEdge;
break;
default:
tmp = FC_TrigMode;
}
SetFocus(tmp);
UpdateDisp(Disp_Param);
}
void VPosAlign(void)
{
S16 tmp1;
S8 tmp2, tmp3, tmp4, tmp5;
// Do VPos alignment
// Set timebase to 1ms so as the calibration won't take too long
// Set trigger mode to AUTO
tmp3 = GetTimebase();
tmp4 = GetVSen();
tmp5 = GetTrigMode();
SetTimeBase(TB_1ms);
SetTrigMode(TM_Auto);
// Make sure the current VPosOfs is zero
SetVPosOfs(0);
// Do alignment for each VSen setting
tmp2 = VSenMin;
while(tmp2 <= VSenMax) {
SetVSen(tmp2);
OutputVSen();
Delay(60000);
StartCapture();
tmp1 = 0;
while(!BitTest(tmp1, DSO_CaptureDone)) {
tmp1 = GetDsoStatus();
}
tmp1 = (S16)(GetAverage() - SampleMidValue);
EE_WriteVariable(VirtAddVarTab[Addr_VPosOfs00 + (tmp2 - VSenMin)], tmp1);
tmp2++;
}
// Restore settings
SetTimeBase(tmp3);
SetVSen(tmp4);
SetTrigMode(tmp5);
OutputVSen();
// Setup current VPos offset
EE_ReadVariable(VirtAddVarTab[Addr_VPosOfs00 + (tmp4 - VSenMin)], &tmp1);
SetVPosOfs(tmp1);
}
void CenterHPos(void)
{
// Set HPos to center
SetHPos((GetRecLen() - WWindowSizex)/2);
UpdateDisp(Disp_Param);
}
void CenterTrigLevel(void)
{
S16 tmp1;
// Set trigger level to the middle of signal amplitude
tmp1 = ((Vmax + Vmin)/2) + 2 * GetVPosOfs();
SetTrigLvl(tmp1);
UpdateDisp(Disp_Param);
}
void LedBlink(void)
{
U16 tmp;
// Turn on LED
Port_BitClr(LED_Base, (1 << LED_Bit));
tmp = 50;
while(tmp) {
Delay(65000);
tmp--;
}
// Turn off LED
Port_BitSet(LED_Base, (1 << LED_Bit));
tmp = 50;
while(tmp) {
Delay(65000);
tmp--;
}
// Turn on LED
Port_BitClr(LED_Base, (1 << LED_Bit));
tmp = 50;
while(tmp) {
Delay(65000);
tmp--;
}
// Turn off LED
Port_BitSet(LED_Base, (1 << LED_Bit));
}
void LedBlink_Fast(void)
{
U16 tmp;
while(1) {
// Turn on LED
Port_BitClr(LED_Base, (1 << LED_Bit));
tmp = 1;
while(tmp) {
Delay(65000);
tmp--;
}
// Turn off LED
Port_BitSet(LED_Base, (1 << LED_Bit));
tmp = 1;
while(tmp) {
Delay(65000);
tmp--;
}
}
}
void SaveWaveform(void)
{
U16 tmp0, tmp1, tmp2;
U16 *ptmp1;
// Display message
PutsGenic(WWindowx0 + 4, WWindowy0 -17, (U8 *)"Saving ", clOrange, clBlack, &ASC8X16);
FLASH_Unlock();
// Erase buffer
tmp2 = FLASH_ErasePage(Waveform_START_ADDRESS);
if(tmp2 != FLASH_COMPLETE) {
// Failed
PutsGenic(WWindowx0 + 4, WWindowy0 -17, (U8 *)"Failed1", clRed, clBlack, &ASC8X16);
goto Failure;
}
tmp2 = FLASH_ErasePage(Waveform_START_ADDRESS + 0x0400);
if(tmp2 != FLASH_COMPLETE) {
// Failed
PutsGenic(WWindowx0 + 4, WWindowy0 -17, (U8 *)"Failed1", clRed, clBlack, &ASC8X16);
goto Failure;
}
tmp2 = FLASH_ErasePage(Parameter_START_ADDRESS);
if(tmp2 != FLASH_COMPLETE) {
// Failed
PutsGenic(WWindowx0 + 4, WWindowy0 -17, (U8 *)"Failed2", clRed, clBlack, &ASC8X16);
goto Failure;
}
// Save waveform data
ptmp1 = SampleBuf;
tmp1 = GetRecLen();
tmp0 = 0;
while(tmp0 < tmp1) {
tmp2 = FLASH_ProgramHalfWord(Waveform_START_ADDRESS + tmp0 * 2, *ptmp1);
if(tmp2 != FLASH_COMPLETE) {
PutsGenic(WWindowx0 + 4, WWindowy0 -17, (U8 *)"Failed3", clRed, clBlack, &ASC8X16);
goto Failure;
}
// Proceed to next sample
ptmp1++;
tmp0++;
}
// Save parameters
ptmp1 = ReadingStr;
*ptmp1++ = (U16)GetRecLen();
*ptmp1++ = (U16)GetVSen();
*ptmp1++ = (U16)GetCpl();
*ptmp1++ = (U16)GetTimebase();
*ptmp1++ = (U16)GetTrigMode();
*ptmp1++ = (U16)GetTrigEdge();
*ptmp1++ = (U16)GetTrigLvl();
*ptmp1++ = (U16)GetHPos();
*ptmp1++ = (U16)GetVPos();
*ptmp1++ = (U16)GetVPosOfs();
ptmp1 = ReadingStr;
tmp1 = 10;
tmp0 = 0;
while(tmp0 < tmp1) {
tmp2 = FLASH_ProgramHalfWord(Parameter_START_ADDRESS + tmp0 * 2, *ptmp1);
if(tmp2 != FLASH_COMPLETE) {
PutsGenic(WWindowx0 + 4, WWindowy0 -17, (U8 *)"Failed4", clRed, clBlack, &ASC8X16);
goto Failure;
}
// Proceed to next sample
ptmp1++;
tmp0++;
}
// Return to normal running
UpdateDisp(Disp_Param);
return;
Failure:
UartPutc(0xFA, USART1);
UartPutc(tmp2 >> 8, USART1);
UartPutc(tmp2, USART1);
UartPutc(0xF0, USART1);
UartPutc(tmp0 >> 8, USART1);
UartPutc(tmp0, USART1);
while(1);
}
void LoadWaveform(void)
{
U16 tmp0, tmp1;
U16 *ptmp1, *ptmp2;
// Display message
PutsGenic(WWindowx0 + 4, WWindowy0 -17, (U8 *)"Loading", clOrange, clBlack, &ASC8X16);
// Load parameters
ptmp2 = Parameter_START_ADDRESS;
SetRecLen(*ptmp2++);
SetVSen((S8)*ptmp2++);
SetCpl((S8)*ptmp2++);
SetTimeBase((S8)*ptmp2++);
SetTrigMode((S8)*ptmp2++);
SetTrigEdge((S8)*ptmp2++);
SetTrigLvl((S16)*ptmp2++);
SetHPos((S16)*ptmp2++);
SetVPos((S16)*ptmp2++);
SetVPosOfs((S16)*ptmp2++);
// Load waveform data to SamBuffer
ptmp1 = SampleBuf;
ptmp2 = (U16 *)Waveform_START_ADDRESS;
tmp1 = GetRecLen();
tmp0 = 0;
while(tmp0 < tmp1) {
*ptmp1++ = *ptmp2++;
tmp0++;
}
// Enter HOLD
SetHold();
// Stop capture
StopCapture();
// Display
UpdateDisp(Disp_Param);
UpdateDisp(Disp_Trace);
}
// ---------------------------------------------------------------
// Note: Freq and duty measurement produce large errors for timebase >= 20us.
// This is because interpolation has been used for these timebases.
#define LevelSen 16 // Sensitivity of level
void Measurements(void)
{
S32 sum, bufsize, index, rms;
S16 vmax, vmin, v0;
S16 tmp;
S16 currentstate;
S32 fallingcnt, risingcnt;
S32 fallingtime, risingtime, highleveltime;
S16 HighLevel, LowLevel;
S32 firstfalling, firstrising; // Keep the start of valid samples
// Initialization for voltage measurement
vmax = 0;
vmin = 0xFFF;
sum = 0;
rms = 0;
v0 = GetVPosOfs(); //
bufsize = GetRecLen();
index = 0;
while(index < bufsize) {
// Get a sample
tmp = *(SampleBuf + index);
// Searching for Vmax
if(tmp > vmax) {
vmax = tmp;
}
// Searching for Vmin
if(tmp < vmin) {
vmin = tmp;
}
// Calculate sum
sum += tmp;
// Calculate rms
rms += ((S32)(tmp - SampleMidValue) * (S32)(tmp - SampleMidValue));
index++;
}
BitSet(Flags, (1 << Flag_VoltValid));
if((vmax > 0x0FFC) || (vmin < 0x0004)) {
BitClr(Flags, (1 << Flag_VoltValid));
}
Vmax = vmax - SampleMidValue;
Vmin = vmin - SampleMidValue;
Vavr = (sum/bufsize) - SampleMidValue;
Vpp = vmax - vmin;
Vrms = (S16)(sqrt((float)rms/(float)bufsize));
// Initialization for freq, Cycle, and duty measurement
v0 = (sum/bufsize);
fallingcnt = 0;
risingcnt = 0;
HighLevel = v0 + LevelSen/2;
LowLevel = v0 - LevelSen/2;
v0 = GetVPosOfs(); //
currentstate = ((*SampleBuf - v0) >= SampleMidValue) ? 1 : 0;
highleveltime = 0;
index = 0;
while(index < bufsize) {
// Get a sample
tmp = *(SampleBuf + index) - v0;
// Processing cycle by cycle
if(currentstate == 1) {
// Currrent level is HIGH
// Searching for falling transition
if(tmp <= LowLevel) {
// Falling transition found
currentstate = 0;
fallingcnt++;
fallingtime = index; // Mark down the falling edge
if(fallingcnt == 1) {
// Mark down the start of valid sample
firstfalling = index;
}
if(risingcnt != 0) {
// Accumulate high level duration
highleveltime += (fallingtime - risingtime);
}
}
}
else {
// Currrent level is LOW
// Searching for rising transition
if(tmp >= HighLevel) {
// Rising transition fount
currentstate = 1;
risingcnt++;
risingtime = index;
if(risingcnt == 1) {
// Mark down the start of valid sample
firstrising = index;
}
}
}
index++;
}
if((risingcnt >= 2) || (fallingcnt >= 2)) {
// At least one cycle found
BitSet(Flags, (1 << Flag_FreqValid));
// Calculate frequency
if(currentstate == 1) {
// The last transition is rising. Use first rising as starting point
sum = risingtime - firstrising;
risingcnt -= 1; // Number of cycles
}
else {
// The last transition is falling. Use first falling as starting point
sum = fallingtime - firstfalling;
risingcnt = fallingcnt - 1; // Number of cycles
if(firstrising < firstfalling) {
// The first crossover is rising. Take out the first segment of high level from 'highleveltime'
highleveltime -= (firstfalling - firstrising);
}
}
Freq = ((float)risingcnt/(float)sum) * SampleRate[GetTimebase() - TBMin];
Cycle = 1.0/Freq;
PW = ((float)highleveltime/(float)risingcnt) / SampleRate[GetTimebase() - TBMin];
Duty = ((float)highleveltime/(float)sum) * 100.0; // In percentage
// Select frequrency unit
FreqUnit = 0;
while(Freq >= 1000.0) {
Freq /= 1000.0;
FreqUnit++;
}
// Select cycle unit
CycleUnit = 4;
while(Cycle <= 0.01) {
Cycle *= 1000.0;
PW *= 1000.0;
CycleUnit++;
}
}
else {
BitClr(Flags, (1 << Flag_FreqValid));
}
}
void OnScreenDisplay(void)
{
U16 tmp1;
// Display measurement results
tmp1 = clMeasurement;
if(!BitTest(Flags, (1 << Flag_VoltValid))) {
tmp1 = clRed;
}
PutsGenic(VoltageReadingx0, VoltageReadingy0, (U8 *)"Vmax:", clMeasurement, clBlack, &ASC8X16);
PutsGenic(VoltageReadingx0 + 42, VoltageReadingy0, VoltToStr(Vmax, ReadingStr), tmp1, clBlack, &ASC8X16);
PutsGenic(VoltageReadingx0, VoltageReadingy0 + 1 * 16, (U8 *)"Vmin:", clMeasurement, clBlack, &ASC8X16);
PutsGenic(VoltageReadingx0 + 42, VoltageReadingy0 + 1 * 16, VoltToStr(Vmin, ReadingStr), tmp1, clBlack, &ASC8X16);
PutsGenic(VoltageReadingx0, VoltageReadingy0 + 2 * 16, (U8 *)"Vavr:", clMeasurement, clBlack, &ASC8X16);
PutsGenic(VoltageReadingx0 + 42, VoltageReadingy0 + 2 * 16, VoltToStr(Vavr, ReadingStr), tmp1, clBlack, &ASC8X16);
PutsGenic(VoltageReadingx0, VoltageReadingy0 + 3 * 16, (U8 *)"Vpp:", clMeasurement, clBlack, &ASC8X16);
PutsGenic(VoltageReadingx0 + 42, VoltageReadingy0 + 3 * 16, VoltToStr(Vpp, ReadingStr), tmp1, clBlack, &ASC8X16);
PutsGenic(VoltageReadingx0, VoltageReadingy0 + 4 * 16, (U8 *)"Vrms:", clMeasurement, clBlack, &ASC8X16);
PutsGenic(VoltageReadingx0 + 42, VoltageReadingy0 + 4 * 16, VoltToStr(Vrms, ReadingStr), tmp1, clBlack, &ASC8X16);
PutsGenic(FreqReadingx0, FreqReadingy0, (U8 *)"Freq:", clMeasurement, clBlack, &ASC8X16);
PutsGenic(FreqReadingx0, FreqReadingy0 + 1 * 16, (U8 *)"Cycl:", clMeasurement, clBlack, &ASC8X16);
PutsGenic(FreqReadingx0, FreqReadingy0 + 2 * 16, (U8 *)"PW:", clMeasurement, clBlack, &ASC8X16);
PutsGenic(FreqReadingx0, FreqReadingy0 + 3 * 16, (U8 *)"Duty:", clMeasurement, clBlack, &ASC8X16);
if(BitTest(Flags, (1 << Flag_FreqValid))) {
FloatToStr(Freq, 3, 3, ReadingStr);
strcat(ReadingStr, UnitsStr[FreqUnit]);
if((tmp1 = strlen(ReadingStr)) < FreqStrLen) {
// Clear previous display
FillRect(FreqReadingx0 + 42, FreqReadingy0, FreqStrLen * 8, 16, clBlack);
}
FreqStrLen = tmp1;
PutsGenic(FreqReadingx0 + 42, FreqReadingy0, ReadingStr, clMeasurement, clBlack, &ASC8X16);
FloatToStr(Cycle, 3, 3, ReadingStr);
strcat(ReadingStr, UnitsStr[CycleUnit]);
if((tmp1 = strlen(ReadingStr)) < CycleStrLen) {
// Clear previous display
FillRect(FreqReadingx0 + 42, FreqReadingy0 + 1 * 16, CycleStrLen * 8, 16, clBlack);
}
CycleStrLen = tmp1;
PutsGenic(FreqReadingx0 + 42, FreqReadingy0 + 1 * 16, ReadingStr, clMeasurement, clBlack, &ASC8X16);
FloatToStr(PW, 3, 3, ReadingStr);
strcat(ReadingStr, UnitsStr[CycleUnit]);
if((tmp1 = strlen(ReadingStr)) < PwStrLen) {
// Clear previous display
FillRect(FreqReadingx0 + 42, FreqReadingy0 + 2 * 16, PwStrLen * 8, 16, clBlack);
}
PwStrLen = tmp1;
PutsGenic(FreqReadingx0 + 42, FreqReadingy0 + 2 * 16, ReadingStr, clMeasurement, clBlack, &ASC8X16);
FloatToStr(Duty, 3, 1, ReadingStr);
strcat(ReadingStr, " %");
PutsGenic(FreqReadingx0 + 42, FreqReadingy0 + 3 * 16, ReadingStr, clMeasurement, clBlack, &ASC8X16);
BitClr(Flags, (1 << Flag_FreqAreaCleared));
}
else {
if(!BitTest(Flags, (1 << Flag_FreqAreaCleared))) {
FillRect(FreqReadingx0 + 42, FreqReadingy0, 120, 70, clBlack);
BitSet(Flags, (1 << Flag_FreqAreaCleared));
}
PutsGenic(FreqReadingx0 + 42, FreqReadingy0, (U8 *)"----", clMeasurement, clBlack, &ASC8X16);
PutsGenic(FreqReadingx0 + 42, FreqReadingy0 + 1 * 16, (U8 *)"----", clMeasurement, clBlack, &ASC8X16);
PutsGenic(FreqReadingx0 + 42, FreqReadingy0 + 2 * 16, (U8 *)"----", clMeasurement, clBlack, &ASC8X16);
PutsGenic(FreqReadingx0 + 42, FreqReadingy0 + 3 * 16, (U8 *)"----", clMeasurement, clBlack, &ASC8X16);
}
if(BitTest(AddOns, (1 << AO_TestSigAmpDisp))) {
// Display test signal amplitude
if(BitTest(AddOns, (1 << AO_TestSigAmp))) {
// Amp is 0.1V
PutsGenic(TestSigAmpx0, TestSigAmpy0, "T.S. Amp: 0.1V", clWhite, clBlue, &ASC8X16);
}
else {
// Amp is normal (3.3V)
PutsGenic(TestSigAmpx0, TestSigAmpy0, "T.S. Amp: 3.3V", clWhite, clBlue, &ASC8X16);
}
}
}
void TestSigAmpDisplay(void)
{
// Display test signal amplitude
if(BitTest(AddOns, (1 << AO_TestSigAmp))) {
// Amp is 0.1V
PutsGenic(TestSigAmpx0, TestSigAmpy0, "T.S. Amp: 0.1V", clWhite, clBlue, &ASC8X16);
}
else {
// Amp is normal (3.3V)
PutsGenic(TestSigAmpx0, TestSigAmpy0, "T.S. Amp: 3.3V", clWhite, clBlue, &ASC8X16);
}
}
// ---------------------------------------------------------------
// Convert voltage to string
//
U8 *VoltToStr(S16 volt, U8 *strbuf)
{
float ftmp;
U8 tmp0;
U16 tmp1;
S8 tmp2;
// Get current Vsen
tmp2 = GetVSen();
// Calculate voltage. The unit of ftmp is mV
ftmp = ((float)(volt >> 2) * (3300.0/4096.0)) * UnitRate[tmp2 - VSenMin] * 1.007;
if(ftmp >= 0) {
tmp0 = ' ';
tmp1 = (U16)ftmp;
}
else {
tmp0 = '-';
tmp1 = (U16)(-ftmp);
}
BinToDecStr16(tmp1, strbuf + 1);
if(tmp2 <= VS_01V) {
// Unit 'V"
*(strbuf + 5) = *(strbuf + 4);
*(strbuf + 4) = *(strbuf + 3);
*(strbuf + 3) = '.';
*(strbuf + 6) = 'V';
*(strbuf + 7) = 0;
// Remove leading 0's
if(*(strbuf + 1) == '0') {
*(strbuf + 1) = tmp0;
*(strbuf + 0) = ' ';
// tmp2 = 1;
}
else {
*(strbuf + 0) = tmp0; // Sign
// tmp2 = 0;
}
}
else {
*(strbuf + 0) = ' ';
*(strbuf + 1) = *(strbuf + 2);
*(strbuf + 2) = *(strbuf + 3);
*(strbuf + 3) = *(strbuf + 4);
*(strbuf + 4) = *(strbuf + 5);
*(strbuf + 5) = 'm';
*(strbuf + 6) = 'V';
*(strbuf + 7) = 0;
// Remove leading 0's
tmp2 = 1;
while(tmp2 < 4) {
if(*(strbuf + tmp2) == '0') {
*(strbuf + tmp2) = ' ';
}
else {
break;
}
tmp2++;
}
tmp2--;
*(strbuf + tmp2) = tmp0; // Sign
}
return strbuf;
}
U8 *BinToDecStr16(U16 binary, U8 *Str)
{
U8 tmp0;
tmp0 = binary/10000;
*(Str + 0) = 0x30 | tmp0;
binary = binary - tmp0 * 10000;
tmp0 = binary/1000;
*(Str + 1) = 0x30 | tmp0;
binary = binary - tmp0 * 1000;
tmp0 = binary/100;
*(Str + 2) = 0x30 | tmp0;
binary = binary - tmp0 * 100;
tmp0 = binary/10;
*(Str + 3) = 0x30 | tmp0;
binary = binary - tmp0 * 10;
*(Str + 4) = 0x30 | binary;
*(Str + 5) = 0;
return Str;
}
// ------------------------------------------------------------------------
// Convert float number 'f' to string with 'width' digits of whole and 'precision' digits of fraction
// digits. Return pointer to the string.
// Condition: f < 10^10, width < 10, presicion < 10
U8 *FloatToStr(float f, U8 width, U8 precision, U8 *str)
{
U32 tmp0;
U8 tmp1, tmp3, tmp4;
U8 tmpstr[24], *ptmp;
tmp1 = 0;
if(f < 0.0) {
tmp1 = 1; // Negative
f = 0.0 - f;
}
tmp0 = f; // Whole portion
BinToDec32(tmp0, &tmpstr[1]);
tmp3 = 0;
// Looking for the first non-zero digit
while(tmp3 < 10) {
if(tmpstr[tmp3 + 1] != '0') {
break;
}
tmp3++;
}
DeZero(&tmpstr[tmp3 + 1], width, ' ');
if(tmp3 == 10) {
tmpstr[10] = '0'; // Whole part is zero. Keep one '0' before decimal point
tmp3 = 9;
}
// Add '-' for negative number
if(tmp1) {
tmpstr[tmp3] = '-';
}
else {
tmpstr[tmp3] = ' ';
}
if(precision == 0) {
// No fraction required
tmpstr[11] = 0;
}
else {
tmpstr[11] = '.';
f = f - (float)tmp0; // Get fraction
tmp4 = precision;
while(tmp4) {
f *= 10.0;
tmp4--;
}
tmp0 = f; // Fraction
BinToDec32(tmp0, &tmpstr[12]);
// Advance the digits of interest
tmp4 = 0;
while(tmp4 < precision) {
tmpstr[12 + tmp4] = tmpstr[12 + (10 - precision) + tmp4];
tmp4++;
}
tmpstr[12 + tmp4] = 0;
}
ptmp = str;
tmp4 = 0;
while((*ptmp++ = tmpstr[tmp3 + tmp4])) {
tmp4++;
}
return str;
}
U8 *BinToDec32(U32 binary, U8 *Str)
{
U8 tmp0;
tmp0 = 0;
while(tmp0 <= 9) {
binary = FindDec(binary, tmp0, (U8 *)Str);
tmp0++;
}
*(Str + 10) = 0;
return Str;
}
U32 FindDec(U32 bin, U8 ind, U8 *str)
{
U8 tmp;
U32 tmp1;
tmp1 = 1;
tmp = 9 - ind;
while(tmp) {
tmp1 *= 10;
tmp--;
}
tmp = bin/tmp1;
*(str + ind) = 0x30 | tmp;
return (bin - tmp * tmp1);
}
// Remove leading zeros for the first 'num' digits. Replace them with 'ch'
void DeZero(U8 *str, U8 num, U8 ch)
{
U8 tmp;
tmp = 0;
while(tmp < num) {
if(*str == '0') {
*str = ch;
}
else {
break;
}
str++;
}
// Put '0' back if '.' is the first non-zero digit
if(*str == '.') {
str--;
*str = '0';
}
}