Jump to content
43oh

(Dual) Monitor auto windows desktop rotation


Recommended Posts

Hi Everyone,

 

my first project with the launch pad is to automatically rotate my windows desktop orientation when I rotate my monitors.

 

I have been working with dual monitors for a while but recently got a monoprice dual monitor stand that allows rotation.

http://www.monoprice.com/products/product.asp?c_id=109&cp_id=10828&cs_id=1082808&p_id=5560&seq=1&format=2

 

I then had the idea to implement my own hardware sensor to detect when they had been rotated to portrait (or even up-side-down).

 

The circuit is simple and the pictures should explain everything.

 

post-261-135135493963_thumb.jpg

 

The hardware is the standard TI launchpad development board, together with Qty.4 tilt switches.

I made a shield with two of the tilt switches on the launch pad (for mouing on the rear of monitor 1) and a mini pcb with the other two tilt switches (for the rear of monitor 2). Some wires a routed between the two.

 

I'm using 4 pins as digital input pulled high. the tilt switches short to ground when closing the circuit. 2 tilt switches for each monitor encode the 4 possible positions (for each monitor).

 

By placing the tilt switchers at 45 degrees to up-down and left-right there is good stability.

 

Power, VCC and Qty.1 digital output (currently assigned to launch pad LEDs) are brought out to each PCB for any future expansion (e.g. ambient rear illumination etc.),

 

 

 

The launchpad had version 0.01 of my general purpose IO code so that a windows program can read the status via com port serial command ("r").

 

here is the launchpad code:

 

 

// General purpose serial I/O (9600 baud) for launchpad 
// Peter R.H. 12/30/2010 onwards
// heavily modified example 9600 uart code and other items

// history
// 1/1/2011 - "t" command not yet working.

// original uart example notes 
//******************************************************************************
//  MSP430G2xx1 Demo - Timer_A, Ultra-Low Pwr UART 9600 Echo, 32kHz ACLK
//
//  Description: Use Timer_A CCR0 hardware output modes and SCCI data latch
//  to implement UART function @ 9600 baud. Software does not directly read and
//  write to RX and TX pins, instead proper use of output modes and SCCI data
//  latch are demonstrated. Use of these hardware features eliminates ISR
//  latency effects as hardware insures that output and input bit latching and
//  timing are perfectly synchronised with Timer_A regardless of other
//  software activity. In the Mainloop the UART function readies the UART to
//  receive one character and waits in LPM3 with all activity interrupt driven.
//  After a character has been received, the UART receive function forces exit
//  from LPM3 in the Mainloop which configures the port pins (P1 & P2) based
//  on the value of the received byte (i.e., if BIT0 is set, turn on P1.0).

//  ACLK = TACLK = LFXT1 = 32768Hz, MCLK = SMCLK = default DCO
//  //* An external watch crystal is required on XIN XOUT for ACLK *//  
//
//               MSP430G2xx1
//            -----------------
//        /|\|              XIN|-
//         | |                 | 32kHz
//         --|RST          XOUT|-
//           |                 |
//           |   CCI0B/TXD/P1.1|-------->
//           |                 | 9600 8N1
//           |   CCI0A/RXD/P1.2|<--------
//
//  D. Dang
//  Texas Instruments Inc.
//  October 2010
//  Built with CCS Version 4.2.0 and IAR Embedded Workbench Version: 5.10
//******************************************************************************


#include "msp430g2231.h"


//------------------------------------------------------------------------------
// Hardware-related definitions
//------------------------------------------------------------------------------
#define UART_TXD   0x02                     // TXD on P1.1 (Timer0_A.OUT0)
#define UART_RXD   0x04                     // RXD on P1.2 (Timer0_A.CCI1A)

//------------------------------------------------------------------------------
// Conditions for 9600 Baud SW UART, SMCLK = 1MHz
//------------------------------------------------------------------------------
#define UART_TBIT_DIV_2     (1000000 / (9600 * 2))
#define UART_TBIT           (1000000 / 9600)

//------------------------------------------------------------------------------
// Global variables used for full-duplex UART communication
//------------------------------------------------------------------------------
unsigned int txData;                        // UART internal variable for TX
unsigned char rxBuffer;                     // Received UART character

//------------------------------------------------------------------------------
// Function prototypes
//------------------------------------------------------------------------------
void TimerA_UART_init(void);
void TimerA_UART_tx(unsigned char byte);
void TimerA_UART_print(char *string);
//void ConfigureAdcTempSensor(void);

//  vars

long temp;
long IntDegF;
long IntDegC;

unsigned int TXByte;


//------------------------------------------------------------------------------
// main()
//------------------------------------------------------------------------------
void main(void)
{

   WDTCTL = WDTPW + WDTHOLD;               // Stop watchdog timer

   //for temperature reading
   TACTL = TASSEL_2 | MC_2;
   while (TAR < 30);
   // ---

   DCOCTL = 0x00;                          // Set DCOCLK to 1MHz
   BCSCTL1 = CALBC1_1MHZ;
   DCOCTL = CALDCO_1MHZ;

   P1OUT = 0x00;                           // Initialize all GPIO

   P1SEL = UART_TXD + UART_RXD;            // Timer function for TXD/RXD pins
   // Set all pins but RXD, P1.3, P1.4, P1.5, P1.7 to output
   P1DIR = 0xFF & ~UART_RXD & ~0x08 & ~0x10 & ~0x20 & ~0x80; 

   //unable to set P1.3 to pull low so set all the others to pull up  (!)
   P1IN |= BIT4; //enables input
   P1REN |= BIT4; // enables resistor
   P1OUT |= BIT4; //resistor set to pull up

   P1IN |= BIT5; //enables input
   P1REN |= BIT5; // enables resistor 
   P1OUT |= BIT5; //resistor set to pull up

   P1IN |= BIT7; //enables input
   P1REN |= BIT7; // enables resistor
   P1OUT |= BIT7; //resistor set to pull up

   P2OUT = 0x00;
   P2SEL = 0x00;
   P2DIR = 0xFF;

   __enable_interrupt();


   TimerA_UART_init();                     // Start Timer_A UART
   TimerA_UART_print("\r\r\rREADY.\r\n");

   for (;
   {


       // Wait for incoming character
       __bis_SR_register(LPM0_bits);

       // Echo received character
       TimerA_UART_tx(rxBuffer);

       // Update board outputs according to received byte
       if (rxBuffer == '1') P1OUT |= 0x01;
       if (rxBuffer == '!') P1OUT &= ~0x01;
       if (rxBuffer == '6') P1OUT |= 0x40;
       if (rxBuffer == '^') P1OUT &= ~0x40;

       if (rxBuffer == 't') 
       {

	ADC10CTL0 |= ENC + ADC10SC; // Sampling and conversion start
       __bis_SR_register(CPUOFF + GIE); // LPM0 with interrupts enabled
   	temp = ADC10MEM;
       	// oF = ((A10/1024)*1500mV)-923mV)*1/1.97mV = A10*761/1024 - 468
   		IntDegF = ((temp - 630) * 761) / 1024;
   		// oC = ((A10/1024)*1500mV)-986mV)*1/3.55mV = A10*423/1024 - 278
   		IntDegC = ((temp - 673) * 423) / 1024;

       	TXByte = (unsigned char)(IntDegC);
       	TimerA_UART_tx(TXByte);
       	TXByte = (unsigned char)(',');
       	TimerA_UART_tx(TXByte);
       	TXByte = (unsigned char)(IntDegF);
       	TimerA_UART_tx(TXByte);
       }

       if (rxBuffer == 'r')  
       {
       	TimerA_UART_tx(';');

       	 if ((8 & P1IN)) TimerA_UART_tx('1');       // if P1.3 set
   		 else TimerA_UART_tx('0');

   		 if ((16 & P1IN)) TimerA_UART_tx('1');      // if P1.4 set
   		 else TimerA_UART_tx('0');rrr

       	 if ((32 & P1IN)) TimerA_UART_tx('1');      // if P1.5 set
   		 else TimerA_UART_tx('0');

       	 if ((128 & P1IN)) TimerA_UART_tx('1');     // if P1.6 set
   		 else TimerA_UART_tx('0');

       	TimerA_UART_tx('.');
       }

   }
}
//------------------------------------------------------------------------------
// Function configures Timer_A for full-duplex UART operation
//------------------------------------------------------------------------------
void TimerA_UART_init(void)
{
   TACCTL0 = OUT;                          // Set TXD Idle as Mark = '1'
   TACCTL1 = SCS + CM1 + CAP + CCIE;       // Sync, Neg Edge, Capture, Int
   TACTL = TASSEL_2 + MC_2;                // SMCLK, start in continuous mode
}
//------------------------------------------------------------------------------
// Outputs one byte using the Timer_A UART
//------------------------------------------------------------------------------
void TimerA_UART_tx(unsigned char byte)
{
   while (TACCTL0 & CCIE);                 // Ensure last char got TX'd
   TACCR0 = TAR;                           // Current state of TA counter
   TACCR0 += UART_TBIT;                    // One bit time till first bit
   TACCTL0 = OUTMOD0 + CCIE;               // Set TXD on EQU0, Int
   txData = byte;                          // Load global variable
   txData |= 0x100;                        // Add mark stop bit to TXData
   txData <<= 1;                           // Add space start bit
}

//------------------------------------------------------------------------------
// Prints a string over using the Timer_A UART
//------------------------------------------------------------------------------
void TimerA_UART_print(char *string)
{
   while (*string) {
       TimerA_UART_tx(*string++);
   }
}
//------------------------------------------------------------------------------
// Timer_A UART - Transmit Interrupt Handler
//------------------------------------------------------------------------------
#pragma vector = TIMERA0_VECTOR
__interrupt void Timer_A0_ISR(void)
{
   static unsigned char txBitCnt = 10;

   TACCR0 += UART_TBIT;                    // Add Offset to CCRx
   if (txBitCnt == 0) {                    // All bits TXed?
       TACCTL0 &= ~CCIE;                   // All bits TXed, disable interrupt
       txBitCnt = 10;                      // Re-load bit counter
   }
   else {
       if (txData & 0x01) {
         TACCTL0 &= ~OUTMOD2;              // TX Mark '1'
       }
       else {
         TACCTL0 |= OUTMOD2;               // TX Space '0'
       }
       txData >>= 1;
       txBitCnt--;
   }
}      
//------------------------------------------------------------------------------
// Timer_A UART - Receive Interrupt Handler
//------------------------------------------------------------------------------
#pragma vector = TIMERA1_VECTOR
__interrupt void Timer_A1_ISR(void)
{
   static unsigned char rxBitCnt = 8;
   static unsigned char rxData = 0;

   switch (__even_in_range(TAIV, TAIV_TAIFG)) { // Use calculated branching
       case TAIV_TACCR1:                        // TACCR1 CCIFG - UART RX
           TACCR1 += UART_TBIT;                 // Add Offset to CCRx
           if (TACCTL1 & CAP) {                 // Capture mode = start bit edge
               TACCTL1 &= ~CAP;                 // Switch capture to compare mode
               TACCR1 += UART_TBIT_DIV_2;       // Point CCRx to middle of D0
           }
           else {
               rxData >>= 1;
               if (TACCTL1 & SCCI) {            // Get bit waiting in receive latch
                   rxData |= 0x80;
               }
               rxBitCnt--;
               if (rxBitCnt == 0) {             // All bits RXed?
                   rxBuffer = rxData;           // Store in global variable
                   rxBitCnt = 8;                // Re-load bit counter
                   TACCTL1 |= CAP;              // Switch compare to capture mode
                   __bic_SR_register_on_exit(LPM0_bits);  // Clear LPM0 bits from 0(SR)
               }
           }
           break;
   }
}
//------------------------------------------------------------------------------


// ADC10 interrupt service routine
#pragma vector=ADC10_VECTOR
__interrupt void ADC10_ISR (void)
{
 __bic_SR_register_on_exit(CPUOFF);        // Clear CPUOFF bit from 0(SR)
}



 

On the windows desktop side I am using the free software iRotate which provide hotkeys for roating the monitor layout.

http://www.entechtaiwan.com/util/irotate.shtm

 

I then coded an AutoIt program to poll the launchpad via serial and simualte pressign the correct irotate hot-keys.

I have the irotate program and this autoit one in my windows startup folder.

 

If you duplicate this project you may have to change this code if your tilt switches are different orientations and/or pins

 

my autoit code is:

; Monitor rotate code
; Peter 1/1/2011

; based upon serial code by "martin"
; http://www.autoitscript.com/forum/topic/45842-serial-port-com-port-udf/
; http://www.mosaiccgl.co.uk/AutoItDownloads/confirm.php?get=COMMGv2.zip


#include 
#include 'CommMG.au3';or if you save the commMg.dll in the @scripdir use #include @SciptDir & '\commmg.dll'
#include 
#include 
#include 
#include 



; keep track of new/old monitor states
$m1old=""
$m1=""
$m2old=""
$m2=""

; ^ = Control , + = shift , ! = ALT

; monitor 1 HotKeys
$sup1="^!{UP}"
$sdown1="^!{DOWN}"
$sleft1="^!{LEFT}"
$sright1="^!{RIGHT}"

; monitor 2 HotKeys
$sup2="^+{UP}"
$sdown2="^+{DOWN}"
$sleft2="^+{LEFT}"
$sright2="^+{RIGHT}"



while ProcessExists("iRotate.exe")=False
sleep(1000) ; allow irotate to start up
WEnd
sleep(2000)


$serror=""
_CommSetPort(5, $serror, 9600, 8, 0, 1, 0, 0, 0)

while 1

sleep(200)
_CommSendString("r",0)
sleep(500)
$sread = _CommGetLine(".",10,800)
;MsgBox(0,"responce",$sread)

   ;monitor 1
   $m1=stringmid($sread,4,2)
if $m1<>$m1old Then
	switch $m1
	case "00"
		send($sup1,0)
	case "10"
		send($sright1,0)
	case "01"
		send($sleft1,0)
	case "11"
		send($sdown1,0)
	Endswitch
EndIf
$m1old=$m1

  ;monitor 2
   $m2=stringmid($sread,3,1)&stringmid($sread,6,1)
if $m2<>$m2old Then
	switch $m2
	case "00"
		send($sup2,0)
	case "10"
		send($sright2,0)
	case "01"
		send($sleft2,0)
	case "11"
		send($sdown2,0)
	Endswitch
EndIf
$m2old=$m2

sleep(1300)
WEnd

 

It uses the autoit serial routines from "Martin"

http://www.autoitscript.com/forum/topic/45842-serial-port-com-port-udf/

 

Enjoy! ;)

 

circuit pictures in the next post.

post-261-135135493973_thumb.jpg

post-261-13513549398_thumb.jpg

Link to post
Share on other sites
Awesome nemi! Thanks for sharing. Never heard about autoit, but it looks like a very handy tool.

 

Where did you get your tilt sensors from?

 

surplus electronics for the tilt switches. I got the cheapest single pin type. Need to solder or contact the case for the other terminal.

http://www.surplus-electronics-sales.com/Zencart/index.php?main_page=product_info&cPath=28&products_id=316

 

autoit is a very handy language for automating widnows programs that don't have command line options or an API.

http://www.autoitscript.com/autoit3/index.shtml

Link to post
Share on other sites
Only 3 wires needed on the extension pcb? It would look nicer with a 3.5mm audio patch cable. Dollar store + radio shack for the sockets (or taken from scrap computer/soundcard)

 

1/8" Stereo Panel-Mount Audio Jack (2-Pack) 2.99

http://www.radioshack.com/product/index ... Id=2103452

 

Good idea. though as I said in the post, I routed VCC and an output line to the mini board for future expansion - such as rear illumination of the monitors (nice on the eyes at night). So I need a 5+ pin connector. I may just look into cheap rj46 jacks and use a network cable.

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...