Touch screen drivers control dashboard for miniature locomotive. Features meters for speed, volts, power. Switches for lights, horns. Drives multiple STM3_ESC brushless motor controllers for complete brushless loco system as used in "The Brute" - www.jons-workshop.com

Dependencies:   TS_DISCO_F746NG mbed Servo LCD_DISCO_F746NG BSP_DISCO_F746NG QSPI_DISCO_F746NG AsyncSerial FastPWM

movingcoilmeter.cpp

Committer:
JonFreeman
Date:
2019-03-04
Revision:
14:6bcec5ac21ca
Parent:
12:a25bdf135348

File content as of revision 14:6bcec5ac21ca:

#include "mbed.h"
#include "TS_DISCO_F746NG.h"
#include "LCD_DISCO_F746NG.h"
#include "Electric_Loco.h"

extern  LCD_DISCO_F746NG    lcd;
static const int    char_widths[]   = {5, 7, 11, 14, 17, 17}    ,
                    meter_radius_min = 30, meter_radius_max = 120;
extern  void    displaytext    (int x, int y, const int font, char * txt)   ;

void    moving_coil_meter::DrawNeedle  (double alpha, int colour)
{
    uint16_t    x1, x2, x3, y1, y2, y3;
    int save_colour, ssa, sca;
    while   (alpha > PI) alpha   -= TWO_PI;
    while   (alpha < 0.0) alpha  += TWO_PI;
    double  shortln = (needle_len / 18.7),
            sina    = sin(alpha),
            cosa    = cos(alpha);

    save_colour = lcd.GetTextColor  ();
    ssa = (int)(shortln * sina);
    sca = (int)(shortln * cosa);
    old_angle = alpha;
    x1 = cent_x - ssa;//(int)(shortln * sin(alpha));
    y1 = cent_y - sca;//(int)(shortln * cos(alpha));
    x2 = cent_x + (int)(needle_len * cosa);
    y2 = cent_y - (int)(needle_len * sina);  //  - as increasing y is downwards
    x3 = cent_x + ssa;//(int)(shortln * sin(alpha));
    y3 = cent_y + sca;//(int)(shortln * cos(alpha));
    lcd.SetTextColor    (colour);
    lcd.FillCircle      (cent_x, cent_y, (int)(needle_len / 15.0));
//    FillTriangle    (x1, x2, x3, y1, y2, y3);

//static void FillTriangle(uint16_t x1, uint16_t x2, uint16_t x3, uint16_t y1, uint16_t y2, uint16_t y3)
//{
    int16_t deltax = 0, deltay = 0, x, y, xinc1 = 0, xinc2 = 0,
            yinc1 = 0, yinc2 = 0, den = 0, num = 0, num_add = 0, num_pixels = 0,
            curpixel = 0;

    deltax = abs(x2 - x1);        /* The difference between the x's */
    deltay = abs(y2 - y1);        /* The difference between the y's */
    x = x1;                       /* Start x off at the first pixel */
    y = y1;                       /* Start y off at the first pixel */

    if (x2 >= x1) {               /* The x-values are increasing */
        xinc1 = 1;
        xinc2 = 1;
    } else {                      /* The x-values are decreasing */
        xinc1 = -1;
        xinc2 = -1;
    }

    if (y2 >= y1) {               /* The y-values are increasing */
        yinc1 = 1;
        yinc2 = 1;
    } else {                      /* The y-values are decreasing */
        yinc1 = -1;
        yinc2 = -1;
    }

    if (deltax >= deltay) {       /* There is at least one x-value for every y-value */
        xinc1 = 0;                  /* Don't change the x when numerator >= denominator */
        yinc2 = 0;                  /* Don't change the y for every iteration */
        den = deltax;
        num = deltax / 2;
        num_add = deltay;
        num_pixels = deltax;         /* There are more x-values than y-values */
    } else {                      /* There is at least one y-value for every x-value */
        xinc2 = 0;                  /* Don't change the x for every iteration */
        yinc1 = 0;                  /* Don't change the y when numerator >= denominator */
        den = deltay;
        num = deltay / 2;
        num_add = deltax;
        num_pixels = deltay;         /* There are more y-values than x-values */
    }

    for (curpixel = 0; curpixel <= num_pixels; curpixel++) {
        lcd.DrawLine(x, y, x3, y3);

        num += num_add;              /* Increase the numerator by the top of the fraction */
        if (num >= den) {           /* Check if numerator >= denominator */
            num -= den;               /* Calculate the new numerator value */
            x += xinc1;               /* Change the x as appropriate */
            y += yinc1;               /* Change the y as appropriate */
        }
        x += xinc2;                 /* Change the x as appropriate */
        y += yinc2;                 /* Change the y as appropriate */
    }
//}
    lcd.SetTextColor    (save_colour);
}

void    moving_coil_meter::LED (int which_led, int colour)  {   //  Meter mounting screw holes serve as LEDs. LCD_COLOR_DARKGRAY for off
    lcd.SetTextColor    (colour);
    switch  (which_led) {
        case    0:  //  NW
            lcd.FillCircle  (cent_x - screw_hole_offset, cent_y - screw_hole_offset, screw_rad);  //  panel mounting screw holes near corners
            break;
        case    1:  //  SW
            lcd.FillCircle  (cent_x - screw_hole_offset, cent_y + screw_hole_offset, screw_rad);
            break;
        case    2:  //  NE
            lcd.FillCircle  (cent_x + screw_hole_offset, cent_y - screw_hole_offset, screw_rad);
            break;
        case    3:  //  SE
            lcd.FillCircle  (cent_x + screw_hole_offset, cent_y + screw_hole_offset, screw_rad);
            break;
        default:
            break;
    }
}

void    moving_coil_meter::redraw ()
{
    int oldcolour1 = lcd.GetTextColor   ();
    int oldcolour2 = lcd.GetBackColor   ();
    lcd.SetTextColor    (body_colour);
    //  Draw meter body as solid square with rounded corners, complete with mounting screw holes !

    int font, charwid, x_offset;
//    int corner_rad          = (meter_radius / 6),
//        screw_hole_offset   = (meter_radius * 92 / 100),
//        screw_rad           = (meter_radius / 13);

    lcd.FillRect    (cent_x - meter_radius, cent_y - meter_radius - corner_rad, meter_radius * 2, corner_rad);
    lcd.FillRect    (cent_x - meter_radius, cent_y + meter_radius, meter_radius * 2, corner_rad + 1);
    lcd.FillRect    (cent_x - meter_radius - corner_rad, cent_y - meter_radius, 1 +(meter_radius + corner_rad) * 2, meter_radius * 2);
    lcd.FillCircle  (cent_x - meter_radius, cent_y - meter_radius, corner_rad);  //  meter box has rounded corners
    lcd.FillCircle  (cent_x - meter_radius, cent_y + meter_radius, corner_rad);
    lcd.FillCircle  (cent_x + meter_radius, cent_y - meter_radius, corner_rad);
    lcd.FillCircle  (cent_x + meter_radius, cent_y + meter_radius, corner_rad);     //  done drawing meter body
/*    lcd.SetTextColor    (LCD_COLOR_DARKGRAY);
    lcd.FillCircle  (cent_x - screw_hole_offset, cent_y - screw_hole_offset, screw_rad);  //  panel mounting screw holes near corners
    lcd.FillCircle  (cent_x - screw_hole_offset, cent_y + screw_hole_offset, screw_rad);    //  Jan 2019 now drawn as LEDs below
    lcd.FillCircle  (cent_x + screw_hole_offset, cent_y - screw_hole_offset, screw_rad);
    lcd.FillCircle  (cent_x + screw_hole_offset, cent_y + screw_hole_offset, screw_rad);
*/
    lcd.SetTextColor    (disc_colour);
    lcd.FillCircle      (cent_x, cent_y, meter_radius);     //  main meter dial face

    int save_colour = lcd.GetTextColor  ();
    int radius_inner = (int) meter_radius - 2, radius_outer = (int) (meter_radius * 0.9);
    double  ang, cosang, sinang, angle_step;
    lcd.SetTextColor    (scale_colour);
    angle_step  = (start_angle - end_angle) / scale_ticks;
    ang = start_angle;
    for (int i = 0; i <= scale_ticks; i++)   {   //    DrawScaleGraduations
        cosang  = cos(ang);
        sinang  = sin(ang);
        lcd.DrawLine    (cent_x + radius_outer * cosang, cent_y - radius_outer * sinang, cent_x + radius_inner * cosang, cent_y - radius_inner * sinang);
        ang -= angle_step;
    }

    font = get_font_size ();
    charwid = char_widths[font];
    x_offset = charwid * strlen(unit_txt) / 2;
    lcd.SetTextColor    (text_colour);
    lcd.SetBackColor    (disc_colour);
    displaytext (cent_x - x_offset, cent_y + (meter_radius * 6) / 19, font, unit_txt);
    lcd.SetBackColor    (oldcolour2);
    lcd.SetTextColor    (oldcolour1);
    
    LED (0, LCD_COLOR_RED);     //  NW
    LED (1, LCD_COLOR_GREEN);   //  SW
    LED (2, LCD_COLOR_BLUE);    //  NE
    LED (3, LCD_COLOR_YELLOW);  //  SE
}


moving_coil_meter::moving_coil_meter   (    int bod_col, int bgcol, int needlecol, int textcol, int scalecol,
                                            int cx, int cy, int size, double lo, double hi, double start_ang, double end_ang, 
                                            int scaleticks, char * units, int decimal_places, bool sign)
{
    strncpy (unit_txt, units, 8);
    body_colour = bod_col;
    disc_colour = bgcol;
    needle_colour   = needlecol;
    text_colour = textcol;
    scale_colour    = scalecol;
    if  (size < meter_radius_min)
        size = meter_radius_min;
    if  (size > meter_radius_max)
        size = meter_radius_max;
    meter_radius = size;
    cent_x = cx;
    cent_y = cy;
    start_angle = start_ang;
    end_angle = end_ang;
    value_min = lo;
    value_max = hi;
    scale_ticks = scaleticks;
    swept_angle = abs(start_angle - end_angle);
    value_range = (value_max - value_min);
    dec_places  = decimal_places;
    draw_sign   = sign;     //  bool
    needle_len = (int)(0.87 * (double)meter_radius);
    corner_rad          = (meter_radius / 6),
    screw_hole_offset   = (meter_radius * 92 / 100),
    screw_rad           = (meter_radius / 13);
}

int     moving_coil_meter::get_font_size ()
{
    int font = meter_radius - meter_radius_min;
    font /= 17;
    if  (font > 4)
        font = 4;
    if  (font < 2)
        font = 2;
    return  font;
}

double  anglefix    (double a)  {       //  Ensures 0.0 <= angle <= + two PI
    while   (a > PI) a   -= TWO_PI;
    while   (a < 0.0) a   += TWO_PI;
    return  a;
}

double  moving_coil_meter::get_pointer_angle    (double v)
{
//    double  a;
    if   (v < value_min) v = value_min;
    if   (v > value_max) v = value_max;
    return   anglefix   (start_angle - ((v - value_min) * swept_angle / value_range))    ;
}

void    moving_coil_meter::set_value   (double meter_read_value)
{
    char    txt[32];
    int x_offset, font, lenchk;//,
//    double  pointer_angle;
    DrawNeedle  (old_angle, disc_colour);                   //  un-draw needle
    DrawNeedle  (get_pointer_angle   (meter_read_value), needle_colour)  ; //  re-draw needle
    if  (dec_places == ONE_DP)
        sprintf (txt, " %+.1f \0", meter_read_value);
    else
        sprintf (txt, " %+.0f \0", meter_read_value);
    lenchk = strlen(txt);
    if  (!draw_sign)    {
        for (int i = 1; i < lenchk; i++)
            txt[i] = txt[i + 1];
        lenchk--;     //  Stupidly, this gives the display flicker blight
    }
//    lenchk = strlen(txt);//  Stupidly, repeating this instead does NOT give the display flicker blight
    font = get_font_size    ();
    x_offset = char_widths[font] * lenchk / 2;
    lcd.SetTextColor    (text_colour);
    lcd.SetBackColor    (disc_colour);
    if  (lenchk > 0 && lenchk < 9)
        displaytext (cent_x - x_offset, cent_y + (meter_radius * 11) / 19, font, txt);
}