(Arduino Uno reflashed as USB HID) How to send Scroll Lock key? - arduino

I am having a hard time properly using USB HID buffers, and want to send the scroll lock key when I press the button.
I have tried sending what I thought was scroll lock (0x47) and it sends the ctrl, left alt, and right alt keys instead.
I used the table here for reference: https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf
This is loosely based off of http://mitchtech.net/arduino-usb-hid-keyboard/
/* Arduino USB Keyboard HID demo
* Cut/Copy/Paste Keys
*/
#define SCRLCK 0x47
//#define KEY_LEFT_SHIFT 0x02
//#define KEY_RIGHT_CTRL 0x10
//#define KEY_RIGHT_SHIFT 0x20
uint8_t buf[8] = {
0 }; /* Keyboard report buffer */
#define PIN_PTT 2
//#define PIN_CUT 6
//#define PIN_PASTE 7
int state = 1;
int prevState;
void setup()
{
Serial.begin(9600);
pinMode(PIN_PTT, INPUT);
//pinMode(PIN_CUT, INPUT);
//pinMode(PIN_PASTE, INPUT);
// Enable internal pull-ups
digitalWrite(PIN_PTT, 1);
//digitalWrite(PIN_CUT, 1);
//digitalWrite(PIN_PASTE, 1);
delay(200);
}
void loop()
{
state = digitalRead(PIN_PTT);
if (prevState != state) {
buf[0] = SCRLCK; // Scroll Lock
// buf[2] = 27; // Letter X
// buf[2] = 123; // Cut key: Less portable
Serial.write(buf, 8); // Send keypress
prevState = state;
releaseKey();
}
}
void releaseKey()
{
buf[0] = 0;
Serial.write(buf, 8); // Release key
delay(100);
}
I expect scroll lock to be sent, but upon using passmark KeyboardTest I see that ctrl, left alt, and right alt are sent instead.

You should send the first key code starting at offset 2:
buf[2] = SCRLCK;
Offset 0 is used for key modifier flags like ctrl, alt, etc.

Related

Problems with interrupt arduino nano with NRF24le1

I am trying to build a wireless control of an LED Stripe.
For this I use 2 Arduino Nano's with a NRF24le1 chip to communicate with each other.
Arduino 1 serves as a controller / master and has 3 buttons and 1 fader for the choice of the respective light mode / blackout, as well as the to serve light intensity.
I would like the values ​​as 6 bytes (button1, button2, button3,
faderintensiät) and received on the 2nd Arduino.
Not constant values ​​are sent, only at change of values.
On the 2nd Nano should then be activated by interrupt flags, if an button press what and the current value from Fader Held so that the loop stays clear and there via the RGBW Led Stripe, ever after which flag is selected, can be iterated.
How can I attach the interrupt? On my Transmitter Adruino the stream is running
This is my code:
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h> // Required for 16 MHz Adafruit Trinket
#endif
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#define LED_PIN 3
#define LED_COUNT 228
uint8_t BRIGHTNESS = 60;
// Declare our NeoPixel strip object:
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRBW + NEO_KHZ800);
RF24 wirelessSPI(7, 8); // CE, CSN
#define PinIRQ 3 // Arduino Uno, Mega und Nano: Pin 3
#define IRQ 1
const byte address[6] = "00001";
boolean light_on = false;
boolean mode_a_active = false;
boolean mode_b_active = false;
boolean receivedMessage = false;
int interruptcounter = 0;
uint8_t settings[6] = {0, 0, 0, 0, 0, 0}; //Saving the incoming data
void setup() {
pinMode(6, OUTPUT);
Serial.begin(9600);
wirelessSPI.begin();
//wirelessSPI.setAutoAck(1); //new
//wirelessSPI.enableAckPayload(); //new
//wirelessSPI.maskIRQ(1,1,0); //new
wirelessSPI.setPALevel(RF24_PA_MIN); //You can set this as minimum or maximum depending on the distance between the transmitter and receiver.
wirelessSPI.openReadingPipe(0, address); //Setting the address at which we will receive the data
wirelessSPI.startListening(); //This sets the module as receiver
strip.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
strip.show(); // Turn OFF all pixels ASAP
strip.setBrightness(50); // Set BRIGHTNESS to about 1/5 (max = 255)
attachInterrupt(IRQ, incomingmessage, HIGH);
}
void loop()
{
wirelessSPI.read(&settings, sizeof(settings)); //Reading the data
if(receivedMessage == true){
Serial.println("messange on interupt received");
Serial.print("interruptcounter counts: ");
Serial.println(interruptcounter);
Serial.println();
Serial.print(settings[0]);
Serial.print(settings[1]);
Serial.print(settings[2]);
Serial.print(settings[3]);
Serial.print(settings[4]);
Serial.println(settings[5]);
receivedMessage = false;
}
}
void incomingmessage()
{
interruptcounter++;
while (wirelessSPI.available()) {
wirelessSPI.read(&settings, sizeof(settings)); //Reading the data
receivedMessage = true; // Variable setzen, dass eine neue Nachricht zur Auswertung bereit steht
}
}

Get Arduino Serial data from RF remote with RF receiver having TX

I have this 433 Mhz remote with 12 key using PT2264 and having universal receiver module to decode signal from this remote. Receiver module output in TX pin with 9600 baud rate.
It send data after receiving from remote as REMOTE_ID:KEY_NUMBER.
for example after pressing key 1 from remote I get #AAAA:01 this as data.
Problem is that it does not send any data if pressed once. Instead we have to keep remote key pressed to transmit key code. This creates a burst of continuous data for that key in format #AAAA:01.
Now my problem is I want to interface this remote to ESP8266 and toggle a http resource. I understand this very typical setup, but i need it this way. My problem is how to detect multiple same key as one event on serial so that if it happens again I can achieve the toggle action.
So in short i want to toggle a resource upon pressing switch on remote.
Hardware setup is simple:
Ive attached 433Mhx receiver modules TX pin to ESP8266's D13 and using SoftwareSerial to read data.
Currently I get :
#AAAA:01#AAAA:01#AAAA:01#AAAA:01#AAAA:01#AAAA:01#AAAA:01#AAAA:01#AAAA:01#AAAA:01
I want it only to detect as one #AAAA:01
but if pressed again after few seconds its second #AAAA:01 with which we can toggle some variable.
code is simple.
#include <SoftwareSerial.h>
SoftwareSerial mySerial(12, 14, false, 256);//RX, TX
String readString; //main captured String
int buttonState = LOW; //this variable tracks the state of the button, low if not pressed, high if pressed
int ledState = 0; //this variable tracks the state of the LED, negative if off, positive if on
long lastDebounceTime = 0; // the last time the output pin was toggled
long debounceDelay = 10; // the debounce time; increase if the output flickers
const byte interruptPin = 13;
void setup() {
pinMode(interruptPin, INPUT_PULLUP);
Serial.begin(9600);
mySerial.begin(9600);
}
void loop() {
if (mySerial.available()) {
char c = mySerial.read(); //gets one byte from serial buffer
if (c == '#') {
//do stuff
int ind1 = readString.indexOf(':');
String id = readString.substring(0, ind1);
String key = readString.substring(ind1 + 1);
Serial.println(id);
if (id == "AAAA") {
Serial.println(millis());
if ((millis() - lastDebounceTime) > 50 ) {
//lastDebounceTime = millis();
Serial.println("key Pressed: ");
Serial.println(key);
}
}
readString = ""; //clears variable for new input
id = "";
key = "";
}
else {
readString += c; //makes the string readString
}
}
}
I tried debauching like code with millis() but it did not work.

Trying to send a float value over SPI between 2 Arduinos

I am currently trying to send a float value across two Arduinos via SPI. Currently I am working to send a static value of 2.25 across and then read it via the Serial.println() command. I would then want to pass a float value from a linear displacement sensor. My end goal is to be able to have the master ask for information, the slave gathers the appropriate data and packages it and then master receives said data and does what it needs with it.
Currently I am getting an error "call of overloaded 'println(byte [7])' is ambiguous" and I am not to sure why I am getting this error. I am currently a mechanical engineering student and I am crash-coursing myself through C/C++. I am not entirely positive about what I am doing. I know that a float is 4 bytes and I am attempting to create a buffer of 7 bytes to store the float and the '\n' char with room to spare. My current code is below.
Master:
#include <SPI.h>
void setup() {
pinMode(SS,OUTPUT);
digitalWrite(SS,HIGH);
SPI.begin();
SPI.setClockDivider(SPI_CLOCK_DIV4);
}
void loop() {
digitalWrite(SS,LOW);
float a = 2.25;
SPI.transfer(a);
SPI.transfer('\n');
digitalWrite(SS,HIGH);
}
My slave code is as follows:
#include <SPI.h>
byte buf[7];
volatile byte pos = 0;
volatile boolean process_it = false;
void setup() {
Serial.begin(9600);
pinMode(MISO,OUTPUT);
digitalWrite(MISO,LOW);
SPCR |= _BV(SPE); // SPI Enable, sets this Arduino to Slave
SPCR |= _BV(SPIE); // SPI interrupt enabled
}
ISR(SPI_STC_vect) {
// Interrupt Service Routine(SPI_(SPI Transfer Complete)_vector)
byte c = SPDR;
// SPDR = SPI Data Register, so you are saving the byte of information in that register to byte c
if (pos < sizeof buf) {
buf[pos++] = c;
if (c == '\n') {
process_it = true;
}
}
}
void loop() {
if (process_it = true) {
Serial.println(buf);
pos = 0;
process_it = false;
}
}
I figured out what I needed to do and I wanted to post my finished code. I also added an ability to transfer more than one float value.
Master:
#include <SPI.h>
float a = 3.14;
float b = 2.25;
uint8_t storage [12];
float buff[2] = {a, b};
void setup()
{
digitalWrite(SS, HIGH);
SPI.begin();
Serial.begin(9600);
SPI.setClockDivider(SPI_CLOCK_DIV8);
}
void loop()
{
digitalWrite(SS, LOW);
memcpy(storage, &buff, 8);
Serial.print("storage[0] = "); Serial.println(storage[0]); // the
following serial prints were to check i was getting the right decimal
numbers for the floats.
Serial.print("storage[1] = "); Serial.println(storage[1]);
Serial.print("storage[2] = "); Serial.println(storage[2]);
Serial.print("storage[3] = "); Serial.println(storage[3]);
Serial.print("storage[4] = "); Serial.println(storage[4]);
Serial.print("storage[5] = "); Serial.println(storage[5]);
Serial.print("storage[6] = "); Serial.println(storage[6]);
Serial.print("storage[7] = "); Serial.println(storage[7]);
SPI.transfer(storage, sizeof storage ); //SPI library allows a user to
transfer a whole array of bytes and you need to include the size of the
array.
digitalWrite(SS, HIGH);
delay(1000);
}
For my Slave code:
#include <SPI.h>
byte storage [8];
volatile byte pos;
volatile boolean process;
float buff[2];
void setup()
{
pinMode(MISO,OUTPUT);
SPCR |= _BV(SPE);
SPCR |= _BV(SPIE);
pos = 0;
process = false;
Serial.begin(9600);
}
ISR(SPI_STC_vect)
{
byte gathered = SPDR;
if( pos < sizeof storage)
{
storage[pos++] = gathered;
}
else
process = true;
}
void loop()
{
if( process )
{
Serial.print("storage[0] = "); Serial.println(storage[0]);
Serial.print("storage[1] = "); Serial.println(storage[1]);
Serial.print("storage[2] = "); Serial.println(storage[2]);
Serial.print("storage[3] = "); Serial.println(storage[3]);
Serial.print("storage[4] = "); Serial.println(storage[4]);
Serial.print("storage[5] = "); Serial.println(storage[5]);
Serial.print("storage[6] = "); Serial.println(storage[6]);
Serial.print("storage[7] = "); Serial.println(storage[7]);
memcpy(buff,&storage,8);
Serial.print("This is buff[0]");Serial.println(buff[0]);
Serial.print("This is buff[1]");Serial.println(buff[1]);
storage[pos] = 0;
pos = 0;
process = false;
}
}
The immediate problem is that Serial.print doesn't know what to do with a byte array. Either declare it as a char array or cast it in the print statement:
char buf[7];
OR
Serial.print((char*) buf);
Either way, though, it's not going to show up as a float like you want.
An easier way to do all this is to use memcpy or a union to go back and forth between float and bytes. On the master end:
uint8_t buf[4];
memcpy(buf, &a, 4);
Then use SPI to send 4 bytes. Reverse it on the peripheral end.
Note that sending '\n' as the termination byte is a bad idea because it can lead to weird behavior, since one of the bytes in the float could easily be 0x0a, the hexadecimal equivalent of '\n'.

Using arduino to control leds as a pattern

I have this code here which sends some keys to windows and also lights up an led attached under each button. I think its right so the led will toggle with the button. What I wanted to achieve ontop of this was so that if no button states hadn't changed for 30 seconds then it goes into a mode where all three leds flash rapidly 3 times then the leds randomly flashes (Like a pinball machine when its not in use). After any input then it goes back to the normal mode
/* Arduino USB Keyboard HID demo
* Cut/Copy/Paste Keys
*/
#define KEY_LEFT_CTRL 0x01
#define KEY_LEFT_SHIFT 0x02
#define KEY_RIGHT_CTRL 0x10
#define KEY_RIGHT_SHIFT 0x20
uint8_t buf[8] = {
0 }; /* Keyboard report buffer */
#define PIN_COPY 5
#define PIN_CUT 6
#define PIN_PASTE 7
#define LED_COPY 8
#define LED_CUT 9
#define LED_PASTE 10
int state = 1;
void setup()
{
Serial.begin(9600);
pinMode(PIN_COPY, INPUT);
pinMode(PIN_CUT, INPUT);
pinMode(PIN_PASTE, INPUT);
// Enable internal pull-ups
digitalWrite(PIN_COPY, 1);
digitalWrite(PIN_CUT, 1);
digitalWrite(PIN_PASTE, 1);
delay(200);
}
void loop()
{
state = digitalRead(PIN_CUT);
if (state != 1) {
buf[0] = KEY_LEFT_CTRL; // Ctrl
buf[2] = 27; // Letter X
// buf[2] = 123; // Cut key: Less portable
Serial.write(buf, 8); // Ssend keypress
digitalWrite(LED_CUT, HIGH);
releaseKey();
}
state = digitalRead(PIN_COPY);
if (state != 1) {
buf[0] = KEY_LEFT_CTRL; // Ctrl
buf[2] = 6; // Letter C
// buf[2] = 124; // Copy key: Less portable
Serial.write(buf, 8); // Send keypress
digitalWrite(LED_COPY, HIGH);
releaseKey();
}
state = digitalRead(PIN_PASTE);
if (state != 1) {
buf[0] = KEY_LEFT_CTRL; // Ctrl
buf[2] = 25; // Letter V
// buf[2] = 125; // Paste key: Less portable
Serial.write(buf, 8); // Send keypress
digitalWrite(LED_PASTE, HIGH);
releaseKey();
}
}
void releaseKey()
{
buf[0] = 0;
buf[2] = 0;
Serial.write(buf, 8); // Release key
delay(500);
digitalWrite(LED_COPY, LOW);
digitalWrite(LED_CUT, LOW);
digitalWrite(LED_PASTE, LOW);
}
For refrence this is the article I was using http://mitchtech.net/arduino-usb-hid-keyboard/
i recomend you to look for Finite State Machine in Google.
Here is one link to implement it in C++
http://www.drdobbs.com/cpp/state-machine-design-in-c/184401236
here is other link to implement it in Arduino
http://playground.arduino.cc/Code/SMlib
it's exactly what you need to do what you want.
i hope this help you!

displaying Hexadecimal value in LCD

I wrote LCD interface program for Atmega328 (Though there are libraries available, I wanted to write from scratch). But have two problems.
1. Sometimes LCD does not display correctly. Only few strips are seen. I end up in resetting once or twice.
2. I am unable to display hexadecimal values usingdisplayOneByteHexValue(). However ASCII coversion was correct and I could see that in Atmel Simulator. Below is the code. I am using Atmel Studio 6.2
/*
* EmbeddedProgram1.c
*
* Created: 16-05-2015 08:19:38
* Author: Mahesha
*/
#ifndef F_CPU
#define F_CPU 8000000UL
#endif
#include <avr/io.h>
#include <util/delay.h>
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Only Change following when changing pin numbers.
// All Data bits have to be assigned sequentially in same the port . RS and EN must be allocated in the same port
#define LCDPORT PORTD
#define LCDDDR DDRD
// used pins on port appropriate ports
#define LCD_DB4 2 // PORTD.2
#define LCD_DB5 3 // PORTD.3
#define LCD_DB6 4 // PORTD.4
#define LCD_DB7 5 // PORTD.5
#define LCD_ENABLE_BIT 6 // PORTD.6 Enable
#define LCD_RS 7 // PORTD.7 Register Select
//#define LCD_RW // R/W is connected to GND permanently
#define LCD_DATA_BITS_MASK 0x3C
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define SET_EIGHT_BIT_MODE() LCDPORT|=((1<<LCD_DB5)|(1<<LCD_DB4)) // Set DB4 and DB5 as 1 for setting eight bit mode.
#define SET_FOUR_BIT_MODE() LCDPORT|=(1<<LCD_DB5)
#define SET_INSTRUCTION_MODE() LCDPORT&=~(1<<LCD_RS) //Function to select command port on LCD RS pin bit 2
#define SET_DATA_MODE() LCDPORT|=(1<<LCD_RS) //Function to select data port on LCD
#define DISABLE_LCD() LCDPORT&=~(1<<LCD_ENABLE_BIT) //Function to disable LCD P0.18
#define ENABLE_LCD() LCDPORT|=(1<<LCD_ENABLE_BIT) //Function to Enable LCD P0.18
//#define EIGHT_BIT_MODE 0x30 // 0 0 1 1 x x x x DB7 to DB0 0f LCD
#define BUSY_FLAG_WAIT_TIME 20
#define FOUR_BIT_5_BY_10_2LINE 0x28
#define LCD_INIT_DELAY 100 // Give a delay of 200 msec after reset. // Datasheet says 10 msec delay.
//Commands Finalized
#define CLEAR_DISPLAY 0x01
#define CURSOR_HOME 0x02 // return home
#define ENTRY_MODE_LEFT_TO_RIGHT 0x06 // Cursor direction from Left to right , Bit 1 of entry mode in LCD
#define DISPLAY_OFF 0x08 // Blink ON, Cursor ON etc are don't care
#define CURSOR_OFF_BLINK_OFF 0x0C
#define CURSOR_OFF_BLINK_ON 0x0D // blink on Even 0x0D also works. So cursor need not be ON
#define CURSOR_ON_BLINK_OFF 0x0E // blink off
#define CURSOR_ON_BLINK_ON 0x0F
#define SHIFT_ENTIRE_LEFT 0x18
#define SHIFT_CURSOR_LEFT 0x10
#define SHIFT_ENTIRE_RIGHT 0x1C
#define SHIFT_CURSOR_RIGHT 0x14
// Function prototypes
void unpackAndSend(char data);
void waitForBusyFlagToClear(void);
void sendLCDPulse(void);
void displayInRow1(char* data);
void displayInRow2(char* data);
void displayInRow1WithPosition(char* data, uint8_t position);
void displayInRow2WithPosition(char* data, uint8_t position);
void sendTextToLCD(char *data);
void displayOneByteHexValue(unsigned char,unsigned char,char);
void initializeLCD(void);
void CL_delayMS(unsigned int delayMS)
{
while(delayMS--)
{
_delay_ms(1);
}
}
void CL_delayuS(unsigned int delayus)
{
while(delayus--)
{
_delay_us(1);
}
}
// writes a char to the LCD
void writeCharToLCD(unsigned char data)
{
SET_DATA_MODE(); // RS bit has to be 1 for data mode
unpackAndSend(data);
}
// sendLCD pulse will just enable and disable the EN bit of LCD display.
void sendLCDPulse(void)
{
DISABLE_LCD();
CL_delayuS(50);
ENABLE_LCD();
CL_delayMS(1);
DISABLE_LCD();
CL_delayMS(1);
}
// writes an instruction to the LCD
void sendLCDCommand(unsigned char inst)
{
SET_INSTRUCTION_MODE();
unpackAndSend(inst);
waitForBusyFlagToClear();
}
// Unpack and send data will separate two nibbles and send twice.
void unpackAndSend(char inst)
{
char temp=inst;
DISABLE_LCD();
// SET_WRITE_MODE(); // If write is permanently disabled, do not use this.
LCDPORT &= (~LCD_DATA_BITS_MASK); // Clear the data bits
//sendLCDPulse();
inst&=0xF0;
inst=inst>>4; // Get the upper nibble
LCDPORT|=inst<<LCD_DB4; //Replace the bits starting from position of bit LCD_DB4 with this new data
sendLCDPulse();
LCDPORT &= (~LCD_DATA_BITS_MASK); // Clear the data bits again
//sendLCDPulse();
temp &=0x0f; //send low nibble
LCDPORT|=temp<<LCD_DB4;
sendLCDPulse();
}
// waitForBusyFlagToClear functio can wait for the busy bit, But since we are permanently connected R/W pin to ground, we cannot read
// the flag from LCD. In case busy bit has to be read, implementation has to be changed.
void waitForBusyFlagToClear(void)
{
CL_delayMS(BUSY_FLAG_WAIT_TIME);
}
// clear display
void clearDisplay(void)
{
sendLCDCommand (CLEAR_DISPLAY);
}
// return home
void returnCursorHome(void)
{
sendLCDCommand (CURSOR_HOME);
}
// LCD off
void displayOFF(void)
{
sendLCDCommand (DISPLAY_OFF);
}
// LCD on
void displayONCursorOFF(void)
{
sendLCDCommand (CURSOR_OFF_BLINK_OFF);
}
// cursor on
void displayONCursorON(void)
{
sendLCDCommand (CURSOR_ON_BLINK_OFF);
}
// blink on
void cursorOffBlinkOn(void)
{
sendLCDCommand (CURSOR_OFF_BLINK_ON);
}
// blink OFF, but display and cursors are ON
void cursorOnBlinkOff(void)
{
sendLCDCommand (CURSOR_ON_BLINK_OFF);
}
// All are ON
void cursorOnBlinkOn(void)
{
sendLCDCommand (CURSOR_ON_BLINK_ON);
}
//go to first line
void LCDline1 (void)
{
sendLCDCommand (0b10000000);
}
//go to second line
void LCDline2 (void)
{
sendLCDCommand (0b11000000);
}
// goto position x,y
// row1 or row2 are the parameters
// So parameters can be 1 or 2
void setRowAndColumnPositionOnDisplay (unsigned char rowNumber, unsigned char position)
{
unsigned char pos;
if (rowNumber == 1)
{
pos = 0x00 + position;
pos|=0x80; // Command to set 1st Row.
}
else //if (rowNumber == 1) // Either row 1 or two. We cannot have else option.
{
pos = 0x40 + position;
pos|=0xC0; // Command to set second row.
}
sendLCDCommand (pos);
}
void displayInRow1(char* data)
{
sendLCDCommand(0x80); // Set DDRAM Address as 0
sendTextToLCD(data);
}
void displayInRow1WithPosition(char* data, unsigned char position)
{
// The position cannot be more than 15. Display is 16 characters (0-15).
if(position>15)
{
position = 15;
}
sendLCDCommand(0x80|position); // Change the DDRAM address to first line by
// keeping D7 high and setting address to 0 onwards
sendTextToLCD(data);
}
////////////////////////////// diaplayInRow2 /////////////////////////////////////////////
void displayInRow2(char* data)
{
sendLCDCommand(0xC0); // Change the DDRAM address to next line 0x40 to 4F
sendTextToLCD(data);
}
////////////////////// diaplayInRow2WithPosition //////////////////////
void displayInRow2WithPosition(char* data, unsigned char position)
{
// The position cannot be more than 15. Display is 16 characters (0-15).
if(position>15)
{
position = 15;
}
sendLCDCommand(0xC0|position); // Change the DDRAM address to second line by
//keeping Bit D7 high and setting address at 0x40 onwards
sendTextToLCD(data);
}
void scrollLcd(char *row1Data,char *row2Data)
{
while(1)
{
sendLCDCommand(SHIFT_CURSOR_LEFT);
sendLCDCommand(SHIFT_ENTIRE_LEFT);
CL_delayMS(200);
}
}
//write text to the LCD
void sendTextToLCD(char *data)
{
while (*data)
{
writeCharToLCD(*data);
data++;
}
}
// Function to convert lower nibble to ASCII Value
//Only lower nibble of the input is considered and higher nibble is lost
char convertLowerNibbleToASCIIValue(char data)
{
data&=0x0F;
if(data<=9)
{
return(data+0x30);
}
else // There is no chance for getting more than 0x0F in the lowerNibble Parameter)
{
return(data+0x37);
}
}
// Function to convert Higher nibble to ASCII Value
//Only higher nibble of the input is considered and lower nibble is lost
char convertHigherNibbleToASCIIValue(char data)
{
data>>=4;
if(data<=9)
{
return(data+0x30);
}
else // There is no chance for getting more than 0x0F in the lowerNibble Parameter)
{
return(data+0x37);
}
}
void displayOneByteHexValue(unsigned char rowNum, unsigned char pos, char data)
{
char temp;
setRowAndColumnPositionOnDisplay(rowNum,pos);
temp = convertHigherNibbleToASCIIValue(data);
sendTextToLCD(&temp);
temp = convertLowerNibbleToASCIIValue(data);
sendTextToLCD(&temp);
}
// init LCD
void initializeLCD(void)
{
// Set the direction of port pins connected to LCD display as output ports.
// We are permanently connecting R/W pin to ground. So there is no read instruction in this case..
LCDDDR |= (1<<LCD_DB4)|(1<<LCD_DB5)|(1<<LCD_DB6)|(1<<LCD_DB7)|(1<<LCD_RS)|(1<<LCD_ENABLE_BIT);
//After reset, data sheet suggests some delay.
CL_delayMS(LCD_INIT_DELAY);
// Note some sites says three times 8 bit mode setting commands need to be sent.
// But it is observed that even without this, LCD works fine. So 1st Command, 2nd Command and 3rd Commands can be deleted below.
//ENABLE_LCD();
// 1st Command
SET_EIGHT_BIT_MODE();
//sendLCDPulse(); // Do not delete this. Need to further analyse. If pulse if sent it is not working
//CL_delayMS(5); // Do not use this delay since delay is not acurate, Either use _delay_ms or use timers
_delay_ms(5);
// Second Command
SET_EIGHT_BIT_MODE();
//sendLCDPulse();
_delay_us(100);
//CL_delayuS(100); // Do not use this delay since delay is not acurate, Either use _delay_ms or use timers
// third Command
SET_EIGHT_BIT_MODE();
//sendLCDPulse();
//CL_delayuS(100); // Do not use this delay since delay is not acurate, Either use _delay_ms or use timers
_delay_us(37);
// Finally Set four bit mode
SET_FOUR_BIT_MODE();
//sendLCDPulse();
CL_delayuS(100);
// First time when 4 bit mode command is sent, only one higher nibble was sent since
// only 4 bits are connected from MPU to LCD. Since D0 to D3 of LCD are not connected,
// their values depend on how the pins are connected in LCD module (May be grounded, may kept open etc)
//So again send function set command to set 2 line display mode mode and 5x7 character mode. But now two write operations to LCD is made
// inside the function sendLCDCommand.
sendLCDCommand (FOUR_BIT_5_BY_10_2LINE);
//turn on display and cursor OFF, Blink OFF (sent two times using below command)
sendLCDCommand (CURSOR_OFF_BLINK_OFF);
//clr display
sendLCDCommand (CLEAR_DISPLAY);
// Set Entry mode left to right
sendLCDCommand (ENTRY_MODE_LEFT_TO_RIGHT);
}
void LCDProgramCallFromMain(char *row1Data, char *row2Data)
{
initializeLCD();
setRowAndColumnPositionOnDisplay (1,0);
sendTextToLCD (row1Data);
displayOneByteHexValue(2,0,0xF4);
//setRowAndColumnPositionOnDisplay (2,5);
//displayInRow1(row1Data);
}
int main(void)
{
LCDProgramCallFromMain("Hello", "Welcome to cloude");
while(1)
{
}
}
Below is the image of the display I am getting. Not able to makeout where the problem is.
Yes, it worked after I changed the function as below. Thanks for the help.
void displayOneByteHexValue(unsigned char rowNum, unsigned char pos, char data)
{
char temp;
setRowAndColumnPositionOnDisplay(rowNum,pos);
temp = convertHigherNibbleToASCIIValue(data);
writeCharToLCD(temp);
temp = convertLowerNibbleToASCIIValue(data);
writeCharToLCD(temp);
}
I further optimized the code and below is the complete working code with small demo function called from main.
#ifndef F_CPU
#define F_CPU 16000000UL
#endif
#include <avr/io.h>
#include <util/delay.h>
#include <stdlib.h>
#include <avr/sfr_defs.h>
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//Only Change following when changing pin numbers.
// All Data bits have to be assigned sequentially in same the port . RS and EN must be allocated in the same port
#define LCDPORT PORTD
#define LCDDDR DDRD
// used pins on port appropriate ports
#define LCD_DB4 2 // PORTD.2
#define LCD_DB5 3 // PORTD.3
#define LCD_DB6 4 // PORTD.4
#define LCD_DB7 5 // PORTD.5
#define LCD_ENABLE_BIT 6 // PORTD.6 Enable
#define LCD_RS 7 // PORTD.7 Register Select
//#define LCD_RW // R/W is connected to GND permanently
#define LCD_DATA_BITS_MASK ((1<<LCD_DB4)|(1<<LCD_DB5)|(1<<LCD_DB6)|(1<<LCD_DB7))
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
#define SET_EIGHT_BIT_MODE() LCDPORT|=((1<<LCD_DB5)|(1<<LCD_DB4)) // Set DB4 and DB5 as 1 for setting eight bit mode.
#define SET_FOUR_BIT_MODE() LCDPORT|=(1<<LCD_DB5)
*/
#define SET_INSTRUCTION_MODE() LCDPORT&=~(1<<LCD_RS) //Function to select command port on LCD RS pin bit 2
#define SET_DATA_MODE() LCDPORT|=(1<<LCD_RS) //Function to select data port on LCD
#define DISABLE_LCD() LCDPORT&=~(1<<LCD_ENABLE_BIT) //Function to disable LCD P0.18
#define ENABLE_LCD() LCDPORT|=(1<<LCD_ENABLE_BIT) //Function to Enable LCD P0.18
#define BUSY_FLAG_WAIT_TIME 20
#define FOUR_BIT_5_BY_10_2LINE 0x28
#define LCD_INIT_DELAY 100 // Give a delay of 100 msec after reset. // Datasheet says 10 msec delay.
// Function prototypes for external use (to be kept in Header file)
void sendLCDCommand(unsigned char cmd);
void displayInRow1(unsigned char* data);
void displayInRow2(unsigned char* data);
void displayInRow1WithPosition(unsigned char* data, uint8_t position);
void displayInRow2WithPosition(unsigned char* data, uint8_t position);
void sendTextToLCD(unsigned char *data);
void initializeLCD(void);
// Function prototypes for file use (only internal)
void unpackAndSend(char data);
void waitForBusyFlagToClear(void);
void sendLCDPulse(void);
void displayOneByteHexValue(unsigned char,unsigned char,char); // This function is no longer needed. Only for demo purpose.
// #define commands are replaced by enums to demonstrate the feature of enumerated data types
enum DISPLAY_PROPERTY
{
CLEAR_DISPLAY=0x01,
CURSOR_HOME = 0x02, // return home
ENTRY_MODE_LEFT_TO_RIGHT= 0x06, // Cursor direction from Left to right , Bit 1 of entry mode in LCD
DISPLAY_OFF= 0x08, // Blink ON, Cursor ON etc are don't care
CURSOR_OFF_BLINK_OFF= 0x0C,
CURSOR_OFF_BLINK_ON= 0x0D, // blink on Even 0x0D also works. So cursor need not be ON
CURSOR_ON_BLINK_OFF=0x0E, // blink off
CURSOR_ON_BLINK_ON=0x0F,
SHIFT_ENTIRE_LEFT= 0x18,
SHIFT_CURSOR_LEFT= 0x10,
SHIFT_ENTIRE_RIGHT= 0x1C,
SHIFT_CURSOR_RIGHT= 0x14,
};
void SET_EIGHT_BIT_MODE(void)
{
LCDPORT=(LCDPORT&(~LCD_DATA_BITS_MASK))|((0<<LCD_DB7)|(0<<LCD_DB6)|(1<<LCD_DB5)|(1<<LCD_DB4)); // Set DB4 and DB5 as 1 for setting eight bit mode.
}
void SET_FOUR_BIT_MODE(void)
{
LCDPORT= (LCDPORT&(~LCD_DATA_BITS_MASK)) | ((0<<LCD_DB7)|(0<<LCD_DB6)|(1<<LCD_DB5)|(0<<LCD_DB4));
}
void CL_delayMS(unsigned int delayMS)
{
while(delayMS--)
{
_delay_ms(1);
}
}
void CL_delayuS(unsigned int delayus)
{
while(delayus--)
{
_delay_us(1);
}
}
// writes a char to the LCD
void writeCharToLCD(unsigned char data)
{
SET_DATA_MODE(); // RS bit has to be 1 for data mode
unpackAndSend(data);
}
// sendLCD pulse will just enable and disable the EN bit of LCD display.
void sendLCDPulse(void)
{
//DISABLE_LCD();
//CL_delayuS(50);
ENABLE_LCD();
CL_delayMS(1);
DISABLE_LCD();
CL_delayMS(1);
}
// writes an instruction to the LCD
void sendLCDCommand(unsigned char cmd)
{
SET_INSTRUCTION_MODE();
unpackAndSend(cmd);
waitForBusyFlagToClear();
}
// Unpack and send data will separate two nibbles and send twice.
void unpackAndSend(char inst)
{
char temp=inst;
DISABLE_LCD();
// SET_WRITE_MODE(); // If write is permanently disabled, do not use this.
LCDPORT &= (~LCD_DATA_BITS_MASK); // Clear the data bits
//sendLCDPulse();
inst&=0xF0;
inst=inst>>4; // Get the upper nibble
LCDPORT|=inst<<LCD_DB4; //Replace the bits starting from position of bit LCD_DB4 with this new data
sendLCDPulse();
LCDPORT &= (~LCD_DATA_BITS_MASK); // Clear the data bits again
//sendLCDPulse();
temp &=0x0f; //send low nibble
LCDPORT|=temp<<LCD_DB4;
sendLCDPulse();
}
// waitForBusyFlagToClear function can wait for the busy bit, But since we are permanently connected R/W pin to ground, we cannot read
// the flag from LCD. In case busy bit has to be read, implementation has to be changed.
void waitForBusyFlagToClear(void)
{
CL_delayMS(BUSY_FLAG_WAIT_TIME);
}
// goto position x,y
// row1 or row2 are the parameters
// So parameters can be 0 or 1
void setRowAndColumnPositionOnDisplay (unsigned char rowNumber, unsigned char position)
{
unsigned char pos;
if (rowNumber==0) // If Row is 0, display in 1st row
{
pos = 0x00 + position;
pos|=0x80; // Command to set 1st Row.
}
else //if (rowNumber == 1) // Either row 1 or two etc
{
pos = 0x40 + position;
pos|=0xC0; // Command to set second row.
}
sendLCDCommand (pos);
}
void displayInRow1(unsigned char* data)
{
sendLCDCommand(0x80); // Set DDRAM Address as 0
sendTextToLCD(data);
}
void displayInRow1WithPosition(unsigned char* data, unsigned char position)
{
sendLCDCommand(0x80|position); // Change the DDRAM address to first line by
// keeping D7 high and setting address to 0 onwards
sendTextToLCD(data);
}
////////////////////////////// diaplayInRow2 /////////////////////////////////////////////
void displayInRow2(unsigned char* data)
{
sendLCDCommand(0xC0); // Change the DDRAM address to next line 0x40 to 4F
sendTextToLCD(data);
}
////////////////////// diaplayInRow2WithPosition //////////////////////
void displayInRow2WithPosition(unsigned char* data, unsigned char position)
{
sendLCDCommand(0xC0|position); // Change the DDRAM address to second line by
//keeping Bit D7 high and setting address at 0x40 onwards
sendTextToLCD(data);
}
//write text to the LCD
void sendTextToLCD(unsigned char *data)
{
while (*data)
{
writeCharToLCD(*data);
data++;
}
}
// initialize LCD
void initializeLCD(void)
{
// Set the direction of port pins connected to LCD display as output ports
// We are permanently connecting R/W pin to ground. So there is no read instruction in this case
LCDDDR |= (1<<LCD_DB4)|(1<<LCD_DB5)|(1<<LCD_DB6)|(1<<LCD_DB7)|(1<<LCD_RS)|(1<<LCD_ENABLE_BIT);
//After reset, data sheet suggests some delay.
CL_delayMS(LCD_INIT_DELAY);
// Note some sites says three times 8 bit mode setting commands need to be sent.
// But it is observed that even without this, LCD works fine. So 1st Command, 2nd Command and 3rd Commands can be deleted below.
//ENABLE_LCD();
// 1st Command
SET_EIGHT_BIT_MODE();
sendLCDPulse(); // Do not delete this. Need to further analyze. If pulse if sent it is not working
//CL_delayMS(5); // Do not use this delay since delay is not acurate, Either use _delay_ms or use timers
_delay_ms(5);
// Second Command
SET_EIGHT_BIT_MODE();
sendLCDPulse();
_delay_us(100);
//CL_delayuS(100); // Do not use this delay since delay is not acurate, Either use _delay_ms or use timers
// third Command
SET_EIGHT_BIT_MODE();
sendLCDPulse();
//CL_delayuS(100); // Do not use this delay since delay is not acurate, Either use _delay_ms or use timers
_delay_us(37);
// Finally Set four bit mode
SET_FOUR_BIT_MODE();
sendLCDPulse();
CL_delayuS(100);
// First time when 4 bit mode command is sent, only one higher nibble was sent since
// only 4 bits are connected from MPU to LCD. Since D0 to D3 of LCD are not connected,
// their values depend on how the pins are connected in LCD module (May be grounded, may kept open etc)
//So again send function set command to set 2 line display mode mode and 5x7 character mode. But now two write operations to LCD is made
// inside the function sendLCDCommand.
sendLCDCommand (FOUR_BIT_5_BY_10_2LINE);
//turn on display and cursor OFF, Blink OFF (sent two times using below command)
sendLCDCommand (CURSOR_OFF_BLINK_OFF);
//clr display
sendLCDCommand (CLEAR_DISPLAY);
// Set Entry mode left to right
sendLCDCommand (ENTRY_MODE_LEFT_TO_RIGHT);
}
void displayNumberTest(int range)
{
float fNum = -0.332;
unsigned char lcdBuffer[50];
sendLCDCommand(CLEAR_DISPLAY);
//setRowAndColumnPositionOnDisplay (0,0);
//sendTextToLCD("Decimal = ");
for (int count=0;count<range;count++)
{
itoa(count,lcdBuffer,2);
setRowAndColumnPositionOnDisplay (1,0);
sendTextToLCD(lcdBuffer);
itoa(count,lcdBuffer,16);
setRowAndColumnPositionOnDisplay (1,13);
sendTextToLCD(lcdBuffer);
itoa(count,lcdBuffer,10);
setRowAndColumnPositionOnDisplay (0,0); // 1st Row, 0th position
sendTextToLCD(lcdBuffer);
// Now display some floating value number.
// use the function dtostrf(). It take 4 parameters
// Parameter1: The value to be converted
// 2: Width
//3. Precision
// 4. String to hold ASCII
// It is also possible to use sprintf function. But it takes more memory.
fNum=(fNum*(-1)*(count+1)/101.12)+0.012;
dtostrf(fNum,5,3,lcdBuffer);
setRowAndColumnPositionOnDisplay (0,7);
sendTextToLCD(lcdBuffer);
CL_delayMS(300);
}
}
// Scroll the display
// This program need to be modified further to display only whatever characters are there in DDRAM.
// Right now 4 columns will be shifted just to demonstrate.
void scrollingDisplayTest(unsigned char *row1Text, unsigned char* row2Text, unsigned int scrollDelay)
{
unsigned char count=40;
sendLCDCommand(CLEAR_DISPLAY);
displayInRow1(row1Text);
displayInRow2(row2Text);
CL_delayMS(2000);
while(count--)
{
sendLCDCommand(SHIFT_ENTIRE_LEFT);
CL_delayMS(scrollDelay);
}
}
void LCDDemoProgram(unsigned char *row1Data, unsigned char *row2Data)
{
initializeLCD();
sendTextToLCD (row1Data);
setRowAndColumnPositionOnDisplay (1,0);
sendTextToLCD (row2Data);
CL_delayMS(3000);
sendLCDCommand(CLEAR_DISPLAY);
// Just to demostrate how Hex values can be displayed without using itoa library function
displayOneByteHexValue(0,0,CLKPR);
setRowAndColumnPositionOnDisplay (1,0);
displayOneByteHexValue(1,12,0xE2);
CL_delayMS(3000);
sendLCDCommand(CLEAR_DISPLAY);
//Wait for 3 seconds
scrollingDisplayTest("Display will be cleared for 3 sec after displaying DONE ", "Please Wait.... Please Wait.....",1000);
CL_delayMS(2000);
sendLCDCommand(CLEAR_DISPLAY);
displayInRow1("DONE");
CL_delayMS(3000);
displayInRow1("Cur OFF Bl OFF");
sendLCDCommand(CURSOR_OFF_BLINK_OFF);
CL_delayMS(3000);
sendLCDCommand(CLEAR_DISPLAY);
displayInRow1("Cur OFF Bl ON");
sendLCDCommand(CURSOR_OFF_BLINK_ON);
CL_delayMS(3000);
sendLCDCommand(CLEAR_DISPLAY);
displayInRow1("Cur ON Bl OFF");
sendLCDCommand(CURSOR_ON_BLINK_OFF);
CL_delayMS(3000);
sendLCDCommand(CLEAR_DISPLAY);
displayInRow1("Cur ON Bl ON");
sendLCDCommand(CURSOR_ON_BLINK_ON);
CL_delayMS(3000);
displayNumberTest(100);
}
//////////////////////////////////////////////// Do No Use Below code. This is Only for demo purpose//////////////////////////////////////////
// Function to convert lower nibble to ASCII Value
//Only lower nibble of the input is considered and higher nibble is lost
// This function is written for understanding purpose only.
// itoa () library function can be used instead of this with base of 16.
unsigned char convertLowerNibbleToASCIIValue(unsigned char data)
{
data&=0x0F;
if(data<=9)
{
return(data+0x30);
}
else // There is no chance for getting more than 0x0F in the lowerNibble Parameter)
{
return(data+0x37);
}
}
// Function to convert Higher nibble to ASCII Value
//Only higher nibble of the input is considered and lower nibble is lost
unsigned char convertHigherNibbleToASCIIValue(unsigned char data)
{
data>>=4;
if(data<=9)
{
return(data+0x30);
}
else // There is no chance for getting more than 0x0F in the lowerNibble Parameter)
{
return(data+0x37);
}
}
void displayOneByteHexValue(unsigned char rowNum, unsigned char pos, char data)
{
char temp;
setRowAndColumnPositionOnDisplay(rowNum,pos);
temp = convertHigherNibbleToASCIIValue(data);
writeCharToLCD(temp);
temp = convertLowerNibbleToASCIIValue(data);
writeCharToLCD(temp);
}
// Displays 8 bit Register values in LCD display unit.
// name is the name of the register to be displayed. You can give any name.
// port is the register name.
void displayRegisterValues(unsigned char *displayName, volatile uint8_t *registerName)
{
char temp2;
displayInRow1(displayName);
//temp2=registerName;
temp2 = (convertLowerNibbleToASCIIValue(*registerName));
displayInRow1(&temp2);
// Get the higher Nibble
temp2 = (convertLowerNibbleToASCIIValue((*registerName)>>4));
displayInRow2(&temp2);
}
int main(void)
{
LCDDemoProgram("Hello", "World is Great");
while(1)
{
//TODO:: Please write your application code
}
return(0);
}

Resources