// // SerialIO v0.3 // A Plugin to the Gear Propeller Simulator that allows connecting // to a FullDuplexSerial object running on the propeller chip. // // CHANGELOG // v0.2 - Adds a scrollbar and automatically scrolls down as text comes in. // v0.2 - Fixes a bug in handling receiving a 0 byte. // v0.1 - Initial release. // // 2009 Ben Levitt // using System; using System.Drawing; using System.Windows.Forms; using Gear.PluginSupport; using Gear.EmulationCore; class SerialIO : PluginBase { public class SerialTextBox:TextBox { public SerialIO serial; private const int WM_PASTE = 0x0302; protected override void WndProc(ref Message m) { switch (m.Msg) { case WM_PASTE: if (Clipboard.ContainsText() == true) { // Add to the send buffer this.serial.SendString(Clipboard.GetText()); } break; default: base.WndProc(ref m); break; } } } private Propeller Chip; private int rxPin; private int txPin; private int baud; private double bitTime; private String displayBuffer; private double rxLastTime; private int rxPhase; // 0=wait for hi, 1=wait for low, 2=wait for data private int rxBitPos; private int rxCurrentByte; private String txBuffer; private double txLastTime; private int txPhase; // 0=idle, 1=send low, 2=send data, 3=send high private int txBitPos; // GUI elements private TextBox rxPinField; private TextBox txPinField; private TextBox baudField; private SerialTextBox bodyField; public override string Title { get { return "Serial"; } } public override Boolean AllowHotKeys { get { return false; } } public SerialIO() { rxPin = 30; txPin = 31; baud = 57600; displayBuffer = ""; this.BuildGui(); this.OnReset(); } public override void OnReset() { // Setup vars rxLastTime = 0; rxPhase = 0; rxBitPos = 0; rxCurrentByte = 0; txBuffer = ""; txLastTime = 0; txPhase = 0; txBitPos = 0; rxPin = Convert.ToInt32(rxPinField.Text); txPin = Convert.ToInt32(txPinField.Text); baud = Convert.ToInt32(baudField.Text); bitTime = 1.0/baud; } private void BuildGui() { // Build the Plugin UI System.Windows.Forms.TableLayoutPanel tableLayoutPanel = new System.Windows.Forms.TableLayoutPanel(); System.Windows.Forms.Label rxPinLabel = new System.Windows.Forms.Label(); System.Windows.Forms.Label txPinLabel = new System.Windows.Forms.Label(); System.Windows.Forms.Label baudLabel = new System.Windows.Forms.Label(); System.Windows.Forms.Button resetButton = new System.Windows.Forms.Button(); System.Windows.Forms.Button clearButton = new System.Windows.Forms.Button(); this.rxPinField = new System.Windows.Forms.TextBox(); this.txPinField = new System.Windows.Forms.TextBox(); this.baudField = new System.Windows.Forms.TextBox(); this.bodyField = new SerialTextBox(); this.SuspendLayout(); tableLayoutPanel.SuspendLayout(); tableLayoutPanel.ColumnCount = 8; tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 15F)); tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 15F)); tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 15F)); tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 15F)); tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 15F)); tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 15F)); tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 15F)); tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 15F)); tableLayoutPanel.Controls.Add(clearButton, 0, 0); tableLayoutPanel.Controls.Add(rxPinLabel, 1, 0); tableLayoutPanel.Controls.Add(this.rxPinField, 2, 0); tableLayoutPanel.Controls.Add(txPinLabel, 3, 0); tableLayoutPanel.Controls.Add(this.txPinField, 4, 0); tableLayoutPanel.Controls.Add(baudLabel, 5, 0); tableLayoutPanel.Controls.Add(this.baudField, 6, 0); tableLayoutPanel.Controls.Add(resetButton, 7, 0); tableLayoutPanel.Controls.Add(this.bodyField, 0, 1); tableLayoutPanel.SetColumnSpan(this.bodyField, 8); tableLayoutPanel.GrowStyle = System.Windows.Forms.TableLayoutPanelGrowStyle.FixedSize; tableLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill; tableLayoutPanel.Name = "tableLayoutPanel"; tableLayoutPanel.RowCount = 2; tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 30F)); tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); tableLayoutPanel.TabIndex = 6; rxPinLabel.Text = "Rx Pin:"; rxPinLabel.TextAlign = ContentAlignment.MiddleRight; rxPinLabel.Dock = System.Windows.Forms.DockStyle.Fill; txPinLabel.Text = "Tx Pin:"; txPinLabel.TextAlign = ContentAlignment.MiddleRight; txPinLabel.Dock = System.Windows.Forms.DockStyle.Fill; baudLabel.Text = "Baud Rate:"; baudLabel.TextAlign = ContentAlignment.MiddleRight; baudLabel.Dock = System.Windows.Forms.DockStyle.Fill; this.rxPinField.BackColor = System.Drawing.SystemColors.Window; this.rxPinField.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.rxPinField.Multiline = false; this.rxPinField.Text = Convert.ToString(rxPin); this.rxPinField.Dock = System.Windows.Forms.DockStyle.Fill; this.rxPinField.Name = "rxPinField"; this.rxPinField.TabIndex = 1; this.txPinField.BackColor = System.Drawing.SystemColors.Window; this.txPinField.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.txPinField.Multiline = false; this.txPinField.Text = Convert.ToString(txPin); this.txPinField.Dock = System.Windows.Forms.DockStyle.Fill; this.txPinField.Name = "txPinField"; this.txPinField.TabIndex = 2; this.baudField.BackColor = System.Drawing.SystemColors.Window; this.baudField.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.baudField.Multiline = false; this.baudField.Text = Convert.ToString(baud); this.baudField.Dock = System.Windows.Forms.DockStyle.Fill; this.baudField.Name = "baudField"; this.baudField.TabIndex = 3; this.bodyField.BackColor = System.Drawing.SystemColors.Window; this.bodyField.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); //this.bodyField.ReadOnly = true; this.bodyField.Multiline = true; this.bodyField.WordWrap = true; this.bodyField.Name = "bodyField"; this.bodyField.ScrollBars = ScrollBars.Vertical; this.bodyField.TabIndex = 0; this.bodyField.Dock = System.Windows.Forms.DockStyle.Fill; this.bodyField.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.txt_KeyPress); this.bodyField.serial = this; resetButton.Text = "Reset Serial"; resetButton.TabIndex = 4; resetButton.Name = "resetButton"; resetButton.Dock = System.Windows.Forms.DockStyle.Fill; resetButton.Click += new System.EventHandler(this.reset_Click); clearButton.Text = "Clear Text"; clearButton.TabIndex = 4; clearButton.Name = "clearButton"; clearButton.Dock = System.Windows.Forms.DockStyle.Fill; clearButton.Click += new System.EventHandler(this.clear_Click); this.Controls.Add(tableLayoutPanel); tableLayoutPanel.ResumeLayout(false); tableLayoutPanel.PerformLayout(); this.ResumeLayout(false); this.PerformLayout(); this.bodyField.Focus(); } public override void PresentChip(Propeller host) { Chip = host; Chip.NotifyOnClock(this); Chip.DrivePin(txPin, false, true); } public override void OnClock(double time) { this.HandleRx(time); this.HandleTx(time); } private void HandleRx(double time) { bool bit = ((Chip.OUT & ((ulong)1 << rxPin)) != 0); if (rxPhase == 0) { if (bit) { rxPhase = 1; } return; } else if (rxPhase == 1) { if (!bit) { rxPhase = 2; rxLastTime = time + bitTime/2; } return; } else if (rxPhase == 2) { double delta = time - rxLastTime; if (delta > bitTime) { this.GetBit(bit); rxLastTime += bitTime; } } } private void GetBit(bool bit) { if (bit) { rxCurrentByte |= (1 << rxBitPos); } rxBitPos++; if (rxBitPos == 8) { rxBitPos = 0; rxPhase = 0; if (rxCurrentByte == 13 || rxCurrentByte == 10) { displayBuffer += "\r\n"; } else if (rxCurrentByte > 0) { displayBuffer += Convert.ToChar(rxCurrentByte); } rxCurrentByte = 0; this.bodyField.Text = displayBuffer; this.bodyField.SelectionStart = this.bodyField.Text.Length; this.bodyField.ScrollToCaret(); } } private void HandleTx(double time) { if (txPhase == 1) { Chip.DrivePin(txPin, false, false); txPhase = 2; txLastTime = time; return; } else if (txPhase == 2) { double delta = time - txLastTime; if (delta > bitTime) { this.SendBit(); txLastTime += bitTime; } } else if (txPhase == 3) { double delta = time - txLastTime; if (delta > bitTime) { txPhase = ((txBuffer.Length > 0) ? 1 : 0); } return; } } private void SendBit() { if (txBitPos == 8) { txBitPos = 0; txPhase = 3; txBuffer = txBuffer.Substring(1, txBuffer.Length-1); Chip.DrivePin(txPin, true, true); return; } int sendByte = (int)(txBuffer[0]); bool bit = ((sendByte & (1 << txBitPos)) != 0); Chip.DrivePin(txPin, false, bit); txBitPos++; } // Handle Paste public void SendString(String str) { txBuffer += str; displayBuffer += str; this.bodyField.Text = displayBuffer; this.bodyField.SelectionStart = this.bodyField.Text.Length; this.bodyField.ScrollToCaret(); if (txPhase == 0) { txPhase = 1; } } private void ClearText() { displayBuffer = ""; this.bodyField.Text = displayBuffer; } private void txt_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e) { if ((Control.ModifierKeys & Keys.Control) != 0) { return; } e.Handled = true; txBuffer += e.KeyChar; if (e.KeyChar == '\n' || e.KeyChar == '\r') { displayBuffer += "\r\n"; } else { displayBuffer += e.KeyChar; } this.bodyField.Text = displayBuffer; this.bodyField.SelectionStart = this.bodyField.Text.Length; this.bodyField.ScrollToCaret(); if (txPhase == 0) { txPhase = 1; } } private void clear_Click(object sender, System.EventArgs e) { this.ClearText(); this.bodyField.Focus(); } private void reset_Click(object sender, System.EventArgs e) { this.OnReset(); this.bodyField.Focus(); } }