/*
Copyright (C) 2000 T. Scott Dattalo
This file is part of gpsim.
gpsim is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
gpsim is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with gpsim; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
/*
led.cc
This is an example module illustrating how gpsim modules may be created.
Additional examples may also be found with the gpsim.
This particular example creates a 7-segment, common cathode LED display.
Pin Numbering of LED:
--------------------
a
---
f | g | b
---
e | | c
---
d
cc = common cathode
Electrical:
----------
a ---|>|---+
b ---|>|---+
c ---|>|---+
d ---|>|---+
e ---|>|---+
f ---|>|---+
g ---|>|---+
|
cc
How It Works:
------------
Once the Led module has been built (and optionally installed), you
can include it in your .stc file. See the examples subdirectory.
*/
/* IN_MODULE should be defined for modules */
#define IN_MODULE
#include <errno.h>
#include <stdlib.h>
#include <string>
#include <iostream>
#include "../config.h" // get the definition for HAVE_GUI
#ifdef HAVE_GUI
#include <gtk/gtk.h>
#include <math.h>
#include "../src/gpsim_interface.h"
#include "../src/gpsim_time.h"
#include "led.h"
#include "../src/packages.h"
namespace Leds {
//--------------------------------------------------------------
//
// Create an "interface" to gpsim
//
class LED_Interface : public Interface
{
private:
Led_base *led;
int lastport;
public:
virtual void SimulationHasStopped (gpointer object)
{
GuiUpdate(object);
}
virtual void GuiUpdate (gpointer object)
{
if(led)
led->update();
/* {
int portval = led->port->get_value();
if(lastport != portval) {
lastport=portval;
led->update();
}
}
*/
}
LED_Interface(Led_base *_led) : Interface((gpointer *) _led)
{
led = _led;
lastport = -1;
}
};
class Led_Input : public IOPIN
{
public:
Led_Input(const char *n, Led_base *pParent);
virtual void setDrivenState(bool);
private:
Led_base *m_pParent;
};
//------------------------------------------------------------------------
//
Led_Input::Led_Input(const char *n, Led_base *pParent)
: IOPIN(n), m_pParent(pParent)
{
}
void Led_Input::setDrivenState(bool bNewState)
{
IOPIN::setDrivenState(bNewState);
}
//------------------------------------------------------------------------
void Led_7Segments::update()
{
update(darea, w_width,w_height);
}
void Led_7Segments::callback()
{
get_cycles().set_break_delta( get_interface().get_update_rate()+1, this);
update();
}
void Led_7Segments::update( GtkWidget *widget,
guint new_width,
guint new_height)
{
guint i;
w_width = new_width;
w_height = new_height;
GdkDrawable *drawable = widget->window;
if(!GTK_WIDGET_REALIZED(widget))
return;
if(segment_gc==NULL)
{
segment_gc = gdk_gc_new(darea->window);
gdk_gc_set_line_attributes(segment_gc,
5,
GDK_LINE_SOLID,
GDK_CAP_ROUND,
GDK_JOIN_ROUND);
g_assert(segment_gc!=NULL);
}
// not a very O-O way of doing it... but here we go directly
// to the I/O port and get the values of the segments
int segment_states = getPinState();
GdkGC *gc = segment_gc;
gdk_gc_set_foreground(gc,
&led_background_color);
gdk_draw_rectangle (drawable, gc,
TRUE,
0,
0,
w_width,
w_height);
// cout << "expose led, segment states = " << segment_states << '\n';
if( (segment_states & 1) == 0) {
// common cathode, cathode must be low to turn
//digits on.
gdk_gc_set_foreground(gc,&led_segment_on_color);
for(i=0; i<7; i++) {
if(segment_states & (2<<i))
gdk_draw_polygon ( drawable,
gc,
TRUE,
segments[i].p,
6);
}
}
gdk_gc_set_foreground(gc,&led_segment_off_color);
// turn off the segments that aren't being driven.
for(i=0; i<7; i++) {
if((segment_states & (2<<i)) == 0)
gdk_draw_polygon ( drawable,
gc,
TRUE,
segments[i].p,
6);
}
}
static gint
led7_expose_event (GtkWidget *widget,
GdkEvent *event,
gpointer user_data)
{
Led_7Segments *led;
guint max_width;
guint max_height;
g_return_val_if_fail (widget != NULL, TRUE);
g_return_val_if_fail (GTK_IS_DRAWING_AREA (widget), TRUE);
// de-reference the user_data into an led object
led = (Led_7Segments *)user_data;
max_width = widget->allocation.width;
max_height = widget->allocation.height;
led->update(widget,max_width,max_height);
return TRUE;
}
static gint
cursor_event (GtkWidget *widget,
GdkEvent *event,
gpointer *user_data)
{
if ((event->type == GDK_BUTTON_PRESS) &&
((event->button.button == 1) ||
(event->button.button == 3)))
{
return TRUE;
}
return FALSE;
}
//-------------------------------------------------------------------
// build_segments
//
// from Dclock.c (v.2.0) -- a digital clock widget.
// Copyright (c) 1988 Dan Heller <argv@sun.com>
// Modifications 2/93 by Tim Edwards <tim@sinh.stanford.edu>
// And further modifications by Scott Dattalo <scott@dattalo.com>
//
// Each segment on the LED is comprised of a 6 point polygon.
// This routine will calculate what those points should be and
// store them an arrary.
void Led_7Segments::build_segments( int w, int h)
{
XfPoint *pts;
float spacer, hskip, fslope, bslope, midpt, seg_width, segxw;
float invcosphi, invsinphi, invcospsi, invsinpsi, slope;
float dx1, dx2, dx3, dx4, dx5, dx6, dy1, dy2, dy5, dy6;
float xfactor, temp_xpts[4];
w_width = w;
w_height = h;
// Hard code the display parameters...
space_factor = (float)0.13;
width_factor = (float)0.13;
sxw = (float)0.13;
angle = 6;
/* define various useful constants */
segxw = sxw * w;
slope = angle;
seg_width = width_factor * w;
spacer = (float)w * space_factor;
hskip = (float)(seg_width * 0.125);
fslope = 1 / (segxw/seg_width + 1/slope);
bslope = -1 / (segxw/seg_width - 1/slope);
midpt = (float)h / 2;
/* define some trigonometric values */
/* phi is the forward angle separating two segments;
psi is the reverse angle separating two segments. */
invsinphi = sqrt(1 + fslope * fslope) / fslope;
invcosphi = sqrt(1 + 1/(fslope * fslope)) * fslope;
invsinpsi = sqrt(1 + bslope * bslope) / -bslope;
invcospsi = sqrt(1 + 1/(bslope * bslope)) * bslope;
/* define offsets from easily-calculated points for 6 situations */
dx1 = hskip * invsinphi / (slope/fslope - 1);
dy1 = hskip * invcosphi / (1 - fslope/slope);
dx2 = hskip * invsinpsi / (1 - slope/bslope);
dy2 = hskip * invcospsi / (bslope/slope - 1);
dx3 = hskip * invsinphi;
dx4 = hskip * invsinpsi;
dx5 = hskip * invsinpsi / (1 - fslope/bslope);
dy5 = hskip * invcospsi / (bslope/fslope - 1);
dx6 = dy5;
dy6 = dx5;
/* calculate some simple reference points */
temp_xpts[0] = spacer + (h - seg_width)/slope;
temp_xpts[1] = spacer + (h - seg_width/2)/slope + segxw/2;
temp_xpts[2] = spacer + h/slope + segxw;
temp_xpts[3] = temp_xpts[0] + segxw;
xfactor = w - 2 * spacer - h / slope - segxw;
/*
cout << "temp_xpts[2] " << temp_xpts[2] << '\n';
cout << "dx3 " << dx3 << '\n';
cout << "fslope " << fslope << '\n';
*/
/* calculate the digit positions */
pts = seg_pts[TOP];
pts[0].y = pts[1].y = 0;
pts[0].x = temp_xpts[2] - dx3;
pts[1].x = w - spacer - segxw + dx4;
pts[2].y = pts[5].y = (seg_width / 2) - dy5 - dy6;
pts[5].x = temp_xpts[1] + dx5 - dx6;
pts[2].x = pts[5].x + xfactor;
pts[3].y = pts[4].y = seg_width;
pts[4].x = temp_xpts[3] + dx4;
pts[3].x = temp_xpts[0] + xfactor - dx3;
pts = &(seg_pts[MIDDLE][0]);
pts[0].y = pts[1].y = midpt - seg_width/2;
pts[0].x = spacer + (h - pts[0].y)/slope + segxw;
pts[1].x = pts[0].x - segxw + xfactor;
pts[2].y = pts[5].y = midpt;
pts[3].y = pts[4].y = midpt + seg_width/2;
pts[5].x = spacer + (h - pts[5].y)/slope + segxw/2;
pts[2].x = pts[5].x + xfactor;
pts[4].x = pts[0].x - seg_width/slope;
pts[3].x = spacer + (h - pts[3].y)/slope + xfactor;
pts = &(seg_pts[BOTTOM][0]);
pts[3].y = pts[4].y = (float)h;
pts[2].y = pts[5].y = h - (seg_width / 2) + dy5 + dy6;
pts[0].y = pts[1].y = h - seg_width;
pts[0].x = spacer + segxw + seg_width/slope + dx3;
pts[1].x = spacer + (h - pts[1].y)/slope + xfactor - dx4;
pts[4].x = spacer + segxw - dx4;
pts[5].x = spacer + segxw/2 + (h - pts[5].y)/slope + dx6 - dx5;
pts[2].x = pts[5].x + xfactor;
pts[3].x = spacer + xfactor + dx3;
pts = &(seg_pts[TOP_LEFT][0]);
pts[0].y = seg_width / 2 - dy6 + dy5;
pts[1].y = seg_width + dy2;
pts[2].y = seg_pts[MIDDLE][0].y - 2 * dy1;
pts[3].y = seg_pts[MIDDLE][5].y - 2 * dy6;
pts[4].y = seg_pts[MIDDLE][0].y;
pts[5].y = seg_width - dy1;
pts[0].x = temp_xpts[1] - dx5 - dx6;
pts[1].x = temp_xpts[3] - dx2;
pts[2].x = seg_pts[MIDDLE][0].x + 2 * dx1;
pts[3].x = seg_pts[MIDDLE][5].x - 2 * dx6;
pts[4].x = spacer + (h - pts[4].y)/slope;
pts[5].x = temp_xpts[0] + dx1;
pts = &(seg_pts[BOT_LEFT][0]);
pts[0].y = seg_pts[MIDDLE][5].y + 2 * dy5;
pts[1].y = seg_pts[MIDDLE][4].y + 2 * dy2;
pts[2].y = seg_pts[BOTTOM][0].y - dy1;
pts[3].y = seg_pts[BOTTOM][5].y - 2 * dy6;
pts[4].y = h - seg_width + dy2;
pts[5].y = midpt + seg_width/2;
pts[0].x = seg_pts[MIDDLE][5].x - 2 * dx5;
pts[1].x = seg_pts[MIDDLE][4].x - 2 * dx2;
pts[2].x = seg_pts[BOTTOM][0].x - dx3 + dx1;
pts[3].x = seg_pts[BOTTOM][5].x - 2 * dx6;
pts[4].x = spacer + seg_width / slope - dx2;
pts[5].x = spacer + (midpt - seg_width/2) / slope;
pts = &(seg_pts[TOP_RIGHT][0]);
pts[0].y = seg_width/2 - dy5 + dy6;
pts[1].y = seg_width - dy2;
pts[2].y = midpt - seg_width/2;
pts[3].y = midpt - 2 * dy5;
pts[4].y = pts[2].y - 2 * dy2;
pts[5].y = seg_width + dy1;
pts[0].x = temp_xpts[1] + xfactor + dx5 + dx6;
pts[1].x = temp_xpts[3] + xfactor + dx1;
pts[2].x = seg_pts[MIDDLE][0].x + xfactor;
pts[3].x = seg_pts[MIDDLE][5].x + xfactor + dx5 * 2;
pts[4].x = seg_pts[TOP_LEFT][4].x + xfactor + dx2 * 2;
pts[5].x = temp_xpts[0] + xfactor - dx1;
pts = &(seg_pts[BOT_RIGHT][0]);
pts[0].y = seg_pts[MIDDLE][2].y + 2 * dy6;
pts[1].y = midpt + seg_width / 2;
pts[2].y = h - seg_width + dy1;
pts[3].y = h - (seg_width / 2) + dy6 - dy5;
pts[4].y = h - seg_width - dy2;
pts[5].y = seg_pts[MIDDLE][3].y + 2 * dy1;
pts[0].x = seg_pts[MIDDLE][2].x + 2 * dx6;
pts[1].x = seg_pts[MIDDLE][3].x + segxw;
pts[2].x = seg_pts[BOTTOM][1].x + dx4 + segxw - dx1;
pts[3].x = seg_pts[BOTTOM][2].x + 2 * dx5;
pts[4].x = seg_pts[BOTTOM][1].x + dx4 + dx2;
pts[5].x = seg_pts[MIDDLE][3].x - 2 * dx1;
// Convert the floating point points into integers.
int i,j;
for(i=0; i<NUM_SEGS; i++) {
for(j=0; j<MAX_PTS; j++) {
segments[i].p[j].x = (int)seg_pts[i][j].x;
segments[i].p[j].y = (int)seg_pts[i][j].y;
}
}
}
void Led_7Segments::build_window()
{
GtkWidget *main_vbox;
GtkWidget *vbox;
main_vbox = gtk_vbox_new (FALSE, 5);
gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 0);
vbox =
gtk_widget_new (gtk_vbox_get_type (),
"GtkBox::homogeneous", FALSE,
//"GtkBox::spacing", 5,
//"GtkContainer::border_width", 10,
"GtkWidget::parent", main_vbox,
"GtkWidget::visible", TRUE,
NULL);
gtk_widget_show(vbox);
darea = gtk_drawing_area_new ();
gtk_widget_set_usize (darea,
100,
100);
gtk_container_add (GTK_CONTAINER (vbox), darea);
gtk_signal_connect (GTK_OBJECT (darea),
"expose_event",
GTK_SIGNAL_FUNC (led7_expose_event),
this);
gtk_widget_set_events (darea, GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK);
gtk_signal_connect (GTK_OBJECT (darea),
"button_press_event",
GTK_SIGNAL_FUNC (cursor_event),
NULL);
gtk_widget_show (darea);
set_widget(main_vbox);
segment_gc=NULL;
// The 'on' color is bright red
led_segment_on_color.red = 0xc000;
led_segment_on_color.green = 0x0000;
led_segment_on_color.blue = 0x0000;
gdk_color_alloc(gdk_colormap_get_system(), &led_segment_on_color);
// The `off' color is dark red
led_segment_off_color.red = 0x4000;
led_segment_off_color.green = 0x0000;
led_segment_off_color.blue = 0x0000;
gdk_color_alloc(gdk_colormap_get_system(), &led_segment_off_color);
// The background is black like my coffee
led_background_color.red = 0x0000;
led_background_color.green = 0x0000;
led_background_color.blue = 0x0000;
gdk_color_alloc(gdk_colormap_get_system(), &led_background_color);
// }
}
//--------------------------------------------------------------
Led_7Segments::Led_7Segments()
{
//cout << "7-segment led constructor\n";
new_name("LED7SEG");
if(get_interface().bUsingGUI()) {
build_segments(100, 100);
build_window();
}
interface = new LED_Interface(this);
get_interface().add_interface(interface);
callback();
}
Led_7Segments::~Led_7Segments()
{
for (int i=0; i<8; i++)
delete m_pins[i];
delete [] m_pins;
}
//--------------------------------------------------------------
// create_iopin_map
//
// This is where the information for the Module's package is defined.
// Specifically, the I/O pins of the module are created.
void Led_7Segments::create_iopin_map()
{
// Define the physical package.
// The Package class, which is a parent of all of the modules,
// is responsible for allocating memory for the I/O pins.
//
// The 7-segment LED has 8 pins
create_pkg(8);
m_pins = new Led_Input *[8];
// Position pins on left side of package
float pos=0.0;
const float dp = (float)(0.9999/7.0);
package->set_pin_position(1,pos);pos+=dp;
package->set_pin_position(2,pos);pos+=dp;
package->set_pin_position(3,pos);pos+=dp;
package->set_pin_position(4,pos);pos+=dp;
package->set_pin_position(5,pos);pos+=dp;
package->set_pin_position(6,pos);pos+=dp;
package->set_pin_position(7,pos);pos+=dp;
package->set_pin_position(8,pos);pos+=dp;
// Here, we create and name the I/O pins. In gpsim, we will reference
// the bit positions as LED.seg0, LED.seg1, ..., where LED is the
// user-assigned name of the 7-segment LED
m_pins[0] = new Led_Input( (name() + ".cc").c_str(), this);
int i;
char ch;
for (ch='0',i = 1; i<8; i++,ch++)
m_pins[i] = new Led_Input((name() + ".seg"+ch).c_str(), this);
for (i=0; i<8; i++)
assign_pin(i+1,m_pins[i]);
initializeAttributes();
}
//--------------------------------------------------------------
unsigned int Led_7Segments::getPinState()
{
unsigned int s=0;
for (int i=0; i<8; i++)
s = (s>>1) | (m_pins[i]->getDrivenState() ? 0x80 : 0);
return s;
}
//--------------------------------------------------------------
// construct
Module * Led_7Segments::construct(const char *_new_name=0)
{
Led_7Segments *l7sP = new Led_7Segments ;
l7sP->new_name(_new_name);
l7sP->create_iopin_map();
return l7sP;
}
//-------------------------------------------------------------
// Led (simple)
//-------------------------------------------------------------
void Led::update()
{
update(darea, w_width,w_height);
}
void Led::callback()
{
get_cycles().set_break_delta( get_interface().get_update_rate()+1, this);
update();
}
void Led::update( GtkWidget *widget,
guint new_width,
guint new_height)
{
if(!get_interface().bUsingGUI())
return;
w_width = new_width;
w_height = new_height;
GdkDrawable *drawable = widget->window;
if(!GTK_WIDGET_REALIZED(widget))
return;
if(gc==NULL)
{
gc = gdk_gc_new(darea->window);
gdk_gc_set_line_attributes(gc,
5,
GDK_LINE_SOLID,
GDK_CAP_ROUND,
GDK_JOIN_ROUND);
g_assert(gc!=NULL);
}
gdk_gc_set_foreground(gc,&led_segment_off_color);
gdk_draw_rectangle (drawable, gc,
TRUE,
0,
0,
w_width,
w_height);
if(m_pin->getDrivenState()) {
gdk_gc_set_foreground(gc,&led_segment_on_color);
gdk_draw_arc(drawable, gc,
TRUE,
0,
0,
w_width,
w_height,
0,64*360);
}
}
static gint
led_expose_event (GtkWidget *widget,
GdkEvent *event,
gpointer user_data)
{
Led *led;
guint max_width;
guint max_height;
g_return_val_if_fail (widget != NULL, TRUE);
g_return_val_if_fail (GTK_IS_DRAWING_AREA (widget), TRUE);
// de-reference the user_data into an led object
led = (Led *)user_data;
max_width = widget->allocation.width;
max_height = widget->allocation.height;
led->update(widget,max_width,max_height);
return TRUE;
}
void Led::build_window()
{
GtkWidget *main_vbox;
main_vbox = gtk_vbox_new (FALSE, 5);
gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 0);
darea = gtk_drawing_area_new ();
w_height=20;
w_width=20;
gtk_widget_set_usize (darea,
w_height,
w_width);
gtk_signal_connect (GTK_OBJECT (darea),
"expose_event",
GTK_SIGNAL_FUNC (led_expose_event),
this);
gtk_widget_set_events (darea, GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK);
gtk_widget_show (darea);
set_widget(darea);
gc=NULL;
// The 'on' color is bright red
led_segment_on_color.red = 0xc000;
led_segment_on_color.green = 0x0000;
led_segment_on_color.blue = 0x0000;
gdk_color_alloc(gdk_colormap_get_system(), &led_segment_on_color);
// The `off' color is dark red
led_segment_off_color.red = 0x4000;
led_segment_off_color.green = 0x0000;
led_segment_off_color.blue = 0x0000;
gdk_color_alloc(gdk_colormap_get_system(), &led_segment_off_color);
}
//--------------------------------------------------------------
Led::Led()
{
new_name("LED");
if(get_interface().bUsingGUI())
build_window();
interface = new LED_Interface(this);
get_interface().add_interface(interface);
callback();
}
Led::~Led()
{
delete m_pin;
}
//--------------------------------------------------------------
// create_iopin_map
//
// This is where the information for the Module's package is defined.
// Specifically, the I/O pins of the module are created.
void Led::create_iopin_map()
{
create_pkg(1);
// Position pin on left side of package
package->set_pin_position(1,0.5);
// Define the LED Cathode. (The anode is implicitly tied to VCC)
m_pin = new Led_Input((name() + ".in").c_str(), this);
assign_pin(1, m_pin);
initializeAttributes();
}
//--------------------------------------------------------------
// construct
Module * Led::construct(const char *_new_name=0)
{
Led *ledP = new Led;
ledP->new_name(_new_name);
ledP->create_iopin_map();
return ledP;
}
}
#endif //HAVE_GUI
syntax highlighted by Code2HTML, v. 0.9.1