I'm trying to implement polling functionality into my simple Linux kernel module called gpio_driver for my Raspberry Pi which should notify the user space poll function about the change of state of one of the GPIO pins (button push).
What I did in my gpio_driver is set the internal pull down on my GPIO_04 pin and direction to input, so when I press the button connected to the pin on one side and +5V on the other, it should generate a pulse which should notify the user space poll function that the button push happened and end the user space program.
The user space program code:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/time.h>
#include <poll.h>
int main(int argc, char *argv[])
{
char str[256];
struct pollfd pfd;
int fd, gpio;
char buf[8];
sprintf(str, "/dev/gpio_driver");
if ((fd = open(str, O_RDONLY)) < 0)
{
fprintf(stderr, "Failed, gpio %d not exported.\n", gpio);
exit(1);
}
pfd.fd = fd;
pfd.events = POLLPRI;
lseek(fd, 0, SEEK_SET); /* consume any prior interrupt */
read(fd, buf, sizeof buf);
poll(&pfd, 1, -1); /* wait for interrupt */
lseek(fd, 0, SEEK_SET); /* consume interrupt */
read(fd, buf, sizeof buf);
exit(0);
}
Complete driver code (poll function implementation at the end of file):
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/proc_fs.h>
#include <linux/string.h>
#include <linux/ioport.h>
#include <linux/ktime.h>
#include <linux/hrtimer.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/uaccess.h>
#include <linux/ioctl.h>
#include <linux/poll.h>
MODULE_LICENSE("Dual BSD/GPL");
/* if defined, timer callback will implement LED0 flashing and
SW0 reading after each interval */
#define TEST
// timer interval defined as (TIMER_SEC + TIMER_NANO_SEC)
#define TIMER_SEC 0
#define TIMER_NANO_SEC 250*1000*1000 /* 250ms */
// NOTE: Check Broadcom BCM8325 datasheet, page 91+
// NOTE: GPIO Base address is set to 0x7E200000,
// but it is VC CPU BUS address, while the
// ARM physical address is 0x3F200000, what
// can be seen in pages 5-7 of Broadcom
// BCM8325 datasheet, having in mind that
// total system ram is 0x3F000000 (1GB - 16MB)
// instead of 0x20000000 (512 MB)
/* GPIO registers base address. */
#define BCM2708_PERI_BASE (0x3F000000)
#define GPIO_BASE (BCM2708_PERI_BASE + 0x200000)
#define GPIO_ADDR_SPACE_LEN (0xB4)
//--
//Handle GPIO: 0-9
/* GPIO Function Select 0. */
#define GPFSEL0_OFFSET (0x00000000)
//Handle GPIO: 10-19
/* GPIO Function Select 1. */
#define GPFSEL1_OFFSET (0x00000004)
//Handle GPIO: 20-29
/* GPIO Function Select 2. */
#define GPFSEL2_OFFSET (0x00000008)
//Handle GPIO: 30-39
/* GPIO Function Select 3. */
#define GPFSEL3_OFFSET (0x0000000C)
//Handle GPIO: 40-49
/* GPIO Function Select 4. */
#define GPFSEL4_OFFSET (0x00000010)
//Handle GPIO: 50-53
/* GPIO Function Select 5. */
#define GPFSEL5_OFFSET (0x00000014)
//--
//GPIO: 0-31
/* GPIO Pin Output Set 0. */
#define GPSET0_OFFSET (0x0000001C)
//GPIO: 32-53
/* GPIO Pin Output Set 1. */
#define GPSET1_OFFSET (0x00000020)
//--
//GPIO: 0-31
/* GPIO Pin Output Clear 0. */
#define GPCLR0_OFFSET (0x00000028)
//GPIO: 32-53
/* GPIO Pin Output Clear 1. */
#define GPCLR1_OFFSET (0x0000002C)
//--
//GPIO: 0-31
/* GPIO Pin Level 0. */
#define GPLEV0_OFFSET (0x00000034)
//GPIO: 32-53
/* GPIO Pin Level 1. */
#define GPLEV1_OFFSET (0x00000038)
//--
//GPIO: 0-53
/* GPIO Pin Pull-up/down Enable. */
#define GPPUD_OFFSET (0x00000094)
//GPIO: 0-31
/* GPIO Pull-up/down Clock Register 0. */
#define GPPUDCLK0_OFFSET (0x00000098)
//GPIO: 32-53
/* GPIO Pull-up/down Clock Register 1. */
#define GPPUDCLK1_OFFSET (0x0000009C)
//--
/* PUD - GPIO Pin Pull-up/down */
typedef enum {PULL_NONE = 0, PULL_DOWN = 1, PULL_UP = 2} PUD;
//--
//000 = GPIO Pin 'x' is an input
//001 = GPIO Pin 'x' is an output
// By default GPIO pin is being used as an input
typedef enum {GPIO_DIRECTION_IN = 0, GPIO_DIRECTION_OUT = 1} DIRECTION;
//--
/* GPIO pins available on connector p1. */
#define GPIO_02 (2)
#define GPIO_03 (3)
#define GPIO_04 (4)
#define GPIO_05 (5)
#define GPIO_06 (6)
#define GPIO_07 (7)
#define GPIO_08 (8)
#define GPIO_09 (9)
#define GPIO_10 (10)
#define GPIO_11 (11)
#define GPIO_12 (12)
#define GPIO_13 (13)
#define GPIO_14 (14)
#define GPIO_15 (15)
#define GPIO_16 (16)
#define GPIO_17 (17)
#define GPIO_18 (18)
#define GPIO_19 (19)
#define GPIO_20 (20)
#define GPIO_21 (21)
#define GPIO_22 (22)
#define GPIO_23 (23)
#define GPIO_24 (24)
#define GPIO_25 (25)
#define GPIO_26 (26)
#define GPIO_27 (27)
/* Declaration of gpio_driver.c functions */
int gpio_driver_init(void);
void gpio_driver_exit(void);
static int gpio_driver_open(struct inode *, struct file *);
static int gpio_driver_release(struct inode *, struct file *);
static ssize_t gpio_driver_read(struct file *, char *buf, size_t , loff_t *);
static ssize_t gpio_driver_write(struct file *, const char *buf, size_t , loff_t *);
static unsigned int gpio_driver_poll(struct file *, poll_table *);
/* Structure that declares the usual file access functions. */
struct file_operations gpio_driver_fops =
{
open : gpio_driver_open,
release : gpio_driver_release,
read : gpio_driver_read,
write : gpio_driver_write,
poll : gpio_driver_poll
};
static DECLARE_WAIT_QUEUE_HEAD(gpio_driver_wait);
/* Declaration of the init and exit functions. */
module_init(gpio_driver_init);
module_exit(gpio_driver_exit);
/* Major number. */
int gpio_driver_major;
/* Buffer to store data. */
#define BUF_LEN 80
char* gpio_driver_buffer;
/* Virtual address where the physical GPIO address is mapped */
void* virt_gpio_base;
/*
* GetGPFSELReg function
* Parameters:
* pin - number of GPIO pin;
*
* return - GPFSELn offset from GPIO base address, for containing desired pin control
* Operation:
* Based on the passed GPIO pin number, finds the corresponding GPFSELn reg and
* returns its offset from GPIO base address.
*/
unsigned int GetGPFSELReg(char pin)
{
unsigned int addr;
if(pin >= 0 && pin <10)
addr = GPFSEL0_OFFSET;
else if(pin >= 10 && pin <20)
addr = GPFSEL1_OFFSET;
else if(pin >= 20 && pin <30)
addr = GPFSEL2_OFFSET;
else if(pin >= 30 && pin <40)
addr = GPFSEL3_OFFSET;
else if(pin >= 40 && pin <50)
addr = GPFSEL4_OFFSET;
else /*if(pin >= 50 && pin <53) */
addr = GPFSEL5_OFFSET;
return addr;
}
/*
* GetGPIOPinOffset function
* Parameters:
* pin - number of GPIO pin;
*
* return - offset of the pin control bit, position in control registers
* Operation:
* Based on the passed GPIO pin number, finds the position of its control bit
* in corresponding control registers.
*/
char GetGPIOPinOffset(char pin)
{
if(pin >= 0 && pin <10)
pin = pin;
else if(pin >= 10 && pin <20)
pin -= 10;
else if(pin >= 20 && pin <30)
pin -= 20;
else if(pin >= 30 && pin <40)
pin -= 30;
else if(pin >= 40 && pin <50)
pin -= 40;
else /*if(pin >= 50 && pin <53) */
pin -= 50;
return pin;
}
/*
* SetInternalPullUpDown function
* Parameters:
* pin - number of GPIO pin;
* pull - set internal pull up/down/none if PULL_UP/PULL_DOWN/PULL_NONE selected
* Operation:
* Sets to use internal pull-up or pull-down resistor, or not to use it if pull-none
* selected for desired GPIO pin.
*/
void SetInternalPullUpDown(char pin, PUD pull)
{
unsigned int gppud_offset;
unsigned int gppudclk_offset;
unsigned int tmp;
unsigned int mask;
/* Get the offset of GPIO Pull-up/down Register (GPPUD) from GPIO base address. */
gppud_offset = GPPUD_OFFSET;
/* Get the offset of GPIO Pull-up/down Clock Register (GPPUDCLK) from GPIO base address. */
gppudclk_offset = (pin < 32) ? GPPUDCLK0_OFFSET : GPPUDCLK1_OFFSET;
/* Get pin offset in register . */
pin = (pin < 32) ? pin : pin - 32;
/* Write to GPPUD to set the required control signal (i.e. Pull-up or Pull-Down or neither
to remove the current Pull-up/down). */
iowrite32(pull, virt_gpio_base + gppud_offset);
/* Wait 150 cycles – this provides the required set-up time for the control signal */
/* Write to GPPUDCLK0/1 to clock the control signal into the GPIO pads you wish to
modify – NOTE only the pads which receive a clock will be modified, all others will
retain their previous state. */
tmp = ioread32(virt_gpio_base + gppudclk_offset);
mask = 0x1 << pin;
tmp |= mask;
iowrite32(tmp, virt_gpio_base + gppudclk_offset);
/* Wait 150 cycles – this provides the required hold time for the control signal */
/* Write to GPPUD to remove the control signal. */
iowrite32(PULL_NONE, virt_gpio_base + gppud_offset);
/* Write to GPPUDCLK0/1 to remove the clock. */
tmp = ioread32(virt_gpio_base + gppudclk_offset);
mask = 0x1 << pin;
tmp &= (~mask);
iowrite32(tmp, virt_gpio_base + gppudclk_offset);
}
/*
* SetGpioPinDirection function
* Parameters:
* pin - number of GPIO pin;
* direction - GPIO_DIRECTION_IN or GPIO_DIRECTION_OUT
* Operation:
* Sets the desired GPIO pin to be used as input or output based on the direcation value.
*/
void SetGpioPinDirection(char pin, DIRECTION direction)
{
unsigned int GPFSELReg_offset;
unsigned int tmp;
unsigned int mask;
/* Get base address of function selection register. */
GPFSELReg_offset = GetGPFSELReg(pin);
/* Calculate gpio pin offset. */
pin = GetGPIOPinOffset(pin);
/* Set gpio pin direction. */
tmp = ioread32(virt_gpio_base + GPFSELReg_offset);
if(direction)
{ //set as output: set 1
mask = 0x1 << (pin*3);
tmp |= mask;
}
else
{ //set as input: set 0
mask = ~(0x1 << (pin*3));
tmp &= mask;
}
iowrite32(tmp, virt_gpio_base + GPFSELReg_offset);
}
/*
* GetGpioPinDirection function
* Parameters:
* pin - number of GPIO pin;
* Operation:
* Gets the desired GPIO pin's direction and returns 0 for IN and 1 for OUT.
*/
int GetGpioPinDirection(char pin)
{
unsigned int GPFSELReg_offset;
unsigned int tmp;
unsigned int mask;
/* Get base address of function selection register. */
GPFSELReg_offset = GetGPFSELReg(pin);
/* Calculate gpio pin offset. */
pin = GetGPIOPinOffset(pin);
/* Get gpio pin direction. */
tmp = ioread32(virt_gpio_base + GPFSELReg_offset);
mask = 0x1 << (pin*3);
tmp &= mask;
tmp=tmp>>(pin*3);
return tmp;
}
/*
* SetGpioPin function
* Parameters:
* pin - number of GPIO pin;
* Operation:
* Sets the desired GPIO pin to HIGH level. The pin should previously be defined as output.
*/
void SetGpioPin(char pin)
{
unsigned int GPSETreg_offset;
unsigned int tmp;
/* Get base address of gpio set register. */
GPSETreg_offset = (pin < 32) ? GPSET0_OFFSET : GPSET1_OFFSET;
pin = (pin < 32) ? pin : pin - 32;
/* Set gpio. */
tmp = 0x1 << pin;
iowrite32(tmp, virt_gpio_base + GPSETreg_offset);
}
/*
* ClearGpioPin function
* Parameters:
* pin - number of GPIO pin;
* Operation:
* Sets the desired GPIO pin to LOW level. The pin should previously be defined as output.
*/
void ClearGpioPin(char pin)
{
unsigned int GPCLRreg_offset;
unsigned int tmp;
/* Get base address of gpio clear register. */
GPCLRreg_offset = (pin < 32) ? GPCLR0_OFFSET : GPCLR1_OFFSET;
pin = (pin < 32) ? pin : pin - 32;
/* Clear gpio. */
tmp = 0x1 << pin;
iowrite32(tmp, virt_gpio_base + GPCLRreg_offset);
}
/*
* GetGpioPinValue function
* Parameters:
* pin - number of GPIO pin;
*
* return - the level read from desired GPIO pin
* Operation:
* Reads the level from the desired GPIO pin and returns the read value.
*/
char GetGpioPinValue(char pin)
{
unsigned int GPLEVreg_offset;
unsigned int tmp;
unsigned int mask;
/* Get base address of gpio level register. */
GPLEVreg_offset = (pin < 32) ? GPLEV0_OFFSET : GPLEV1_OFFSET;
pin = (pin < 32) ? pin : pin - 32;
/* Read gpio pin level. */
tmp = ioread32(virt_gpio_base + GPLEVreg_offset);
mask = 0x1 << pin;
tmp &= mask;
return (tmp >> pin);
}
/*
* Initialization:
* 1. Register device driver
* 2. Allocate buffer
* 3. Initialize buffer
* 4. Map GPIO Physical address space to virtual address
* 5. Initialize GPIO pin
*/
int gpio_driver_init(void)
{
int result = -1;
printk(KERN_INFO "Inserting gpio_driver module\n");
/* Registering device. */
result = register_chrdev(0, "gpio_driver", &gpio_driver_fops);
if (result < 0)
{
printk(KERN_INFO "gpio_driver: cannot obtain major number %d\n", gpio_driver_major);
return result;
}
gpio_driver_major = result;
printk(KERN_INFO "gpio_driver major number is %d\n", gpio_driver_major);
/* Allocating memory for the buffer. */
gpio_driver_buffer = kmalloc(BUF_LEN, GFP_KERNEL);
if (!gpio_driver_buffer)
{
result = -ENOMEM;
goto fail_no_mem;
}
/* Initialize data buffer. */
memset(gpio_driver_buffer, 0, BUF_LEN);
/* map the GPIO register space from PHYSICAL address space to virtual address space */
virt_gpio_base = ioremap(GPIO_BASE, GPIO_ADDR_SPACE_LEN);
if(!virt_gpio_base)
{
result = -ENOMEM;
goto fail_no_virt_mem;
}
/* Initialize GPIO pin. */
SetGpioPinDirection(GPIO_04, GPIO_DIRECTION_IN);
SetInternalPullUpDown(GPIO_04, PULL_DOWN); // rising edge
return 0;
fail_no_virt_mem:
/* Freeing buffer gpio_driver_buffer. */
if (gpio_driver_buffer)
{
kfree(gpio_driver_buffer);
}
fail_no_mem:
/* Freeing the major number. */
unregister_chrdev(gpio_driver_major, "gpio_driver");
return result;
}
/*
* Cleanup:
* 1. stop the timer
* 2. release GPIO pins (clear all outputs, set all as inputs and pull-none to minimize the power consumption)
* 3. Unmap GPIO Physical address space from virtual address
* 4. Free buffer
* 5. Unregister device driver
*/
void gpio_driver_exit(void)
{
printk(KERN_INFO "Removing gpio_driver module..\n");
/* Clear GPIO pins. */
ClearGpioPin(GPIO_04);
/* Set GPIO pins as inputs and disable pull-ups. */
SetGpioPinDirection(GPIO_04, GPIO_DIRECTION_IN);
SetInternalPullUpDown(GPIO_04, PULL_NONE);
/* Unmap GPIO Physical address space. */
if (virt_gpio_base)
{
iounmap(virt_gpio_base);
}
/* Freeing buffer gpio_driver_buffer. */
if (gpio_driver_buffer)
{
kfree(gpio_driver_buffer);
}
/* Freeing the major number. */
unregister_chrdev(gpio_driver_major, "gpio_driver");
}
/* File open function. */
static int gpio_driver_open(struct inode *inode, struct file *filp)
{
/* Success. */
return 0;
}
/* File close function. */
static int gpio_driver_release(struct inode *inode, struct file *filp)
{
/* Success. */
return 0;
}
/* File read function */
static ssize_t gpio_driver_read(struct file *filp, char *buf, size_t len, loff_t *f_pos)
{
/* Size of valid data in gpio_driver - data to send in user space. */
int data_size = 0;
/* Copy GPIO pin state to user space. */
gpio_driver_buffer[0]=GetGpioPinValue(GPIO_04);
/* TODO: fill gpio_driver_buffer here. */
if (*f_pos == 0)
{
/* Get size of valid data. */
data_size = strlen(gpio_driver_buffer);
/* Send data to user space. */
if (copy_to_user(buf, gpio_driver_buffer, data_size) != 0)
{
return -EFAULT;
}
else
{
(*f_pos) += data_size;
return data_size;
}
}
else
{
return 0;
}
}
/* File write function */
static ssize_t gpio_driver_write(struct file *filp, const char *buf, size_t len, loff_t *f_pos)
{
printk(KERN_ALERT "Sorry, this operation isn't supported.\n");
return -EPERM; // Operation not permitted error
}
static unsigned int gpio_driver_poll(struct file *filp, poll_table *wait)
{
unsigned int mask = 0;
poll_wait(filp, &gpio_driver_wait, wait);
if(GetGpioPinValue(GPIO_04)!=0)
gpio_driver_buffer[0] = 1;
if(gpio_driver_buffer[0] == 1)
{
mask |= POLLIN | POLLPRI;
wake_up_interruptible(&gpio_driver_wait);
}
gpio_driver_buffer[0] = 0;
return mask;
}
However, when I run my user space program, it doesn't react to my button pushes and it never exits. I'm pretty new to polling all the code written in the driver related to polling and user program I picked up online from different sources and some books (mainly Linux Device Drivers, 3rd edition, chapter 6). What am I doing wrong?
I wrote up a function to connect via UART and print a string for debugging purposes. But my logic does not add up somehow... I see the line "Random Number" printed in the console but blazingly fast... no matter what I add for a _delay_ms value, it is not usable. Did I miss out on anything?
Why is my delay function not having any influence on the output on the serial terminal?
void initUSART(void) {
#define BAUDRATE ((F_CPU) / (BAUD * 8UL)-1) // Set Baud Rate Value for UBRR
// Set register
UBRR0H = (BAUDRATE >> 8);
UBRR0L = BAUDRATE;
UCSR0A |= (1 << U2X0);
// Enable USART transmitter and receiver
UCSR0B = (1 << TXEN0) | (1 << RXEN0);
// Set 8 data bits and 1 stop bit
UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
}
void transmitByte(uint8_t data) {
// Wait for empty transmit buffer
loop_until_bit_is_set(UCSR0A, UDRE0);
UDR0 = data;
}
void printString(const char myString[]) {
uint8_t i = 0;
while (myString[i]) {
transmitByte(myString[i]);
i++;
}
}
int main(void)
{
setup();
randomSeed(adcRead(0));
while (1)
{
printString("Random Number:\n");
_delay_ms(100000);
}
return (0);
}
When I use \r\nat the end of the string, the output gets really strange:
When I try this test code everything works as expected, the LED blinks every second. I really don't see where the difference is, as it is the same function.
/* Blinker Demo */
// ------- Preamble -------- //
#include <avr/io.h> /* Defines pins, ports, etc */
#include <util/delay.h> /* Functions to waste time */
int main(void) {
// -------- Inits --------- //
DDRB |= 0b00000001; /* Data Direction Register B:
writing a one to the bit
enables output. */
// ------ Event loop ------ //
while (1) {
PORTB = 0b00000001; /* Turn on first LED bit/pin in PORTB */
_delay_ms(1000); /* wait */
PORTB = 0b00000000; /* Turn off all B pins, including LED */
_delay_ms(1000); /* wait */
} /* End event loop */
return 0; /* This line is never reached */
}
Only 4 letters are showing up. Like in the example, I send the string "abcdef", but it only shows the 4 letters "abcf". I don't know why the other letters don't show up. I'm using Atmega8 and Bray terminal. I'm already following from the datasheet [http://ww1.microchip.com/downloads/en/DeviceDoc/21822E.pdf][1]. But I've already found a dead end.
Implementation of functions
#include <avr/io.h>
#include <math.h>
#include <util/delay.h>
#define DD_SS PINB2 //Chip select ON RC2
#define DD_MOSI PINB3 // Master out - Slave in pin
#define DD_MISO PINB4 // Master in - Slave out pin
#define DD_SCK PINB5 // Clock from master
#define DDR_SPI PORTB // DDR_SPI
void serial_init(void)
{
UBRRH = 0x00;
UBRRL = 7;
UCSRC = (1<<URSEL)|(1<<USBS)|(3<<UCSZ0)|(1 << UCSZ1);
UCSRB = (1 << RXEN) | (1 << TXEN)| (1<<RXCIE);
}
unsigned char Usart_Receive(void)
{
while ((UCSRA & (1 << RXC)) == 0) {};
return UDR;
}
void Usart_Transmit(unsigned char c)
{
PORTD= 0b00000100; //RTS Enable
while ((UCSRA & (1 << UDRE)) == 0) {};
UDR = c;
PORTD= 0b00000000; //RTS Disable
}
void SPI_MasterInit(void)
{
DDRB = 0b00101100;
DDR_SPI = (1<<DD_MOSI)|(1<<DD_SCK);
SPCR = 0b01010000;
SPSR = 0b00000001;
}
unsigned char spi_transfer(volatile char data)
{
SPDR = data;
while(!(SPSR & (1<<SPIF)));
{
}
return SPDR;
}
void SPI_MasterTransmit (uint8_t Data)
{
uint16_t address;
SPCR = (1<<SPE) | (1<<MSTR) | (0<<CPHA);
DDR_SPI &= ~(1<<DD_SS); // Select EEPROM
spi_transfer(WREN); // Send WRITE_ENABLE command
DDR_SPI |= (1<<DD_SS); // Release EEPROM
DDR_SPI &= ~(1<<DD_SS); //ss goes low
spi_transfer(WRITE); // write data to memory
spi_transfer (address>>8);
spi_transfer (address);
spi_transfer(Data);
DDR_SPI |= (1<<DD_SS); //ss goes high
}
unsigned char SPI_MasterReceive(uint16_t address)
{
unsigned long data;
SPCR = (1<<SPE) | (1<<MSTR) | (0<<CPHA);
//waitBusy();
DDR_SPI &= ~(1<<DD_SS); //ss goes low
spi_transfer(READ); //enable write operation
spi_transfer (address>>8);
spi_transfer (address);
data = spi_transfer(0xff);
DDR_SPI |= (1<<DD_SS); //goes high
return data;
}
and this is main function
int main (void)
{
char data;
unsigned char address;
serial_init();
SPI_MasterInit();
while(1)
{
data = Usart_Receive();
_delay_ms(10);
SPI_MasterTransmit(data);
_delay_ms(10);
data = SPI_MasterReceive(address); //read data from the memory
_delay_ms(10); //pause for readability
Usart_Transmit(data);
}
return 0;
}
I hope someone can help me here. :)
Your USART is transmitting too fast for your receiver. By your fourth time through the main loop, the USART transmitter has overwritten the "d" with "e" and then with "f".
A way to get around this is to use interrupts for receiving data, instead of polling like you are doing now. But you won't be able to write to the EEPROM as fast as the interrupts come. Instead, you should queue up the letters into a circular array or linked list or some other data structure as they arrive, and then write them to EEPROM in the main loop as time allows.
Note that this solution will only help with bursty data; you save up the burst and then deal with it as you can. But if the USART is continuously too fast, then you will never be able to keep up.
To debug this issue you need to localise the place of problem and to do this you have to split your experiment on sub-tasks.
One of them is to check UART separately, the code gets here like:
while(1)
{
data = Usart_Receive();
_delay_ms(10);
Usart_Transmit(data);
}
The second one is to check SPI apart from UART stuff if you have JTAG, or altogether if you get managed with making UART working. For the separate SPI checking just comment Usart_Receive(); and Usart_Transmit(data); initialize data with anything and probably increment it in the while. Hope this idea helps.
I have made a library for LCD with HD44780 controller in it, and from the datasheet I read that the code 0x18 will shift the entire display one position to the left, but when I made that, the display disappears.
I have also read that 0x1C shifts the entire display one position to the right but when I made it, the entire display shifts to left.
My code:
/*
* main.c
*
* Created: 11/14/2013 7:54:02 PM
* Author: A R M T
*/
#include <avr/io.h>
#define F_CPU 1000000UL
#define __DELAY_BACKWARD_COMPATIBLE__
#include <util/delay.h>
#define LCD_DPRT PORTA //LCD DATA PORT
#define LCD_DDDR DDRA //LCD DATA DDR
#define LCD_DPIN PINA //LCD DATA PIN
#define LCD_CPRT PORTB //LCD COMMANDS PORT
#define LCD_CDDR DDRB //LCD COMMANDS DDR
#define LCD_CPIN PINB //LCD COMMANDS PIN
#define LCD_RS 0 //LCD RS
#define LCD_RW 1 //LCD RW
#define LCD_EN 2 //LCD EN
//*************************************************************
void delay_us(unsigned int d);
void lcdCommand(unsigned char cmnd);
void lcdData(unsigned char data);
void lcd_init(void);
void lcd_gotoxy(unsigned char x, unsigned char y);
void lcd_print(char *str);
//*************************************************************
void delay_us(unsigned int d)
{
_delay_us(d);
}
//*************************************************************
void lcdCommand(unsigned char cmnd)
{
LCD_DPRT = cmnd; //send cmnd to data port
LCD_CPRT &= ~(1<<LCD_RS); //RS = 0 for command
LCD_CPRT &= ~(1<<LCD_RW); //RW = 0 for write
LCD_CPRT |= (1<<LCD_EN); //EN = 1 for H-to-l pulse
delay_us(1); //Wait to make enable wide
LCD_CPRT &= ~(1<<LCD_EN); //EN = 1 for H-to-l pulse
delay_us(100); //Wait to make enable wide
}
//*************************************************************
void lcdData(unsigned char data)
{
LCD_DPRT = data; //send data to data port
LCD_CPRT |= (1<<LCD_RS); //RS = 1 for data
LCD_CPRT &= ~(1<<LCD_RW); //RW = 0 for write
LCD_CPRT |= (1<<LCD_EN); //EN = 1 for H-to-L pulse
delay_us(1); //wait to make enable wide
LCD_CPRT &= ~(1<<LCD_EN); //EN = 0 for H-to-L pulse
delay_us(100); //wait to make enable wide
}
//*************************************************************
void lcd_init(void)
{
LCD_DDDR = 0xFF;
LCD_CDDR = 0xFF;
LCD_CPRT &= ~(1<<LCD_EN); //LCD_EN = 0
delay_us(15000); //wait for init
lcdCommand(0x38); //init. LCD 2 line, 5 * 7 matrix
lcdCommand(0x0E); //display on, cursor on
lcdCommand(0x01); //clear LCD
delay_us(2000); //wait
lcdCommand(0x06); //shift cursor right
}
//*************************************************************
void lcd_gotoxy(unsigned char x, unsigned char y)
{
unsigned char firstCharAdr[] = {0x80, 0xC0, 0x94, 0xD4};
lcdCommand(firstCharAdr[y-1] + x - 1);
delay_us(100);
}
//*************************************************************
void lcd_print(char *str)
{
unsigned char i = 0;
while (str[i] != 0)
{
lcdData(str[i]);
i++;
}
}
//*************************************************************
int main(void)
{
lcd_init();
lcd_gotoxy(1,1);
lcd_print("Armia");
lcd_gotoxy(1,2);
lcd_print("Wagdy");
_delay_ms(1000 / 2);
lcdCommand(0x18); // Shift the entire display one position to the left
while(1);
return 0;
}
I meant that when I burned this code I excepected that aftr(1000 / 2) ms this output will shift to left
but what appears was(the word disappears instead of shifting left one postition)
Can any one help me in that problem please?!
I tried your code in proteus version 7.10 and shift left works fine
Note that you are using the _delay_us function with a variable parameter, that is not the correct way, it gives wrong delays and generates big hex file because it forces the float library to be included
delay.h manual
Note:
In order for these functions to work as intended, compiler optimizations must be enabled, and the delay time must be an
expression that is a known constant at compile-time. If these
requirements are not met, the resulting delay will be much longer (and
basically unpredictable), and applications that otherwise do not use
floating-point calculations will experience severe code bloat by the
floating-point library routines linked into the application.
Replace the code with a loop that calls the delay several times
while (d--)
{
_delay_ms(1);
}
For last 2 weeks am trying to learn timer & interrupt & wrote a program (with my understanding) to blink LEDs on ATMEGA2560 but no matter what I do TCNT0 never increments & ISR() function never gets called. Where am I going wrong and how can I fix it? Here is my code:
#include<avr/io.h>
#include<avr/interrupt.h>
#define READ_ATMEGA(ADDR) *((P_CHAR)(BASE_ADDR + ((ADDR) * ADDR_MULTIPLIER)))
#define WRITE_ATMEGA(ADDR, DATA) *((P_CHAR)(BASE_ADDR + ((ADDR) * ADDR_MULTIPLIER))) = DATA
#define BASE_ADDR 0x20
void init_timer0_ovf_interrupt(void);
void timer0_interrupt_isr(void);
void initialize_ports(void);
void delay(unsigned int no_65_5ms_interrupts);
void __attribute__((ISR)) timer0_interrupt_isr(void);
//#pragma interrupt_handler timer0_interrupt_isr:24
unsigned int delay_timer;
int main(void)
{
initialize_ports();
init_timer0_ovf_interrupt();
delay(46);
return 0;
}
void initialize_ports(void)
{
READ_ATMEGA(4) = 0xff;
WRITE_ATMEGA(5, 0x00);
}
void delay(unsigned int no_65_5ms_interrupts)
{
TCNT0 = 0x00;
delay_timer = 0;
while(delay_timer <= no_65_5ms_interrupts)
{
;
}
}
void init_timer0_ovf_interrupt(void)
{
TCCR0A = 0X00;
TCCR0B = 0x02;
TIMSK0 = 0x01;
TIFR0 = 1<<0;
OCR0A = 25;
sei();
}
void timer0_interrupt_isr(void)
{
delay_timer++;
if(delay_timer >= OCR0A)
{
PORTB = ~(PORTB);
delay_timer = 0;
}
}
The global variable delay_timer is shared between interrupt and non-interrupt code. It should be declared as volatile as the value can change outside of delay().
If you look at the generated code for delay() you'll probably see that the value of delay_timer isn't being re-read while spinning in the while loop.
Also, volatile isn't enough. You've got non-interrupt code and interrupt code both writing to the same variable (delay_timer). You need to protect writes to the variable in non-interrupt code, there's a race-condition there. The easy/lazy way is to disable interrupts & restore them in the non-interrupt code.
(As for setting up your interrupts & starting your timer, that info should be in the chip's datasheet. Usually that's the part that's easier to get right, it's the shared data stuff that bites people.)
3-4 days ago, I wrote the same program a little differently & got LEDs blinking but still not sure whether it is the correct way of using timer & interrupt. Could anyone please see this & tell me whether it's the correct or not? I managed to write this program by reading programs of timers, interrupts.
#include <avr/io.h>
#include <avr/interrupt.h>
volatile uint8_t intrs;
ISR(TIMER0_OVF_vect) {
/* this ISR is called when TIMER0 overflows */
intrs++;
/* strobe PORTB.5 - the LED on arduino boards */
if (intrs >= 61){
PORTB = ~PORTB;
intrs = 0;
}
}
int main(void) {
TCCR0B = 0x02;
/* Enable Timer Overflow Interrupts */
TIMSK0 = 0x01;
/* other set up */
DDRB = 0xff;
TCNT0 = 0;
intrs = 0;
/* Enable Interrupts */
sei();
while (1)
; /* empty loop */
}
If it's the correct way then I can start working on next step.
Thanks
If could be that your while loop in the delay function doesn't do anything and will not increment delay_timer so you are stuck in an endless loop:
void delay(unsigned int no_65_5ms_interrupts)
{
TCNT0 = 0x00;
delay_timer = 0;
while(delay_timer <= no_65_5ms_interrupts)
{
; //Nothing is happening here!!
}
}