Blog Sturntech

Archive for the ‘C’ Category

Embedded Linux: Changing TFT-LCD screens

without comments

A general approach to changing TFT-LCD screens is documented here. The platform for this posting is OMAP3 (gumstix overo and beagleboard -xm).

The OMAP2/3 Display Sub-System is nicely documented in this dev repository.

A common method for configuring omapfb is to pass the configuration parameters as boot arguments when the linux kernel is initialized.

You can find this list in the development repository at the top of this post.
This is a listing of the configuration parameters for omapfb.
vram
- Amount of total VRAM to preallocate. For example, "10M".

omapfb.video_mode
- Default video mode for default display. For example, "dvi:800x400MR-24@60". See drivers/video/modedb.c

omapfb.vram
- VRAM allocated for each framebuffer. Normally omapfb allocates vram
depending on the display size. With this you can manually allocate
more. For example "4M,3M" allocates 4M for fb0, 3M for fb1.

omapfb.debug
- Enable debug printing. You have to have OMAPFB debug support enabled
in kernel config.

omap-dss.def_disp
- Name of default display, to which all overlays will be connected.
Common examples are "lcd" or "tv".
omap-dss.debug
- Enable debug printing. You have to have DSS debug support enabled in
kernel config.

The kernel bootloader must be modified by adding these variables to the boot arguments. If you already have a working omapfb framebuffer, then most likely these variables are already being set to non-configured values.

If you are using the U-boot bootloader (found here) then it is a simple task to modify the bootargs variable. There is a pretty good writeup by Texas Instruments detailing how to modify U-boot (found here).

My bootargs line reads:
console=ttyS2,115200n8 mpurate=500 vram=12M omapfb.mode=dvi:640x480@60 omapdss.def_disp=dvi root=/dev/mmcblk0p2 rw rootfstype=ext3 rootwait

So you’ve configured omapfb and your embedded linux system is outputting a LCD signal suitable for driving a TFT-LCD panel. If the image is distorted, a possible cause is that your particular panel is not supported by the display device driver you chose to use.

The Linux Kernel has drivers. Some of these drivers were written to support TFT-LCD screen devices.

I’d suggest finding the datasheet for the new panel. Find the timing value ranges for the pixel clock, the vertical sync pulses, the horizontal sync pulses, the “front” and “back” porches, and the data enable signals.

Here is an example set of specifications (taken from the datasheet for an NEC NL6448AC33)

Find the source code for the display device driver you are using. The display device driver was selected when the “omap_dss.def_disp” and “omapfb.mode” bootargs variables were set. The various text strings used to configure that option (example, “tv”, “dvi”, “lcd”) are mapped to a device driver in the board file used to configure the linux kernel. It is usually found in the /arch/ directory of the kernel source. For example: When configuring the linux kernel for a beagleboard, it is found at /arch/arm/mach-omap2/board-omap3beagle.c

Here are the mappings for “dvi” and “tv”:

static struct omap_dss_device beagle_dvi_device = {
.type = OMAP_DISPLAY_TYPE_DPI,
.name = "dvi",
.driver_name = "generic_panel",
.phy.dpi.data_lines = 24,
.platform_enable = beagle_enable_dvi,
.platform_disable = beagle_disable_dvi,
};

static struct omap_dss_device beagle_tv_device = {
.name = "tv",
.driver_name = "venc",
.type = OMAP_DISPLAY_TYPE_VENC,
.phy.venc.type = OMAP_DSS_VENC_TYPE_SVIDEO,
.platform_enable = beagle_panel_enable_tv,
.platform_disable = beagle_panel_disable_tv,
};

The driver_name variable indicates the device driver that is being used. These are found in the /drivers/ directory of the linux kernel source. For example, the “panel-generic” driver linked to the “dvi” text can be found in /drivers/video/omap2/displays/panel-generic.c

The timings can be found near the top of this file:

static struct omap_video_timings generic_panel_timings = {
/* 640 x 480 @ 60 Hz Reduced blanking VESA CVT 0.31M3-R */
.x_res = 640,
.y_res = 480,
.pixel_clock = 23500,
.hfp = 48,
.hsw = 32,
.hbp = 80,
.vfp = 3,
.vsw = 4,
.vbp = 7,

Configuring these values to match those specified by your LCD panel should be a simple task from here. After that you can rebuild your kernel and hopefully enjoying a functional embedded display running on your new LCD.

If you need to do more than simply tune the timing values, I like to find kernel patches that modified something similar in the kernel for hints on where to look for the files I would need to modify. For the task of specifying a custom driver, here’s the kernel patch that changed a set of arch board files to use generic device drivers instead of the old default drivers (found here).

Significant development for the display you’re supporting can be submitted as a patch to the Linux Kernel Development Lists to possibly become part of the Linux Kernel and allow your work to benefit the Open-Source community.

Written by sturnfie

March 26th, 2011 at 7:36 pm

IMSA Intersession 2011 – Microcontroller Projects

without comments

For this year’s Intersession we will design and implement a Persistence of Vision (wikipedia) project.

This posting is my prototype design for the course. The full course documentation can be found at http://code.google.com/p/intersession2011-pov/

POV RevA Build

Build of POV RevA

This project will be based on a PIC18F46k20 microcontroller (link). The firmware for the microcontroller will be written in C and compiled with the MPLAB C18 compiler (link). The functionality of the microcontroller will be used to turn LEDs ON and OFF. Timer modules will be used to sequence timed changing of the LEDs. An EEPROM module will be used to store LED patterns to draw POV designs. Initial POV testing of the design will be battery powered and will be waved by hand. Further testing of the design will involve mounting the design to a motor rotating at 360RPM.

Overview

The idea of this project is to use a microcontroller to create a device that can draw designs in the “air” by illuminating LEDs in sequence as the device moves through a repetitive motion. For this project, our initial repetitive motion will be generated by hand-waving the board through the air.

The example board pictured above has 8 LEDs. Each of these LEDs is independently controllable. The trick is to light the LEDs in a way that a pattern is drawn with the board’s motion.

Linear Motion: Zig Zag

For our first design we will draw a zig-zag pattern using 6 LEDs while waving the board through a linear motion. The algorithm is simple: one LED will be lit at a time, starting at one side of the 6 LEDs and moving toward the other, then the direction will reverse and the pattern will repeat. While this algorithm is running, the student will wave the board through the air. A certain amount of tuning will be required to synchronize the lighting of the LEDs with the movement of the board. This tuning can occur by changing the speed of waving or by modifying the timer interval between LED changes.

POV Linear Zig-Zag

Here is an example program to generate the above pattern. This program assumes the LEDs are all connected such that they ground into PORTB[5:0]. See the Hardware Schematic further down this page for hardware implementation details.
//linear-POV-zigzag.c for IMSAInterssion 2011

#pragma config FOSC = INTIO67
#pragma config WDTEN = OFF, LVP = OFF
#include "p18f46K20.h"

void main (void) {
// We will be shifting 0's in from a side to move the 1 (lit LED)
char pattern = 0b00000001;
char shiftDir = 0; //1 RSHIFT, 0 LSHIFT

// Set TRISB to configure PORTB as OUTPUTS
TRISB = 0;

// Configure the LATB register to set all PORTB outputs HIGH
// Remember: We are grounding the LEDs into the PORT pins.
// The LED will turn on when the pin LATB OUPUT is LOW
LATB = 0xFF;
while (1) {
// Delay some cycles between each LED change
Delay1KTCYx(5);

// We are at one end of the LED string, switch directions
if(pattern == 0b1000000){
// Change shift direction
shiftDir = 1;
}
// We are at the other end, switch directions
else if(pattern == 0b00000001){
//Change shift direction
shiftDir = 0;
}

if(shiftDir == 0){
//Left shift in a 1
pattern = pattern << 1;}

//This blog´s software currently replaces certain characters with ascii command-codes.
// The above should be two left-arrow caret symbols; serving as the LeftShift operator.

else{ //if (shiftDir == 1){
//Right shift in a 1
pattern = pattern >> 1;

//This blog´s software currently replaces certain characters with  ascii command-codes.
// The above should be two right-arrow caret symbols; serving as the RightShift operator.

}

// Set the IO pin Latches
// We need to invert the pattern bit string since
// the LEDs are wired to light when LATB is LOW
LATB = ~pattern;
}
}

Hardware Schematic

The hardware schematic for the example design at the top of this page can be found below. The schematic has the full number of LEDs installed (the example only has the first 8 installed. 6 on PORTB and 2 on PORTD). The microcontroller is configured to use an internal 16MHz oscillator. Connecting the VCC pin from the ICSP connector to the circuit’s VCC bus allows the circuit to be powered from an external programming device (such as a PICKit2) during development. A 2xAA battery compartment is used to power the circuit during operation. Alternatively, this circuit will function from any 3.3V supply.

Schematic for POV RevA Device

Rotational Motion: Zig Zag

For the rotational motion we will mount the board to a motor. I chose to use a salvaged floppy disk motor. The motor control circuitry is configured to provide a 360RPM rotation rate.

Build of the POV RevA.1 design

Here is the zig-zag POV rotational pattern.

This is the zig-zag pattern shown on a POV RevA.1

The only change to the above code was modifying the delay count in the main while loop.

The modified lines should look like:
while (1) {
// Delay some cycles between each LED change
Delay100TCYx(5);

Written by sturnfie

December 26th, 2010 at 5:11 pm

MAX3421E USB HOST Debugging: HRSLT = hrJERR (0x0D)

with 4 comments

The MAX3421E (link) is a USB Peripheral/Host Controller with SPI Interface. It contains the digital logic and analog circuitry necessary to implement a full-speed USB peripheral or a full-/low-speed host compliant to USB specification rev 2.0.

When developing with the MAX3421E, routinely checking the HRSLT codes after each USB operation is important for confirming that a given operation reached a successful completion. The MAX3421E provides the HRSL register for this purpose; specifically the HRSLT field. The following is a summary of the error codes, taken from AN3785: Max3421E Programming Guide  (link).

HRSLT codes for the MAX3421E from AN3785

Information on the exact cause of the various error codes is scarce. I recently spent several hours trying to track down why a design was encountering the 0x0D (hrJERR) code.  Several forums have postings from developers confounded by this error, with no successful resolutions discovered. It seemed important to me to share what I learned.

In my design, the MAX3421E was fully responsive via SPI and had been configured into HOST mode. I was successfully retrieving my USB test peripheral’s Device Descriptor, Configuration Descriptors, and Endpoint Descriptors. I could execute Set_Address CONTROL messages with success and everything looked fine. My DATA0/DATA1 toggles were synced properly and my transactional NAKs were at a minimum.

Then I tried to initiate a BULK_OUT transfer and encountered the hrJERR error code. Long debugging story short, I had forgotten to execute a Set_Configuration CONTROL message to the peripheral in order to actually enable the functionality of the USB peripheral. Since my firmware was attempting to drive the MAX3421E Host (and subsequently the USB peripheral) without informing the USB peripheral what to expect, the JERR error resulted from the unexpected J-State change on the D+/D- lines.

Written by sturnfie

November 23rd, 2010 at 6:38 pm

Posted in C,Design,Tip,USB

Debugging a MCP2515: Stand-alone CAN controller

without comments

The MCP2515 is a stand-alone CAN controller with SPI interface (link). This chip is able to maintain the state-machine necessary to drive/read the signals on a CAN bus.

Block Diagram of the MCP2515

I recently chose this chip to allow a micro-controller based device to communicate via a CAN bus. The SPI interface proved pretty simple; a command message is clocked into the MCP2515, followed by an address, followed by any additional data required for the particular command.

The MCP2515 has several transmit buffers, several receive buffers, several status buffers, and plenty of configuration registers to handle interfacing with the CAN bus and filtering CAN messages.

I wrote the firmware and ordered the chip. The datasheet was very complete regarding how to interface over SPI, so I didn’t expect any significant roadblocks when testing my code with the chip.

I received the chip, built a prototype unit, and gave it a whirl. The chip sent a single CAN packet and went silent. Power-cycling the prototype led to non-repeat behavior (sometimes 1 packet, sometimes none).

Ok, it is definitely a software bug. I just needed to find it. I connected a scope to an unused  I/O pin on the micro-controller. I began reading out the contents of the various MCP2515 control registers at different portions of the software. I soon discovered (via the CANCTRL and CANSTAT registers) that the device was getting pushed into LOOPBACK mode.

This was strange. The only operational modes used in my software were CONFIGURATION and NORMAL. I quickly checked the mode bits before and after each signification communication with the MCP2515 and found that the mode changed to LOOPBACK after I set the ID field of the TXBnSIDH/TXBnSIDL registers.

It turns out that I mistakenly thought the xxxxSIDL register would be at the lower address, and xxxxSIDH at the higher, of the MCP2515 register map. When writing the ID to these registers, I would write the high-order bits to the xxxxSIDH address, then decrement the xxxxSIDH address thinking this would drop me to the xxxxSIDL address.

CAN Controller register map for the MCP2515

It didn’t. Going the wrong direction in the MCP2515 register map, I was writing the xxxxSIDL value to the TXBnCTRL register. The particular xxxxSIDL value was setting the TXREQ field of this register. After setting the xxxxSIDL ID field, my code started writing the data-to-be-sent into the transmit buffer. Once TXREQ was set, the MCP2515 tries to send the data in the transmit buffer….a buffer that I was concurrently attempting to update with the data to transfer.

This causes an internal error in the MCP2515 that results in the operational mode being set to LOOPBACK. While in this mode, the transmitter and receiver buffers are directly linked internally, so no external signals are generated. Once LOOPBACK mode is entered, a request to enter NORMAL mode must be sent to resume signal transmission/reception.

Written by sturnfie

November 10th, 2010 at 1:01 pm

Posted in C,CAN,Tip,USB

Using a PIC18F14K50 for a USB 2.0 CDC Peripheral Device

with 4 comments

USB has become quite ubiquitous across the wide realm of electronic devices. Every modern computer, most cellphones, and even some automobiles have USB support for peripheral devices. The key to USB’s success as an interconnection technology stems from the strict electrical and timing requirements imposed by the USB Implementers Forum (link). This standard is enforced through certification procedures, and ensures all USB devices can “talk to each other” (although making use of what is “said” is left to the implementing engineer).

USB and micro-controllers are commonly merged via external module conversion solutions. Companies such as FTDI Chip (link) have developed IC chips that can accept a UART/SPI communication from a micro-controller and translate/re-package it to conform to the signalling and voltage level requirements of USB. This is fine for most projects as the UART/SPI modules are quite common on micro-controllers, and even the limited maximum throughput provided by these solutions is adequate for most designs.

However, to unleash the full power of USB, this proxy approach is not acceptable. As such, various micro-controller manufacturers have begun integrating USB modules into their chip-sets. These modules are designed to drive a Universal Serial Bus with the proper signaling voltages. However, the designer must still develop firmware to program onto the micro-controller, in order to implement a USB device. The PIC18F14K50 is one such micro-controller.

Overview

The PIC18F14K50 (link) is a FLASH micro-controller that includes a full speed USB 2.0 compliant interface. This micro-controller has several other serial interfaces, a couple motor driving PWM modules, and 9 available ADC channels, which is all well and good, but really I chose it because it is cheap ($2) and has USB 2.0 support.

This project will implement a CDC (Communication Device Class) Peripheral device. There are several CDC classes (seen here). This project will focus implementing a serial communication interface.

Design

This design targets a minimalistic approach. The PIC18F14K50 is the sole logic IC. All other components are support components for the PIC. A 12MHZ oscillator provides a clock to the PIC. A ICSP interface allows for in-circuit programming of the PIC. The USB connector is a USB-A plug (to allow for connecting to a USB Host). A 1.5k resistor connects D+ to +V to indicate Full-Speed USB operation. An additional resistor network is driven from VBUS to function as a Connection-Sense circuit (to detect when the device is plugged into a PC). The PIC multiplexs PGC/PGD with D+/D-, so isolation resistors were included to prevent damage to a connected programmer or USB Host. This project is able to be powered from a bench supply during programming, and is to be powered from the USB connection during operation.

The firmware for this project enumerates the device as a CDC serial device. A PC that enumerates this device will create a virtual COM port through which a serial-terminal/telnet program may connect to the device at 9600,0,0,8. My favorite serial-terminal program is the always-free PuTTY. The windows driver file for this device is based on the generic cdc_ntxp.inf driver provided with the CCS C compiler.

Schematic

The following is the schematic for this project. As you can see, implementing USB only requires 4 pins. This circuit is the same irregardless of what USB Class is being implemented.

Schematic for the PIC18F14K50 USB Project

Firmware

The firmware for this project was written in C and compiled using the CCS C compiler v4.106. The USB stack provided with the CCS C compiler was used to handle the transactional overhead required by the USB standard.

The PIC is configured to use a high-speed external oscillator. The oscillator I chose operates at 12MHZ. The internal x4 PLL is enabled, scaling the processing clock to a frequency of 48MHZ. The PIC is then configured to operate at this clock frequency (CPUDIV1 implies no post-PLL scaling occurs on the processing clock).

The firmware is straight-forward to extend. On power-up, the device holds in a function loop. This loop begins by calling a function (usb_task()) that handles any USB transactions that are queued up. The first transactional status flag checked is whether or not an enumeration transaction has occurred (usb_enumerated()). Following the indication of a successful enumeration, the firmware polls the transaction receive buffer for new content (usb_cdc_kbhit()). If the HOST device sent data, the newest byte is retrieved via a call to usb_cdc_getc(). This firmware compares the result to a static char (ASCII byte). If the comparison passes, a character stream is written to the USB transmit buffer, which is then processed and sent to the HOST.

This firmware will respond with ” Here’s an A \n” when an ASCII “A” is seen in the receive buffer, and with ” Now a B \n” when an ASCII “B” is seen.

The usb_task() function call includes a periodic state check of a connection sense pin. This pin is used to sense the voltage on the VBUS +5V provided by the USB Host through the USB connector. If a voltage is not sensed on this input, the PIC firmware will de-enumerate and return to a waiting-for-enumeration state.

Here is the firmware.  Remember, you’ll need the CCS C compiler v4.106 to use it verbatim. It is recommended that you peruse through the USB_CDC libraries, as there are several variables that may need to be configured to meet your particular application. I won’t include these libraries here as they are included with the CCS C Compiler (and you will need that compiler to use this firmware).
——————————-

// PIC18F14K50 CDC USB Device
// Sturntech 2010

#include "18F14K50.h"

#fuses HS,NOWDT,NOPROTECT,NOLVP,NODEBUG,NOBROWNOUT,PLLEN,CPUDIV1,PUT,MCLR
#use delay(clock=48000000)

#define USB_CON_SENSE_PIN  PIN_C5

// Includes all USB code and interrupts
#include "usb_cdc.h"

void main(void)
{
   BYTE i;
   usb_init_cs();

   do
   {
      usb_task();
      if (usb_enumerated())
      {
         if (usb_cdc_kbhit())
         {
            i = toupper(usb_cdc_getc());

            if (i == 'A')
            {
               printf(usb_cdc_putc, " Here's an A \n");
            }

            if (i == 'B')
            {
               printf(usb_cdc_putc, " Now a B \n");
            }
         }
      }
   } while (TRUE);
}

——————————————

Build

There wasn’t a whole lot to this build. I used a breadboard for the IC and passive components. The USB-A plug was designed for PCB mounting, so I used a piece of protoboard as a mount. I knew I wasn’t going to program the circuit and connect the USB at the same time, so I neglected to place the isolation resistors. The first picture is a close-up of the device. The 2nd picture is the device talking over USB to the serial-terminal program on alaptop.

Close-up picture of the PIC18F14K50 USB Project

Picture of the PIC18F14K50 USB Project connected to a Laptop

Written by sturnfie

October 21st, 2010 at 4:54 pm

Posted in C,Design,EAGLE,PIC,USB