Re: E85 / E86 BMW Z4 – Android tablet integration - INCOMPLE
Posted: Sun Jul 06, 2014 7:38 am
removed
Code: Select all
/*==========================================================================================================================
Created by JS online, [email protected]
Last update 21-May-2015
Arduino R3 UNO code to operate L9935 stepper motor driver.
Main functionality:
1. Opening screen fully - after ignition is turned on or by open button
2. Closing screen fully - after ignition is turned off or by close button
3. Saving monitor last position to Arduino internal EEPROM memory after ignition is turned off (full step only).
4. Opening by small steps - by step open button
5. Closign by small steps - by step close button (min position is limited to half way - the same as in original behavior)
Enable Serial Monitor Ctrl + Shift + M to see debug messages.
==========================================================================================================================*/
#include <SPI.h>
#include <EEPROM.h>
//L9935 pinning
int SLAVESEL=10; //using digital pin 10 for SPI slave select
int DATAOUT=11; //using digital pin 11 for MOSI (Master Out Slave In) - master line for sending data to slave
int DATAIN=12; //using digital pin 12 for MISO (Master In Slave Out) - the slave sending data to the master
int SPICLOCK=13; //using digital pin 13 for SCK (Serial Clock) - The clock pulses which synchronize data transmission generated by the master
int EN=9; //Enable (Low Active)
//Board Monitor Buttons
#define OPEN_BUTTON 6 //montior open button full
#define CLOSE_BUTTON 7 //montior close button full
#define STEP_OPEN 4 //montior open button step
#define STEP_CLOSE 5 //montior close button step
/*Ignition key 12V relay
R3 UNO, L9935 stepper motor (tablet and other consumers) are powered from +12V ACC (Battery) - X18802 18pin white board monitor socket, Pin 8 = +12V ACC (thru fuse F49), Pin 9 = GND).
Timer is used for power off delay option. Timer is activated by +12V IGN (Ignition). Power off delay is required to allow close screen after ignition key is turned off and to do not leave
consumers connected to +12V ACC permanently. Timer logic is the following:
1. after ignition is turned on everything is powered on. Timer Output is operational and connected to +12 ACC: consumers are powered.
2. after ignition is turned off Timer is activated and disconnects +12V Timer Output from +12V Timer Input (ACC) after X seconds (time is adjusted by trimmer and is from 1 sec up to 20 mins).
After timeout Timer disconnects ACC 12V power, so R3 UNO, L9935 stepper motor and other consumers are also unpowered.*/
#define IGN_PWR 8 //igntion 12V power detection. Relay connected to 12V iginition power connects/disconnects GND with IGN_PWR wire.
int ignState = 0;
//Delays - miliseconds used for various delays
int DELAY_SS=1; //very short delay
int DELAY_S=16; //short delay
int DELAY_N=100; //standard delay
int DELAY_L=500; //long delay
int DELAY_INIT=5000; //delay on ignition
//Monitor positioning: over movement protection and last position. Last saved position works by operating buttons only. Positioning is not tracked if monitor is adjusted manually.
//Last position of monitor is saved after ignition 12v power is turned off. Last position of monitor is restored after ingnition 12v power is tuned on.
float POS_CUR=0; //monitor current positon full step counter
int POS_CUR_HS=0; //monitor position half step counter
float POS_STEP_HS=0.25; //monitor position half step
int POS_STEP_MIN=7; //limiting min half step position
float POS_MAX=10.7; //monitor maximum position
int POS_CUR_SAVED=0; //monitor saved position (full step only)
int POS_CUR_ADDR=0; //EEPROM address for storing current position (full step only)
//int ERR_CODE; //read L9935 SPI error code in case if DATAIN pin is connected
// the setup function runs once when you press reset or power the board
void setup() {
pinMode(SLAVESEL, OUTPUT); //CSN
pinMode(DATAOUT, OUTPUT); //SDI
pinMode(DATAIN, INPUT); //SDO not used
pinMode(SPICLOCK, OUTPUT); //SCK
pinMode(EN, OUTPUT); //Enable
pinMode(OPEN_BUTTON, INPUT_PULLUP); //Open Button Full
pinMode(CLOSE_BUTTON, INPUT_PULLUP); //Close Button Full
pinMode(STEP_OPEN, INPUT_PULLUP); //Open Button Step
pinMode(STEP_CLOSE, INPUT_PULLUP); //Close Button Step
pinMode(IGN_PWR, INPUT_PULLUP); //Iginition 12V
SPI.begin(); //wake up the SPI bus.
SPI.setBitOrder(MSBFIRST); //data to be sent MSB (most significant byte) first
SPI.setClockDivider(SPI_CLOCK_DIV16); //1Mhz SPI clock 16/16
SPI.setDataMode(SPI_MODE3); //SPI_MODE3 - CPOL=1 the base value of the clock is one; CPHA=1, data are captured on clock's rising edge and data is propagated on a falling edge
// initialize serial communication at 9600 bits per second:
Serial.begin(9600);
//detect initial state of ignition 12V power
ignState = digitalRead(IGN_PWR);
if (ignState==LOW) {
//ignition 12V power is on
//power on delay
delay(DELAY_INIT);
readPOS_CUR(POS_CUR_ADDR); //reading current monitor position from internal EEPROM memory
screenUp(POS_CUR_SAVED);
}
else {
//ignition 12V power is off
screenDown();
}
}
// the loop function runs over and over again forever
void loop() {
int switchValue;
switchValue = digitalRead(IGN_PWR);
if (switchValue != ignState)
{
if (switchValue==LOW) {
//ignition 12V power has been turned on
//power on delay
delay(DELAY_INIT);
readPOS_CUR(POS_CUR_ADDR); //reading current monitor position from internal EEPROM memory
screenUp(POS_CUR_SAVED);
}
else {
//ignition 12V power has been turned off
writePOS_CUR(POS_CUR_ADDR, POS_CUR); //writing current monitor position to internal EEPROM memory
screenDown();
}
//udpate state
ignState = switchValue;
}
switchValue = digitalRead(OPEN_BUTTON);
if (switchValue==LOW) {
screenUp(-1); //sending -1 to execute standard logic without restoring last position
}
switchValue = digitalRead(CLOSE_BUTTON);
if (switchValue==LOW) {
screenDown();
}
switchValue = digitalRead(STEP_OPEN);
if (switchValue==LOW) {
activateL9935EN();
stepUpHalf();
}
switchValue = digitalRead(STEP_CLOSE);
if (switchValue==LOW) {
activateL9935EN();
stepDownHalf();
}
}
/* L9935 basic info. For more info see L9935 technical data sheet.
Command byte: 6 first bits (0-5) are used for command, Last 2 bits (6,7) are used for command response - return error code (not processed in current code, can be read and processed thru DATAIN pin).
Command byte bits: 76543210
bit5,bit4: current range of bridge A (Outputs A1 and A2)
bit3: polarity of bridge A
bit2,bit1: current range of bridge B (Outputs B1 and B2)
bit0: polarity of bridge B
bit7,bit6: Error1 and Error 2
Command byte should be converted from binary to decimal (00001111 = 15) and passed as decimal to setValue function.
Current settings (bit5 bit4 for A1 and A2 or bit2 bit1 for B1 and B2). HIGH=H=1; LOW=L=0;
H H = 1 1 = 0 - not used in current code
H L = 1 0 = 60 mA 6% internally sensed - used in current code for parking, screen can be moved manually in this mode.
L H = 0 1 = 550 mA 61 % - used in current code for step opening and step closing
L L = 0 0 = 900 mA 100 % - used in current code for opening and closing screen fully
*/
//functions
void setSPIValue(int SPI_COMMAND)
{
digitalWrite(SLAVESEL, LOW); //Start of frame. Falling slope of CSN indicates start of frame.
SPI.transfer(SPI_COMMAND); //Send value (0~255) in decimal format. Data transfer (reading SDI into the register) takes place at the rising slopes of SCK.
// ERR_CODE = digitalRead(DATAIN); //Read error code
digitalWrite(SLAVESEL, HIGH); //End of frame
delay(DELAY_S);
}
void activateL9935EN(void) {
digitalWrite(EN, HIGH); //disable L9935
delay(DELAY_SS);
digitalWrite(EN, LOW); //enable L9935. Falling slope of EN activates the device. After ten.sck the device is ready to work.
delay(DELAY_SS);
}
void screenUp(float POS) {
activateL9935EN();
float b;
//Initial opening: saved positon from internal EEPROM will be used. Other cases: standard logic calculating current and max position.
if (POS >= 0) b = POS; else b = POS_MAX-POS_CUR;
for (int a = 0; a < b; a++)
{
stepUp();
}
screenPark();
}
void screenDown(void) {
activateL9935EN();
int b = POS_CUR;
while (POS_CUR >= 1)
{
stepDown();
}
POS_CUR=0; //resetting current position
POS_CUR_HS=0; //resetting half step position
screenPark();
}
void stepUp(void) {
//full step mode processing for complete opening;
//POS_CUR direction 0 --> 1 --> 2 --> 3
/*States:
0 = CH A enabled, polarity=HIGH. CH B disabled.
1 = CH A disabled. CH B disabled, polarity=LOW.
2 = CH A enabled, polarity = LOW. CH B disabled.
1 = CH A disabled. CH B enabled, polarity=HIGH.
*/
if(POS_CUR <= (POS_MAX - 1)) {
setSPIValue(15); //00001111 CH A enabled, polarity=HIGH (Current=900mA). CH B disabled.
setSPIValue(56); //00111000 CH A disabled. CH B enabled, polarity=LOW (Current=900mA).
setSPIValue(6); //00000110 CH A enabled, polarity=LOW (Current=900mA). CH B disabled.
setSPIValue(57); //00111001 CH A disabled. CH B enabled, polarity=HIGH (Current=900mA).
POS_CUR += 1;
Serial.print("POS_CUR=");Serial.println(POS_CUR);
}
}
void stepDown(void) {
//full step mode processing for complete closing;
//POS_CUR direction 0 --> 3 --> 2 --> 1
if(POS_CUR > 0) {
setSPIValue(15); //00001111 CH A enabled, polarity=HIGH (Current=900mA). CH B disabled.
setSPIValue(57); //00111001 CH A disabled. CH B enabled, polarity=HIGH (Current=900mA).
setSPIValue(7); //00000111 CH A enabled, polarity=LOW (Current=900mA). CH B disabled.
setSPIValue(48); //00110000 CH A disabled. CH B enabled, polarity=LOW (Current=900mA).
POS_CUR -= 1;
Serial.print("POS_CUR=");Serial.println(POS_CUR);
}
}
void screenPark(void) {
/*Parking allows manual movement of screen by hand (counter is not working in this case).
Stepper motor current is set to HL (internally sensed).
HH will not fix monitor at all as current is 0.
HL and LL with 550mA and 900mA current will hold monitor and will not allow manual movement.
*/
setSPIValue(47); //00101111 CH A enabled, polarity=HIGH. CH B disabled (Current 60mA).
delay(DELAY_S);
}
void stepUpHalf(void) {
/*Half steps can be used here by using middle states (currently not used) - step 1.2, step 2.2, step 3.2, step 4.2.
In this case both channels are enabled, use current max 550mA (avoid higher current 900mA, overheating risk exists).
*/
if(POS_CUR <= (POS_MAX - POS_STEP_HS) && POS_CUR >= POS_STEP_MIN) {
//POS_CUR_HS direction 0 --> 1 --> 2 --> 3
if(POS_CUR_HS == 0) {
setSPIValue(31); //00011111 CH A enabled, polarity=HIGH (Current=550mA). CH B disabled. --current step
setSPIValue(58); //00111010 CH A disabled. CH B enabled, polarity=LOW. (Current=550mA). --next step
setSPIValue(60); //00111100 CH A disabled. CH B enabled, polarity=LOW (Current=60mA). --park
Serial.print("STEP1");
}
if(POS_CUR_HS == 1) {
setSPIValue(58); //00111010 CH A disabled. CH B enabled, polarity=LOW (Current=550mA). --current step
setSPIValue(22); //00010110 CH A enabled, polarity=LOW (Current=550mA). CH B disabled. --next step
setSPIValue(38); //00100110 CH A enabled, polarity=LOW (Current=60mA). CH B disabled. --park
Serial.print("STEP2");
}
if(POS_CUR_HS == 2) {
setSPIValue(22); //00010110 CH A enabled, polarity=LOW (Current=550mA). CH B disabled. --current step
setSPIValue(51); //00110011 CH A disabled. CH B enabled, polarity=HIGH (Current=550mA). --next step
setSPIValue(53); //00110101 CH A disabled. CH B enabled, polarity=HIGH (Current=60mA). --park
Serial.print("STEP3");
}
if(POS_CUR_HS == 3) {
setSPIValue(51); //00110011 CH A disabled. CH B enabled, polarity=HIGH (Current=550mA). --current step
setSPIValue(31); //00011111 CH A enabled (Current=550mA), polarity=HIGH. CH B disabled. --next step
setSPIValue(47); //00101111 CH A enabled (Current=60mA), polarity=HIGH. CH B disabled. --park
Serial.print("STEP4");
}
if(POS_CUR_HS == 3) POS_CUR_HS=0; else POS_CUR_HS +=1;
Serial.print(" POS_CUR_HS=");Serial.print(POS_CUR_HS);
POS_CUR += POS_STEP_HS;
Serial.print(" POS_CUR=");Serial.println(POS_CUR);
delay(DELAY_L);
}
}
void stepDownHalf(void) {
/*Half steps can be used here by using middle states (currently not used) - step 1.2, step 2.2, step 3.2, step 4.2.
In this case both channels are enabled, use current max 550mA (avoid 900mA current, overheating risk exists).
*/
if(POS_CUR > POS_STEP_MIN) {
//POS_CUR_HS direction 0 --> 3 --> 2 --> 1
if(POS_CUR_HS == 0) {
setSPIValue(31); //00011111 CH A enabled, polarity=HIGH (Current=550mA). CH B disabled. --current step
setSPIValue(59); //00111011 CH A disabled. CH B enabled, polarity=HIGH (Current=550mA). --next step
setSPIValue(61); //00111101 CH A disabled. CH B enabled, polarity=HIGH (Current=60mA). --park
Serial.print("STEP1");
}
if(POS_CUR_HS == 3) {
setSPIValue(59); //00111011 CH A disabled. CH B enabled, polarity=HIGH (Current=550mA). --current step
setSPIValue(23); //00010111 CH A enabled, polarity=LOW. CH B disabled (Current=550mA). --next step
setSPIValue(39); //00100111 CH A enabled, polarity=LOW. CH B disabled (Current=60mA). --park
Serial.print("STEP2");
}
if(POS_CUR_HS == 2) {
setSPIValue(23); //00010111 CH A enabled, polarity=LOW. CH B disabled (Current=550mA). --current step
setSPIValue(58); //00111010 CH A disabled. CH B enabled, polarity=LOW (Current=550mA). --next step
setSPIValue(60); //00111100 CH A disabled. CH B enabled, polarity=LOW (Current=60mA). --park
Serial.print("STEP3");
}
if(POS_CUR_HS == 1) {
setSPIValue(58); //00111010 CH A disabled. CH B enabled, polarity=LOW (Current=550mA). --current step
setSPIValue(31); //00011111 CH A enabled, polarity=HIGH (Current=550mA). CH B disabled. --next step
setSPIValue(47); //00101111 CH A enabled, polarity=HIGH (Current=60mA). CH B disabled. --park
Serial.print("STEP4");
}
if(POS_CUR_HS == 0) POS_CUR_HS=3; else POS_CUR_HS -=1;
Serial.print(" POS_CUR_HS=");Serial.print(POS_CUR_HS);
POS_CUR -= POS_STEP_HS;
Serial.print(" POS_CUR=");Serial.println(POS_CUR);
delay(DELAY_L);
}
}
void readPOS_CUR(int ADDR) {
//Reads a byte from the EEPROM. Locations that have never been written to have the value of 255.
//Serial.println("Reading current position from internal EEPROM!");
POS_CUR_SAVED = EEPROM.read(ADDR);
Serial.print("Reading DONE! Current position: ");Serial.println(POS_CUR_SAVED);
}
void writePOS_CUR(int ADDR, int VAL) {
//Write a byte to the EEPROM. The value is written only if differs from the one already saved at the same address.
//Serial.print("Writing current positon to internal EEPROM: "); Serial.println(POS_CUR);
EEPROM.update(ADDR, VAL);
Serial.println("Writing DONE!");
}