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);
}