Arduino - managing two SPI devices at once - arduino

I'm trying to control two SPI devices at once using identical MISO, MOSI, and CLOCK pins but different SS pins.
One is the Wifly shield from SparkFun that uses a SPI-to-UART chip, the other is a MAX31855.
They work independently, but not together..
The SPI-to-UART code that I'm using is listed below. The only changes I've made is in the header file; I set select() and deselect() to public.
#include "SpiUart.h"
// See section 8.10 of the datasheet for definitions
// of bits in the Enhanced Features Register (EFR)
#define EFR_ENABLE_CTS 1 << 7
#define EFR_ENABLE_RTS 1 << 6
#define EFR_ENABLE_ENHANCED_FUNCTIONS 1 << 4
// See section 8.4 of the datasheet for definitions
// of bits in the Line Control Register (LCR)
#define LCR_ENABLE_DIVISOR_LATCH 1 << 7
// The original crystal frequency used on the board (~12 MHz) didn't
// give a good range of baud rates so around July 2010 the crystal
// was replaced with a better frequency (~14 MHz).
#ifndef USE_14_MHZ_CRYSTAL
#define USE_14_MHZ_CRYSTAL true // true (14 MHz) , false (12 MHz)
#endif
#if USE_14_MHZ_CRYSTAL
#define XTAL_FREQUENCY 14745600UL // On-board crystal (New mid-2010 Version)
#else
#define XTAL_FREQUENCY 12288000UL // On-board crystal (Original Version)
#endif
// See datasheet section 7.8 for configuring the
// "Programmable baud rate generator"
#define PRESCALER 1 // Default prescaler after reset
#define BAUD_RATE_DIVISOR(baud) ((XTAL_FREQUENCY/PRESCALER)/(baud*16UL))
// TODO: Handle configuration better
// SC16IS750 register values
struct SPI_UART_cfg {
char DataFormat;
char Flow;
};
struct SPI_UART_cfg SPI_Uart_config = {
0x03,
// We need to enable flow control or we overflow buffers and
// lose data when used with the WiFly. Note that flow control
// needs to be enabled on the WiFly for this to work but it's
// possible to do that with flow control enabled here but not there.
// TODO: Make this able to be configured externally?
EFR_ENABLE_CTS | EFR_ENABLE_RTS | EFR_ENABLE_ENHANCED_FUNCTIONS
};
void SpiUartDevice::begin(unsigned long baudrate) {
/*
* Initialize SPI and UART communications
*
* Uses BAUD_RATE_DEFAULT as baudrate if none is given
*/
SPI.begin();
initUart(baudrate);
}
void SpiUartDevice::deselect() {
/*
* Deslects the SPI device
*/
digitalWrite(SS, HIGH);
}
void SpiUartDevice::select() {
/*
* Selects the SPI device
*/
digitalWrite(SS, LOW);
}
void SpiUartDevice::initUart(unsigned long baudrate) {
/*
* Initialise the UART.
*
* If initialisation fails this method does not return.
*/
// Initialise and test SC16IS750
configureUart(baudrate);
if(!uartConnected()){
while(1) {
// Lock up if we fail to initialise SPI UART bridge.
};
}
// The SPI UART bridge is now successfully initialised.
}
void SpiUartDevice::setBaudRate(unsigned long baudrate) {
unsigned long divisor = BAUD_RATE_DIVISOR(baudrate);
writeRegister(LCR, LCR_ENABLE_DIVISOR_LATCH); // "Program baudrate"
writeRegister(DLL, lowByte(divisor));
writeRegister(DLM, highByte(divisor));
}
void SpiUartDevice::configureUart(unsigned long baudrate) {
/*
* Configure the settings of the UART.
*/
// TODO: Improve with use of constants and calculations.
setBaudRate(baudrate);
writeRegister(LCR, 0xBF); // Access EFR register
writeRegister(EFR, SPI_Uart_config.Flow); // Enable enhanced registers
writeRegister(LCR, SPI_Uart_config.DataFormat); // 8 data bit, 1 stop bit, no parity
writeRegister(FCR, 0x06); // Reset TXFIFO, reset RXFIFO, non FIFO mode
writeRegister(FCR, 0x01); // Enable FIFO mode
}
boolean SpiUartDevice::uartConnected() {
/*
* Check that UART is connected and operational.
*/
// Perform read/write test to check if the UART is working
const char TEST_CHARACTER = 'H';
writeRegister(SPR, TEST_CHARACTER);
return (readRegister(SPR) == TEST_CHARACTER);
}
void SpiUartDevice::writeRegister(byte registerAddress, byte data) {
/*
* Write <data> byte to the SC16IS750 register <registerAddress>
*/
select();
SPI.transfer(registerAddress);
SPI.transfer(data);
deselect();
}
byte SpiUartDevice::readRegister(byte registerAddress) {
/*
* Read byte from SC16IS750 register at <registerAddress>.
*/
// Used in SPI read operations to flush slave's shift register
const byte SPI_DUMMY_BYTE = 0xFF;
char result;
select();
SPI.transfer(SPI_READ_MODE_FLAG | registerAddress);
result = SPI.transfer(SPI_DUMMY_BYTE);
deselect();
return result;
}
int SpiUartDevice::available() {
/*
* Get the number of bytes (characters) available for reading.
*
* This is data that's already arrived and stored in the receive
* buffer (which holds 64 bytes).
*/
// This alternative just checks if there's data but doesn't
// return how many characters are in the buffer:
// readRegister(LSR) & 0x01
return readRegister(RXLVL);
}
int SpiUartDevice::read() {
/*
* Read byte from UART.
*
* Returns byte read or or -1 if no data available.
*
* Acts in the same manner as 'Serial.read()'.
*/
if (!available()) {
return -1;
}
return readRegister(RHR);
}
size_t SpiUartDevice::write(byte value) {
/*
* Write byte to UART.
*/
while (readRegister(TXLVL) == 0) {
// Wait for space in TX buffer
};
writeRegister(THR, value);
}
size_t SpiUartDevice::write(const char *str, size_t size) {
/*
* Write string to UART.
*/
while (size--)
write(*str++);
while (readRegister(TXLVL) < 64) {
// Wait for empty TX buffer (slow).
// (But apparently still not slow enough to ensure delivery.)
};
}
void SpiUartDevice::flush() {
/*
* Flush characters from SC16IS750 receive buffer.
*/
// Note: This may not be the most appropriate flush approach.
// It might be better to just flush the UART's buffer
// rather than the buffer of the connected device
// which is essentially what this does.
while(available() > 0) {
read();
}
}
void SpiUartDevice::ioSetDirection(unsigned char bits) {
writeRegister(IODIR, bits);
}
void SpiUartDevice::ioSetState(unsigned char bits) {
writeRegister(IOSTATE, bits);
}
I attempted to use it like this:
SpiSerial.deselect(); //Deselect Wi-Fi
delay(100); //Wait, just for the heck of it.
currentTemp = thermocouple.readFarenheit(); //Read from max31855... readFarenheit selects and unselects its own SS pin.
SpiSerial.select(); //Reselect Wi-Fi
But it's still unable to function. What further things should I attempt in order to get this to function?

TWO issues:
You don't mention anything about the MAX31855 library or how it is used. It looks like you are using Adafruit_MAX31855. Did you set the second argument of the constructors instance to "SS" or the value of the pin for chip select tied to the MAX31855? Their respective chip selects can not share the same pin.
The Adafruit_MAX31855 bit bangs out an emulated SPI over GPIO. This is not the same as the Wi-Fi's true hard SPI. Once spi.begin(); from SpiUartDevice::begin() then the GPIO pins shared with the hard SPI are no longer available as GPIO. As a result, the Adafruit_MAX31855 bit bangs do nothing. There are several options for the latter problem.
Use a different MAX31855 library that does not bit bang it. But I don't quickly see one.
Fix the Adafruit_MAX31855 to hard SPI.
Use different pins for the MAX31855 other than the Hard SPI pins.
Use the SPI.end() function to shut down the SPI and return GPIO back to normal IO use, and then restart SPI.begin() after bit banging out the Adafruit_MAX31855 function calls.
Such as the following:
SpiSerial.deselect(); //Deselect Wi-Fi
SPI.end(); //Restore GPIO mode of pins
delay(100); //Wait, just for the heck of it
currentTemp = thermocouple.readFarenheit();
SPI.begin(); //Restore SPI mode of pins
SpiSerial.select(); //Reselect Wi-Fi

Related

ESP8266 random Soft WDT resets

I use WeMos D1 mini Pro with Bosche BME680 sensor shield. I get the data from sensor and put them frequently into Firebase database. Everything works, except that my device has random crashes.
The sensor library works in a way that for around 5 first minutes it is not showing IAQ data (it's returning iaq = 25 and accuracy = 0). ESP8266 crashes around 5th minute of working program - when IAQ data is available and some of correct reading had been made.
I think problem might be caused by bsec_iot_loop() which is working too long. I was trying to use yield() in random places of bsec_iot_loop(), but it didn't work. Program works perfectly, when I comment out Firebase set methods.
Huge part of my code is based on official Bosch documentation. Frankly speaking, it's extended copy-paste. Here's the docs: https://www.bosch-sensortec.com/bst/products/all_products/bsec
Here's the code:
/**********************************************************************************************************************/
/* header files */
/**********************************************************************************************************************/
#include "bsec_integration.h"
#include <Wire.h>
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <FirebaseArduino.h>
#include <time.h>
#define DEVICE_NAME "device1"
#define SSID "ssid"
#define PWD "pass"
#define FIREBASE_HOST "host"
#define FIREBASE_AUTH "auth"
#define UPDATE_INTERVAL 20
int startupTime;
/**********************************************************************************************************************/
/* functions */
/**********************************************************************************************************************/
/*!
* #brief Write operation in either Wire or SPI
*
* param[in] dev_addr Wire or SPI device address
* param[in] reg_addr register address
* param[in] reg_data_ptr pointer to the data to be written
* param[in] data_len number of bytes to be written
*
* #return result of the bus communication function
*/
int8_t bus_write(uint8_t dev_addr, uint8_t reg_addr, uint8_t *reg_data_ptr, uint16_t data_len)
{
Wire.beginTransmission((uint8_t) 0x77);
Wire.write(reg_addr); /* Set register address to start writing to */
/* Write the data */
for (int index = 0; index < data_len; index++) {
Wire.write(reg_data_ptr[index]);
}
return (int8_t)Wire.endTransmission();
}
/*!
* #brief Read operation in either Wire or SPI
*
* param[in] dev_addr Wire or SPI device address
* param[in] reg_addr register address
* param[out] reg_data_ptr pointer to the memory to be used to store the read data
* param[in] data_len number of bytes to be read
*
* #return result of the bus communication function
*/
int8_t bus_read(uint8_t dev_addr, uint8_t reg_addr, uint8_t *reg_data_ptr, uint16_t data_len)
{
int8_t comResult = 0;
Wire.beginTransmission((uint8_t) 0x77);
Wire.write(reg_addr); /* Set register address to start reading from */
comResult = Wire.endTransmission();
delayMicroseconds(150); /* Precautionary response delay */
Wire.requestFrom((uint8_t) 0x77, (uint8_t)data_len); /* Request data */
int index = 0;
while (Wire.available()) /* The slave device may send less than requested (burst read) */
{
reg_data_ptr[index] = Wire.read();
index++;
}
return comResult;
}
/*!
* #brief System specific implementation of sleep function
*
* #param[in] t_ms time in milliseconds
*
* #return none
*/
void sleep(uint32_t t_ms)
{
delay(t_ms);
}
/*!
* #brief Capture the system time in microseconds
*
* #return system_current_time current system timestamp in microseconds
*/
int64_t get_timestamp_us()
{
return (int64_t) millis() * 1000;
}
/*!
* #brief Load previous library state from non-volatile memory
*
* #param[in,out] state_buffer buffer to hold the loaded state string
* #param[in] n_buffer size of the allocated state buffer
*
* #return number of bytes copied to state_buffer
*/
uint32_t state_load(uint8_t *state_buffer, uint32_t n_buffer)
{
// ...
// Load a previous library state from non-volatile memory, if available.
//
// Return zero if loading was unsuccessful or no state was available,
// otherwise return length of loaded state string.
// ...
return 0;
}
/*!
* #brief Save library state to non-volatile memory
*
* #param[in] state_buffer buffer holding the state to be stored
* #param[in] length length of the state string to be stored
*
* #return none
*/
void state_save(const uint8_t *state_buffer, uint32_t length)
{
// ...
// Save the string some form of non-volatile memory, if possible.
// ...
}
/*!
* #brief Load library config from non-volatile memory
*
* #param[in,out] config_buffer buffer to hold the loaded state string
* #param[in] n_buffer size of the allocated state buffer
*
* #return number of bytes copied to config_buffer
*/
uint32_t config_load(uint8_t *config_buffer, uint32_t n_buffer)
{
// ...
// Load a library config from non-volatile memory, if available.
//
// Return zero if loading was unsuccessful or no config was available,
// otherwise return length of loaded config string.
// ...
return 0;
}
void connectToWiFi() {
Serial.print("Connecting to ");
Serial.println(SSID);
WiFi.begin(SSID, PWD);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
void configureFirebase() {
Serial.print("Connecting to ");
Serial.println(FIREBASE_HOST);
Serial.println("");
Firebase.begin(FIREBASE_HOST, FIREBASE_AUTH);
delay(500);
}
void configureTime() {
configTime(0, 0, "pool.ntp.org", "time.nist.gov");
Serial.println("\nWaiting for time");
while (!time(nullptr)) {
Serial.print(".");
delay(1000);
}
Serial.println("");
}
void configureSensor() {
return_values_init ret;
/* Init I2C and serial communication */
Wire.begin();
/* Call to the function which initializes the BSEC library
* Switch on low-power mode and provide no temperature offset */
ret = bsec_iot_init(BSEC_SAMPLE_RATE_LP, 5.0f, bus_write, bus_read, sleep, state_load, config_load);
if (ret.bme680_status)
{
/* Could not intialize BME680 */
Serial.println("Error while initializing BME680");
return;
}
else if (ret.bsec_status)
{
/* Could not intialize BSEC library */
Serial.println("Error while initializing BSEC library");
return;
}
Serial.println("Sensor success");
}
/*!
* #brief Handling of the ready outputs
*
* #param[in] timestamp time in nanoseconds
* #param[in] iaq IAQ signal
* #param[in] iaq_accuracy accuracy of IAQ signal
* #param[in] temperature temperature signal
* #param[in] humidity humidity signal
* #param[in] pressure pressure signal
* #param[in] raw_temperature raw temperature signal
* #param[in] raw_humidity raw humidity signal
* #param[in] gas raw gas sensor signal
* #param[in] bsec_status value returned by the bsec_do_steps() call
*
* #return none
*/
void output_ready(int64_t timestamp, float iaq, uint8_t iaq_accuracy, float temperature, float humidity,
float pressure, float raw_temperature, float raw_humidity, float gas, bsec_library_return_t bsec_status)
{
yield();
char startupTimeStr[32];
itoa(startupTime, startupTimeStr, 10);
//Get current time
time_t now = time(nullptr);
//Get last update time
int lastUpdate = Firebase.getInt("device1/lastUpdate");
if (Firebase.failed()) {
Serial.print("getting device1/lastUpdate failed:");
Serial.println(Firebase.error());
return;
}
if (lastUpdate + UPDATE_INTERVAL <= (int) now) {
//Set last update
Firebase.setInt("device1/lastUpdate", (int) now);
//Set the reading
char nowStr[32];
itoa(now, nowStr, 10);
String path = "device1/readings/" + String(nowStr);
// Firebase.setInt(path + "/iaq", iaq);
// Firebase.setFloat(path + "/temp", temperature);
// Firebase.setFloat(path + "/humid", humidity);
// Firebase.setFloat(path + "/press", pressure);
//Set uptime
int uptime = (int) now - startupTime;
//Firebase.setInt("device1/uptimes/" + String(startupTimeStr), uptime);
//Verbose data
Serial.print("Updated: ");
Serial.print((int) now);
Serial.print(" | Uptime: ");
Serial.print(uptime);
Serial.print(" | IAQ: ");
Serial.print(iaq);
Serial.print(" | Acc: ");
Serial.println(iaq_accuracy);
}
}
void setup()
{
Serial.begin(9600);
while (!Serial);
connectToWiFi();
configureFirebase();
configureTime();
configureSensor();
startupTime = (int) time(nullptr);
Serial.print("Startup time:");
Serial.println(startupTime);
/* Call to endless loop function which reads and processes data based on sensor settings */
/* State is saved every 10.000 samples, which means every 10.000 * 3 secs = 500 minutes */
bsec_iot_loop(sleep, get_timestamp_us, output_ready, state_save, 10000);
}
void loop()
{
}
And here's beginning of typical Serial monitor dump:
Soft WDT reset
ctx: cont
sp: 3fff0df0 end: 40101b51 offset: 01b0
>>>stack>>>
3fff0fa0: 3fff31f4 3fff70ec 3fff662c 3fff372c
3fff0fb0: 0002d5a7 3fff70ec 3fff662c 40208866
3fff0fc0: 3fff662c 00000000 3fff703c 40201952
3fff0fd0: 3fff703c 00001388 3fff3b04 3fff0680
3fff0fe0: 000001bb 3fff662c 3fff31f4 3fff0680
3fff0ff0: 000001bb 3fff662c 3fff31f4 402089fd
3fff1000: 3ffe9770 5561c923 3ffe9770 5561c923
3fff1010: 3fff367c 00000000 3fff3684 4020717c
3fff1020: 00000000 00000206 00000206 4020526c
3fff1030: fffffff4 00000000 3fff3684 40207980
3fff1040: 3ffe9584 00000046 3ffe96a9 40201ff3
3fff1050: 3fff36fc 3fff10c0 3fff367c 4020204c
3fff1060: 3fff3708 00000000 00000000 3ffe96a6
3fff1070: 3fff367c 3fff10a0 3ffe96a6 3ffe96a6
3fff1080: 3fff367c 3fff0680 3fff1188 402030fa
3fff1090: 3fff0660 3fff0680 3fff1188 40203b71
3fff10a0: 3fff3708 3e9b1316 3e9b1316 3d003b33
3fff10b0: 41ad99fb bf64685f 00000000 40212b8c
3fff10c0: 3fff39d0 af3cd700 0000d700 00000012
3fff10d0: 00000000 3fff11ac 3fff1198 3fff065c
3fff10e0: 3fff1128 3fff1128 402030e4 40202032
3fff10f0: 3fff112c 40590000 3ed1ca3e 3fff0660
3fff1100: 3fff11ac 3fff1120 3fff1188 40203e0a
3fff1110: 3fff1120 40fb6e3e 3fff2180 40219384
3fff1120: 3fff367c 3fff3944 3fff0680 41ad99fb
I am almost sure that this is happening because your code never reaches the loop() function, on ESP8266 Arduino library it resets the watchdog timer each interaction of loop function.
I think you can solve your problem by two ways, one is opening the function bsec_iot_loop() and put the calls inside the while(1) to loop() function, the other option is to call ESP.wdtFeed() inside the while(1) to reset watchdog Timer.
The link bellow has a good explanation about watchdog timer on ESP Arduino library.
https://techtutorialsx.com/2017/01/21/esp8266-watchdog-functions/
Okay, so I solved the problem. As I suspected there was a problem with watchdog timer. Specifically, the code never reach loop() function, because while(1) inside bsec_iot_loop(). Solution is simple. I put code inside while(1) into loop(). The rest of the code bsec_iot_loop() was declaration of variables and I made them global.
Not releasing system resources is a common problem in the arduino library. Especially on systems with a single core CPU like esp8266 where no native threading is available to the system. The underlying RTOS needs processing time to maintain its TCP-Stack and other stuff. Calling yield() every now and then is not a reliable solution.
The arduino standard library for the esp8266 mostly consists of blocking functions, which often leads the programmer into WDT-Pitfalls.
I always recommend people to use async libraries for the esp8266. There are plenty of them available. And always return control back to the operating system as much as possible because even simple delay()-Calls are capable of triggering WDT-Reset.

Arduino CC3000 Wifi Shield can not run a 'buildtest' in the cc3000 library

To check if my cc3000 Wifi Shield works, I download the Adafruit CC3000 Library and run the example in the library, which is 'buildtest'.
Here are the link of Adafruit and library.
https://learn.adafruit.com/adafruit-shield-compatibility/cc3000-wifi-shield
https://github.com/adafruit/Adafruit_CC3000_Library
But when I run 'buildtest', the only things I can see in Serial monitor are corrupt(or garbled) messages.
Here is the code of 'buildtest' in the library.
/***************************************************
This is an example for the Adafruit CC3000 Wifi Breakout & Shield
Designed specifically to work with the Adafruit WiFi products:
----> https://www.adafruit.com/products/1469
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Written by Kevin Townsend & Limor Fried for Adafruit Industries.
BSD license, all text above must be included in any redistribution
****************************************************/
/*
This example does a full test of core connectivity:
* Initialization
* SSID Scan
* AP connection
* DHCP printout
* DNS lookup
* Ping
* Disconnect
It's a good idea to run this sketch when first setting up the
module.
*/
#include <Adafruit_CC3000.h>
#include <ccspi.h>
#include <SPI.h>
#include <string.h>
#include "utility/debug.h"
// These are the interrupt and control pins
#define ADAFRUIT_CC3000_IRQ 3 // MUST be an interrupt pin!
// These can be any two pins
#define ADAFRUIT_CC3000_VBAT 5
#define ADAFRUIT_CC3000_CS 10
// Use hardware SPI for the remaining pins
// On an UNO, SCK = 13, MISO = 12, and MOSI = 11
Adafruit_CC3000 cc3000 = Adafruit_CC3000(ADAFRUIT_CC3000_CS, ADAFRUIT_CC3000_IRQ, ADAFRUIT_CC3000_VBAT,
SPI_CLOCK_DIVIDER); // you can change this clock speed but DI
#define WLAN_SSID "myNetwork" // cannot be longer than 32 characters!
#define WLAN_PASS "myPassword"
// Security can be WLAN_SEC_UNSEC, WLAN_SEC_WEP, WLAN_SEC_WPA or WLAN_SEC_WPA2
#define WLAN_SECURITY WLAN_SEC_WPA2
/**************************************************************************/
/*!
#brief Sets up the HW and the CC3000 module (called automatically
on startup)
*/
/**************************************************************************/
void setup(void)
{
Serial.begin(115200);
Serial.println(F("Hello, CC3000!\n"));
displayDriverMode();
Serial.print("Free RAM: "); Serial.println(getFreeRam(), DEC);
/* Initialise the module */
Serial.println(F("\nInitialising the CC3000 ..."));
if (!cc3000.begin())
{
Serial.println(F("Unable to initialise the CC3000! Check your wiring?"));
while(1);
}
/* Optional: Update the Mac Address to a known value */
/*
uint8_t macAddress[6] = { 0x08, 0x00, 0x28, 0x01, 0x79, 0xB7 };
if (!cc3000.setMacAddress(macAddress))
{
Serial.println(F("Failed trying to update the MAC address"));
while(1);
}
*/
uint16_t firmware = checkFirmwareVersion();
if (firmware < 0x113) {
Serial.println(F("Wrong firmware version!"));
for(;;);
}
displayMACAddress();
/* Optional: Get the SSID list (not available in 'tiny' mode) */
#ifndef CC3000_TINY_DRIVER
listSSIDResults();
#endif
/* Delete any old connection data on the module */
Serial.println(F("\nDeleting old connection profiles"));
if (!cc3000.deleteProfiles()) {
Serial.println(F("Failed!"));
while(1);
}
/* Optional: Set a static IP address instead of using DHCP.
Note that the setStaticIPAddress function will save its state
in the CC3000's internal non-volatile memory and the details
will be used the next time the CC3000 connects to a network.
This means you only need to call the function once and the
CC3000 will remember the connection details. To switch back
to using DHCP, call the setDHCP() function (again only needs
to be called once).
*/
/*
uint32_t ipAddress = cc3000.IP2U32(192, 168, 1, 19);
uint32_t netMask = cc3000.IP2U32(255, 255, 255, 0);
uint32_t defaultGateway = cc3000.IP2U32(192, 168, 1, 1);
uint32_t dns = cc3000.IP2U32(8, 8, 4, 4);
if (!cc3000.setStaticIPAddress(ipAddress, netMask, defaultGateway, dns)) {
Serial.println(F("Failed to set static IP!"));
while(1);
}
*/
/* Optional: Revert back from static IP addres to use DHCP.
See note for setStaticIPAddress above, this only needs to be
called once and will be remembered afterwards by the CC3000.
*/
/*
if (!cc3000.setDHCP()) {
Serial.println(F("Failed to set DHCP!"));
while(1);
}
*/
/* Attempt to connect to an access point */
char *ssid = WLAN_SSID; /* Max 32 chars */
Serial.print(F("\nAttempting to connect to ")); Serial.println(ssid);
/* NOTE: Secure connections are not available in 'Tiny' mode!
By default connectToAP will retry indefinitely, however you can pass an
optional maximum number of retries (greater than zero) as the fourth parameter.
ALSO NOTE: By default connectToAP will retry forever until it can connect to
the access point. This means if the access point doesn't exist the call
will _never_ return! You can however put in an optional maximum retry count
by passing a 4th parameter to the connectToAP function below. This should
be a number of retries to make before giving up, for example 5 would retry
5 times and then fail if a connection couldn't be made.
*/
if (!cc3000.connectToAP(WLAN_SSID, WLAN_PASS, WLAN_SECURITY)) {
Serial.println(F("Failed!"));
while(1);
}
Serial.println(F("Connected!"));
/* Wait for DHCP to complete */
Serial.println(F("Request DHCP"));
while (!cc3000.checkDHCP())
{
delay(100); // ToDo: Insert a DHCP timeout!
}
/* Display the IP address DNS, Gateway, etc. */
while (! displayConnectionDetails()) {
delay(1000);
}
#ifndef CC3000_TINY_DRIVER
/* Try looking up www.adafruit.com */
uint32_t ip = 0;
Serial.print(F("www.adafruit.com -> "));
while (ip == 0) {
if (! cc3000.getHostByName("www.adafruit.com", &ip)) {
Serial.println(F("Couldn't resolve!"));
}
delay(500);
}
cc3000.printIPdotsRev(ip);
/* Do a quick ping test on adafruit.com */
Serial.print(F("\n\rPinging ")); cc3000.printIPdotsRev(ip); Serial.print("...");
uint8_t replies = cc3000.ping(ip, 5);
Serial.print(replies); Serial.println(F(" replies"));
if (replies)
Serial.println(F("Ping successful!"));
#endif
/* You need to make sure to clean up after yourself or the CC3000 can freak out */
/* the next time you try to connect ... */
Serial.println(F("\n\nClosing the connection"));
cc3000.disconnect();
}
void loop(void)
{
delay(1000);
}
/**************************************************************************/
/*!
#brief Displays the driver mode (tiny of normal), and the buffer
size if tiny mode is not being used
#note The buffer size and driver mode are defined in cc3000_common.h
*/
/**************************************************************************/
void displayDriverMode(void)
{
#ifdef CC3000_TINY_DRIVER
Serial.println(F("CC3000 is configure in 'Tiny' mode"));
#else
Serial.print(F("RX Buffer : "));
Serial.print(CC3000_RX_BUFFER_SIZE);
Serial.println(F(" bytes"));
Serial.print(F("TX Buffer : "));
Serial.print(CC3000_TX_BUFFER_SIZE);
Serial.println(F(" bytes"));
#endif
}
/**************************************************************************/
/*!
#brief Tries to read the CC3000's internal firmware patch ID
*/
/**************************************************************************/
uint16_t checkFirmwareVersion(void)
{
uint8_t major, minor;
uint16_t version;
#ifndef CC3000_TINY_DRIVER
if(!cc3000.getFirmwareVersion(&major, &minor))
{
Serial.println(F("Unable to retrieve the firmware version!\r\n"));
version = 0;
}
else
{
Serial.print(F("Firmware V. : "));
Serial.print(major); Serial.print(F(".")); Serial.println(minor);
version = major; version <<= 8; version |= minor;
}
#endif
return version;
}
/**************************************************************************/
/*!
#brief Tries to read the 6-byte MAC address of the CC3000 module
*/
/**************************************************************************/
void displayMACAddress(void)
{
uint8_t macAddress[6];
if(!cc3000.getMacAddress(macAddress))
{
Serial.println(F("Unable to retrieve MAC Address!\r\n"));
}
else
{
Serial.print(F("MAC Address : "));
cc3000.printHex((byte*)&macAddress, 6);
}
}
/**************************************************************************/
/*!
#brief Tries to read the IP address and other connection details
*/
/**************************************************************************/
bool displayConnectionDetails(void)
{
uint32_t ipAddress, netmask, gateway, dhcpserv, dnsserv;
if(!cc3000.getIPAddress(&ipAddress, &netmask, &gateway, &dhcpserv, &dnsserv))
{
Serial.println(F("Unable to retrieve the IP Address!\r\n"));
return false;
}
else
{
Serial.print(F("\nIP Addr: ")); cc3000.printIPdotsRev(ipAddress);
Serial.print(F("\nNetmask: ")); cc3000.printIPdotsRev(netmask);
Serial.print(F("\nGateway: ")); cc3000.printIPdotsRev(gateway);
Serial.print(F("\nDHCPsrv: ")); cc3000.printIPdotsRev(dhcpserv);
Serial.print(F("\nDNSserv: ")); cc3000.printIPdotsRev(dnsserv);
Serial.println();
return true;
}
}
/**************************************************************************/
/*!
#brief Begins an SSID scan and prints out all the visible networks
*/
/**************************************************************************/
void listSSIDResults(void)
{
uint32_t index;
uint8_t valid, rssi, sec;
char ssidname[33];
if (!cc3000.startSSIDscan(&index)) {
Serial.println(F("SSID scan failed!"));
return;
}
Serial.print(F("Networks found: ")); Serial.println(index);
Serial.println(F("================================================"));
while (index) {
index--;
valid = cc3000.getNextSSID(&rssi, &sec, ssidname);
Serial.print(F("SSID Name : ")); Serial.print(ssidname);
Serial.println();
Serial.print(F("RSSI : "));
Serial.println(rssi);
Serial.print(F("Security Mode: "));
Serial.println(sec);
Serial.println();
}
Serial.println(F("================================================"));
cc3000.stopSSIDscan();
}
Sorry, I know this is very long code, but I can't understand why I get the corrupt message.
I don't think this problem is related to the setting of WIFI(#define WLAN SSID or #define WLAN PASS).
Because... if you look at the 'void setup' function, you can see
Serial.println(F("Hello, CC3000!\n"));
so I should see this message(Hello CC3000) no matter what.
Even if I set the my WLAN SSID and WLAN PASS, same corrupt message appears.
I can not check whether my cc3000 work well because of this corrupt message.
How can I solve this problem??
It usually means that you are using the wrong baudrate. In your program listing you have this line
“Serial.begin(115200);”
This sets the default baudrate.
In the Arduino software-> Tools->Serial Monitor-> bottom right corner you will see the baudrate window. This needs to match the baudrate in your program otherwise you get rubbish displayed.

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);
}

RS232 communication in MPlab

I am using MPlab to read data from a pic micro controller. I am using pic18F87J11.
The data that I want to read is on pin 3 of the DB9 of the RS232, and my RS232 is connected to the pic micro controller.
Can anyone help me or give me a simple sample code to do that??
Thank you,
//
// Designed by www.MCUExamples.com
// rasika0612#gmail.com
// Serial communications example. Echo data comes to serial port
// using serial receive interrupt.
//
#include <p18f4520.h>
#pragma config OSC = HS // 20MHz Crystal, (HS oscillator)
#pragma config PBADEN = OFF // PORTB<4:0> pins are configured as digital I/O on Reset)
#pragma config WDT = OFF // watch dog timer off
#pragma config LVP = OFF // Low voltage program off
unsigned char cUART_char;
unsigned char cUART_data_flg;
void init_uart(void);
void UART_putc(unsigned char c);
void InterruptHandlerLow ();
void main()
{
init_uart(); // init UART module
while (1) // infinite loop which handles ncoming data as they arrive
{
if (cUART_data_flg==1)// if new data available, send it back through USART tx line (echo it)
{
UART_putc(cUART_char);
cUART_data_flg=0; // clear new data flag so one charactor will echoed once
}
}
}
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
#pragma code InterruptVectorLow = 0x18
void InterruptVectorLow (void)
{
_asm
goto InterruptHandlerLow //jump to interrupt routine
_endasm
}
//----------------------------------------------------------------------------
// Low priority interrupt routine
#pragma code
#pragma interrupt InterruptHandlerLow
void InterruptHandlerLow ()
{
if (PIR1bits.RCIF==1)//is interrupt occured by EUSART receive?,
//then RCREG is full we have new data (cleared when RCREG is read)
{
if(RCSTA&0x06) //more efficient way than following commented method to check for reception error
//if(RCSTAbits.FERR==1 || RCSTAbits.OERR==1 )
{
RCSTAbits.CREN=0; //Overrun error (can be cleared by clearing bit CREN)
cUART_char=RCREG; //clear Framing error
RCSTAbits.CREN=1;
}
else
{
cUART_char = RCREG; // read new data into variable
cUART_data_flg = 1; // new data received. so enable flg
}
}
}
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
void init_uart(void) // init UART module for 9600bps boud, start bit 1, stopbit 1, parity NONE
{
cUART_data_flg=0; // init data receive flag to zero (no data)
TRISCbits.TRISC7=1; //Make UART RX pin input
TRISCbits.TRISC6=0; //Make UART TX pin output
SPBRGH = 0x02; //9600bps 20MHz Osc
SPBRG = 0x08;
RCSTAbits.CREN=1; //1 = Enables receiver
RCSTAbits.SPEN=1; //1 = Serial port enabled (configures RX/DT and TX/CK pins as serial port pins)
BAUDCONbits.BRG16=1;//1 = 16-bit Baud Rate Generator – SPBRGH and SPBRG
TXSTAbits.SYNC=0; //0 = Asynchronous mode
TXSTAbits.BRGH=1; //1 = High speed
TXSTAbits.TXEN=1; //1 = Transmit enabled
RCONbits.IPEN = 1; //enable Interrupt priority levels
IPR1bits.RCIP=0; // EUSART Receive Interrupt Priority 0 = Low priority
PIE1bits.RCIE=1; // 1 = Enables the EUSART receive interrupt
INTCONbits.GIEL = 1;//enable interrupts
INTCONbits.GIEH = 1;
}
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
void UART_putc(unsigned char c)
{
TXSTAbits.TXEN=0;// disable transmission
TXREG=c; // load txreg with data
TXSTAbits.TXEN=1; // enable transmission
while(TXSTAbits.TRMT==0) // wait here till transmit complete
{
Nop();
}
}

Arduino SNES wireless receiver code questions

I am building a transmitter and receiver pair for two SNES controllers, as I don't like using long extension cords to get the controllers to reach the couch. I'm using ATmega328Ps for the AVRs, with RF24l01 wireless transceivers (using ManiacBugs RF24 library). I am using a modified snesPad library on the transmitter to poll the button states of two controllers and return them as a 32-bit unsigned long, then transmitting that to the receiver. All standard libraries, no issues there (so far).
However, on the receiver side, I have to properly respond to the latch and clock signals from the SNES, and I haven't found any stock libraries for that. For those not familiar with the SNES controllers, they are basically two 8-bit parallel to serial shift registers in series. They latch all of the button states on the rising edge of the latch signal (12 µs pulse high, normally low), drive the first bit on the falling edge of latch, and then drive each successive bit on the rising edge of clock (6 µs delay from fall of latch, normally high, 6 µs low - 6 µs high cycle).
I've decided to use an external interrupt to trigger the proper behavior for the latch pulse and each clock pulse. I'm new to programming Arduinos though, and new to C/C++ as well. Although my code will compile, I'm not certain if it will actually function as intended. If someone who has some experience with AVR's could take a look at my code and let me know if it will work, and if not, where and why, I would be very grateful!
Arduino sketch follows:
/*
Copyright (C) 2012 John Byers <jbyers2#wgu.edu>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 3 as published by the Free Software Foundation
*/
/**
* Dual Wireless Retro Controller Adapter Transmitter
*
* This is the reciever side code for the adapter set.
*/
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <RF24_config.h>
//
// Hardware Configuration
//
RF24 radio(9,10);
//
// Variable Inits
//
volatile unsigned long state2 = 0xFFFFFFFF;
volatile byte i = 0;
const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL };
int strobe = 2;
int clock = 3;
volatile int data1 = 5;
volatile int data2 = 6;
bool firstLoop = true;
volatile int status2 = 1;
void setup()
{
radio.begin();
radio.setRetries(0,15);
radio.enableDynamicPayloads();
Serial.begin(57600);
pinMode(strobe, INPUT);
pinMode(clock, INPUT);
pinMode(data1, OUTPUT); digitalWrite(data1, LOW);
pinMode(data2, OUTPUT); digitalWrite(data2, LOW);
radio.openWritingPipe(pipes[1]);
radio.openReadingPipe(1,pipes[0]);
//
//Dump the configuration of the RF unit for debugging
//
radio.printDetails();
//
//Setup Interupts
//
attachInterrupt(strobe,latch,RISING);
attachInterrupt(clock,data,RISING);
}
void loop()
{
if (firstLoop) {
int status1 = 1;
bool ok = radio.write( &status1, sizeof(int));
firstLoop = false;
radio.startListening();
if (!ok) {
Serial.println("sync packet transmission failed");
}
else {
Serial.println("sync packet transmission successful");
}
}
if ( radio.available() )
{
unsigned long state = 0;
radio.read( &state, sizeof(unsigned long) );
Serial.println(state, BIN);
state2 = state;
}
else
{
Serial.println("No data recieved yet");
}
}
//Latch interrupt routine
void latch()
{
i = 0;
digitalWrite(data1,HIGH);
digitalWrite(data2,HIGH);
digitalWrite(data1,bitRead(state2,i));
digitalWrite(data2,bitRead(state2,(i+16)));
Serial.println("Bit0 out");
}
//Data interrupt routine
void data()
{
i++;
digitalWrite(data1,bitRead(state2,i));
digitalWrite(data2,bitRead(state2,(i+16)));
Serial.print("Bit");
Serial.print(i);
Serial.println(" out");
if(i=15)
{
digitalWrite(data1,LOW);
digitalWrite(data2,LOW);
radio.stopListening();
int status1 = status2;
bool ok = radio.write( &status1, sizeof(int));
if (!ok) {
Serial.println("sync packet transmission failed");
}
else {
Serial.println("sync packet transmission successful");
}
radio.startListening();
}
}

Resources