|
|
Riadok 31: |
Riadok 31: |
| Ak sa nám už správne podarilo pripojiť senzor aj displej, hardvérová časť nášho zadania je hotová. | | Ak sa nám už správne podarilo pripojiť senzor aj displej, hardvérová časť nášho zadania je hotová. |
| Ďalej nasleduje programová časť. Pri tejto časti budeme používať datasheety, priložené vyššie. Ak chceme aby náš senzor vypisoval informácie na displej, budeme potrebovať knižnicu pre LCD displej. Pre výpis cez sériový kanál na obrazovku budeme potrebovať knižnicu UART. A pre použitie samotného senzora budeme ešte potrebovať používať knižnicu pre I2C zbernicu, ktorá slúži na prenos dát zo senzoru a na ich čítanie. | | Ďalej nasleduje programová časť. Pri tejto časti budeme používať datasheety, priložené vyššie. Ak chceme aby náš senzor vypisoval informácie na displej, budeme potrebovať knižnicu pre LCD displej. Pre výpis cez sériový kanál na obrazovku budeme potrebovať knižnicu UART. A pre použitie samotného senzora budeme ešte potrebovať používať knižnicu pre I2C zbernicu, ktorá slúži na prenos dát zo senzoru a na ich čítanie. |
| Samotný program budeme písať podľa datasheetu, kde si naštudujeme ako náš senzor komunikuje s mikroprocesorom a ako z neho čítať dáta. Na to nám slúži tento obrázok nižšie z datasheetu, ktorý opisuje ako správne komunikovať a vyčítavať zo senzoru informácie. | | Samotný program budeme písať podľa datasheetu, kde si naštudujeme ako náš senzor komunikuje s mikroprocesorom a ako z neho čítať dáta. Na to nám slúži obrázok priloženy nižšie z datasheetu, ktorý opisuje ako správne komunikovať a vyčítavať informácie zo senzoru. |
|
| |
|
| [[Súbor:CITANIE_DAT_SHT31.png|400px|thumb|center|Algoritmus čítania dát zo senzoru.]] | | [[Súbor:CITANIE_DAT_SHT31.png|400px|thumb|center|Algoritmus čítania dát zo senzoru.]] |
|
| |
|
| Na začiatku začneme posielať I2C adresu a hodnotu, ktorou nastavíme, že chceme zapisovať. Po prijatí ACK (acknowledge), nastavíme MSB (most significant bit) a s ním si pre jednoduchosť vypneme Clock stretching. Ďalej nastavíme LSB (least significant bit), ktorým si volíme repeatebility. Ďalej pošleme informáciu o adrese a hodnote a počkáme na ACK, inak posielanie opakujeme v cykle až kým nedostaneme odpoveď. Keď odpoveď dostaneme, môžeme si do bufferu vyčítať dáta ktoré nám prišli zo senzoru. Tieto dáta nám prídu v dvoch častiach. Jedna nám hovorí o teplote a druhá o vlhkosti. Pomocou vzorčekov, ktoré si nájdeme v datasheete si údaje zo senzoru prepočítame a za pomoci nami vytvorenej funkcie na výpočet a výpis ich môžeme vypísať na displej ale aj cez sériovú linku na obrazovku. | | Na začiatku začneme posielať I2C adresu a hodnotu, ktorou nastavíme, že chceme zapisovať dáta. Po prijatí ACK (acknowledge), nastavíme MSB (most significant bit) a s ním si pre jednoduchosť vypneme Clock stretching. Ďalej nastavíme LSB (least significant bit), ktorým si volíme repeatebility. Ďalej pošleme informáciu o adrese a hodnote a počkáme na ACK, inak posielanie opakujeme v cykle až kým nedostaneme odpoveď. Keď odpoveď dostaneme, môžeme si do bufferu vyčítať dáta ktoré nám prišli zo senzoru. Tieto dáta nám prídu v dvoch častiach. Jedna nám hovorí o teplote a druhá o vlhkosti. Pomocou vzorčekov, ktoré si nájdeme v datasheete si údaje zo senzoru prepočítame a za pomoci nami vytvorenej funkcie na výpočet a výpis ich môžeme vypísať na displej ale aj cez sériovú linku na obrazovku. |
|
| |
|
| === Algoritmus a program === | | === Algoritmus a program === |
Riadok 138: |
Riadok 138: |
| return (0); | | return (0); |
| } | | } |
|
| |
| </source></tab>
| |
| <tab name="lcd_ch.c"><source lang="c++" style="background: LightYellow;">
| |
| #include "lcd_ch.h"
| |
|
| |
| void lcd_data(unsigned char);
| |
| void lcd_command(unsigned char);
| |
|
| |
| void ini_ports(void);
| |
| void En_imp(void);
| |
|
| |
| void wr_data(unsigned char);
| |
| unsigned char busy_flag(void);
| |
| int8_t lcd_read_AC(void);
| |
|
| |
| void zob_text(char *);
| |
| void def_Clear_spec_znaky(void);
| |
|
| |
| void def_znak(unsigned char *, unsigned char);
| |
| void def_spec_znaky(void);
| |
|
| |
| unsigned char Znak_OO[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0}; // Prazdny znak dole su tri bodky
| |
|
| |
| unsigned char Znak_SC[8] = {0x1A, 0x1D, 0x04, 0x04, 0x04, 0x05, 0x02, 0}; // st.C
| |
| unsigned char Znak_SM[8] = {0x0A, 0x04, 0x0E, 0x10, 0x0E, 0x01, 0x1E, 0}; // s + mekcen
| |
| unsigned char Znak_CM[8] = {0x0A, 0x04, 0x0E, 0x10, 0x10, 0x11, 0x0C, 0}; // c + mekcen
| |
|
| |
| // Vynulovanie CGRAM
| |
| void def_Clear_spec_znaky(void)
| |
| {
| |
| // zapis specialneho znaku do CGRAM na poziciu 0, a 7
| |
| for (char i = 0; i < 8; i++)
| |
| def_znak(Znak_OO, i);
| |
| lcd_command(0x80);
| |
| }
| |
|
| |
| void def_spec_znaky(void)
| |
| {
| |
| // zapis vlastnych znakkov do CGRAM na poziciu 4, a 0 (8)
| |
|
| |
| def_znak(Znak_SC, 4); // stupen Celzia
| |
| def_znak(Znak_SM, 0); // s + makcen
| |
| // obnovenie obsahu AC. AC nastvime na 1. riadok, 1. znak
| |
| lcd_command(0x80);
| |
| }
| |
|
| |
| void def_spec_znaky_AC(void)
| |
| {
| |
| // zapis specialneho znaku do CGRAM na poziciu 7
| |
|
| |
| unsigned char obsah_AC = 0;
| |
| obsah_AC = lcd_read_AC(); //
| |
| // kon_vyp = obsah_AC;
| |
|
| |
| // sem dame nove vlastne znaky
| |
| def_znak(Znak_CM, 7); // c + makcen
| |
| // obnovenie obsahu AC. AC
| |
|
| |
| lcd_command(0x80 | obsah_AC);
| |
| }
| |
|
| |
| void ini_ports(void)
| |
| {
| |
|
| |
| /* inicializacia portov - vstupy / vystupy
| |
| oba typy displaja */
| |
| // nasledovna # riadky su spolocne pre oba typy LCD
| |
| LCD_CTRL_DDR |= (1 << LCD_EN_pin); // (Enable) output
| |
| LCD_CTRL_DDR |= (1 << LCD_RS_pin); // (RS) output
| |
| LCD_DATA_DDR |= (1 << LCD_D4_pin) | (1 << LCD_D5_pin) | (1 << LCD_D6_pin) | (1 << LCD_D7_pin); // Datove piny ako Output (Data pre display)
| |
| // klasicky LCD vyuziva aj RW pin
| |
| LCD_CTRL_DDR |= (1 << LCD_RW_pin); // (RW) output
| |
| }
| |
|
| |
| void EnOn(void)
| |
| {
| |
| LCD_CTRL_PORT |= (1 << LCD_EN_pin); // -> "log.1"
| |
| LCD_DELAY; // cca 400 ns
| |
| }
| |
|
| |
| void EnOff(void)
| |
| {
| |
| LCD_CTRL_PORT &= ~(1 << LCD_EN_pin); // -> "log.0"
| |
| LCD_DELAY;
| |
| }
| |
|
| |
| void En_imp(void)
| |
| {
| |
| LCD_CTRL_PORT |= (1 << LCD_EN_pin); // -> "log.1"
| |
| LCD_DELAY; // cca 400 ns
| |
| LCD_CTRL_PORT &= ~(1 << LCD_EN_pin); // -> "log.0" spolu cca 500 ns
| |
| }
| |
|
| |
| void lcd_init(void)
| |
| { // 4 bitove pripojenie display-a
| |
| ini_ports(); // inicializacia porov
| |
| _delay_ms(15);
| |
| // 1. -------------------
| |
| LCD_CTRL_PORT &= ~(1 << LCD_RS_pin); // set RS = to "log. 0" Instruction Register (IR)
| |
| LCD_CTRL_PORT &= ~(1 << LCD_RW_pin); // set R/W = to "log. 0" - Write
| |
| // RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
| |
| // 0 0 0 0 1 1 x x x x = 0x30
| |
| PORT_DATA_WR_H(0x30); // 8-bitove pripojenie
| |
| En_imp();
| |
| // 2. -------------------
| |
| // zopakujem 8-bitove pripojenie
| |
| _delay_ms(5);
| |
| En_imp();
| |
| // 3. -------------------
| |
| // zopakujem 8-bitove pripojenie
| |
| _delay_ms(1);
| |
| En_imp(); // - stacilo by delay 0.1ms, momentalne najkratsie nastavitelne je 1ms
| |
| // 4. -------------------
| |
| // zmenim na 4-bitove pripojenie
| |
| // RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
| |
| // 0 0 0 0 1 0 x x x x = 0x20
| |
| PORT_DATA_WR_H(0x20); // 4-bitove pripojenie
| |
| _delay_ms(1);
| |
| En_imp(); // - stacilo by delay 0.04ms
| |
| // -------------------
| |
| // LCD function set mod: DL = 0 - 4-bitove data, N = 1 - 2 riadky, F = 0 - 5x7 dots
| |
| // RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
| |
| // 0 0 0 0 1 DL N F x x = 0x28
| |
| // Mozeme nasledujuci prikaz pre LCD vynechat?
| |
| _delay_ms(2);
| |
| lcd_command(0x28);
| |
| // RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
| |
| // 0 0 0 0 0 0 0 0 0 1 = 0x01 , Display clear
| |
| // Mozeme nasledujuci prikaz pre LCD vynechat?
| |
| _delay_ms(2);
| |
| lcd_command(0x01);
| |
| // RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
| |
| // 0 0 0 0 0 0 0 1 I/D S = 0x06, I/D = 1 - inkrement
| |
| // Mozeme nasledujuci prikaz pre LCD vynechat?
| |
| _delay_ms(2);
| |
| lcd_command(0x02);
| |
| // RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
| |
| // 0 0 0 0 0 0 1 D C B = 0x0E, D = C = 1 - Display a kurzor zapnute
| |
| // Mozeme nasledujuci prikaz pre LCD vynechat?
| |
| _delay_ms(2);
| |
| lcd_command(0x0E); // B = 0 - blikanie vypnute
| |
| }
| |
|
| |
| // zapis data do Data Register
| |
| void lcd_data(unsigned char data)
| |
| {
| |
| while (busy_flag() & 0x80)
| |
| ;
| |
| LCD_CTRL_PORT |= (1 << LCD_RS_pin); // (RS = High)
| |
| LCD_CTRL_PORT &= ~(1 << LCD_RW_pin); // (RW = Low, write)
| |
| wr_data(data);
| |
| }
| |
|
| |
| // zapis commandu do Instruction Register
| |
| void lcd_command(unsigned char command)
| |
| {
| |
| while (busy_flag() & 0x80)
| |
| ;
| |
| // while(rd_BF()); // test BF sa da realizovat pre klasicky LCD, nie Shield
| |
| LCD_CTRL_PORT &= ~(1 << LCD_RS_pin); // (RS = Low)
| |
| LCD_CTRL_PORT &= ~(1 << LCD_RW_pin); // (RW = Low, write)
| |
| wr_data(command);
| |
| }
| |
|
| |
| void wr_data(unsigned char data)
| |
| {
| |
| PORT_DATA_WR_H(data); // data High nibble
| |
| En_imp();
| |
|
| |
| PORT_DATA_WR_L(data); // data Low nibble
| |
| En_imp();
| |
| }
| |
|
| |
| // klasicke pripojenie LCD umozni aj test BF
| |
| unsigned char busy_flag(void)
| |
| { // rd_BF
| |
| unsigned char pom = 0;
| |
| LCD_CTRL_PORT &= ~(1 << LCD_RS_pin); // (RS = Low)
| |
| LCD_CTRL_PORT |= (1 << LCD_RW_pin); // (RW = High, read)
| |
| // port B, datove bity budu teraz input
| |
| LCD_DATA_DDR &= ~((1 << LCD_D4_pin) | (1 << LCD_D5_pin) | (1 << LCD_D6_pin) | (1 << LCD_D7_pin)); // Datove piny nastavime na input, (Data pre disp)
| |
|
| |
| // Spravne by sa malo vycitavaj pred dobeznou hranou EN impulzu. Je tam urcity presah. Mozeme vycitat aj po dobeznej hrane.
| |
| // Spravne vycitavanie pocas EN impulzu.
| |
| EnOn();
| |
| pom = PORT_DATA_RD_H; // vycitam High nibble AC
| |
| EnOff();
| |
|
| |
| EnOn();
| |
| pom |= PORT_DATA_RD_L; // vycitam Low nibble AC
| |
| EnOff();
| |
| // datove bity zase output
| |
| LCD_DATA_DDR |= (1 << LCD_D4_pin) | (1 << LCD_D5_pin) | (1 << LCD_D6_pin) | (1 << LCD_D7_pin); // Datove piny nastavime na output (Data pre disp)
| |
| // if(pom & 0x80) return 1; // display je busy
| |
| // else
| |
| return pom; // display je not busy
| |
| }
| |
|
| |
| void zob_text(char *s)
| |
| {
| |
| register unsigned char c;
| |
| while ((c = *s++))
| |
| lcd_data(c); // retazec konci "nulou"
| |
| }
| |
|
| |
| void def_znak(unsigned char *ZnakArray, unsigned char kam)
| |
| {
| |
| lcd_command(0x40 | (kam << 3)); // nastavenie adresy znaku v CGRAM
| |
| for (unsigned char i = 0; i < 8; i++)
| |
| lcd_data(*(ZnakArray + i));
| |
| }
| |
|
| |
| void lcd_putc(char ch)
| |
| {
| |
| switch (ch)
| |
| {
| |
| case '\r':
| |
| lcd_command(0xC0); // Presun na za?iatok 2. riadka (0x80 + 0x40)
| |
| break;
| |
|
| |
| case '\f':
| |
| lcd_command(0X80); // Presun na zaciatok 1. riadku
| |
| break;
| |
|
| |
| default:
| |
| lcd_data(ch);
| |
| }
| |
| }
| |
|
| |
| /* ******************************************************** */
| |
| /* vypis retazca na poziciu, resp. podla nasledovnych */
| |
| /* formatovacich prikazov */
| |
| /* : \r - prechod na novy riadok */
| |
| /* : \f - prechod na zaciatok displeja */
| |
| /* ******************************************************** */
| |
|
| |
| void lcd_puts(char *str)
| |
| { /* definicia funkcie */
| |
|
| |
| while (*str)
| |
| {
| |
| lcd_putc(*str++);
| |
| }
| |
| }
| |
|
| |
| int8_t lcd_read_AC(void)
| |
| { // rd_BF
| |
| char pom_AC;
| |
|
| |
| while ((pom_AC = busy_flag()) & 0x80)
| |
| ;
| |
| // kedze po BF = 0 este cca 4us sa nezmenil obsah AC
| |
| // treba vycitat este raz
| |
| pom_AC = busy_flag();
| |
| return pom_AC; // display not busy
| |
| }
| |
|
| |
|
| |
| </source></tab>
| |
| <tab name="lcd_ch.h"><source lang="c++" style="background: LightYellow;">
| |
| /*
| |
| * lcd_ch.h
| |
| *
| |
| * Created: 3/10/2021 7:05:39 PM
| |
| * Author: Admin
| |
| */
| |
| #ifndef F_CPU
| |
| #define F_CPU 16000000UL /* Define CPU frequency here 16MHz */
| |
| #endif
| |
|
| |
| #ifndef LCD_CH_H_
| |
| #define LCD_CH_H_
| |
|
| |
| #include <avr/io.h>
| |
| #include <util/delay.h>
| |
|
| |
| // LCD klasik zapojenie vid. MIPS
| |
| // - http://senzor.robotika.sk/sensorwiki/index.php/LCD_displej_s_radi%C4%8Dom_HD44780
| |
|
| |
| #define LCD_CTRL_DDR DDRD
| |
| #define LCD_CTRL_PORT PORTD
| |
|
| |
| #define LCD_DATA_DDR DDRB
| |
| #define LCD_DATA_PORT PORTB
| |
| #define LCD_DATA_PIN PINB
| |
|
| |
| #define LCD_RS_pin 2
| |
| #define LCD_RW_pin 3
| |
| #define LCD_EN_pin 4
| |
|
| |
| #define LCD_D4_pin 1
| |
| #define LCD_D5_pin 2
| |
| #define LCD_D6_pin 3
| |
| #define LCD_D7_pin 4
| |
|
| |
| // Oneskorenie 6 SC
| |
| #define NOP() asm("nop")
| |
| #define LCD_DELAY \
| |
| NOP(); \
| |
| NOP(); \
| |
| NOP(); \
| |
| NOP(); \
| |
| NOP(); \
| |
| NOP();
| |
|
| |
| #define PORT_DATA_WR_H(x) \
| |
| LCD_DATA_PORT &= 0b11100001; \
| |
| LCD_DATA_PORT |= (x & 0xF0) >> 3
| |
| #define PORT_DATA_WR_L(x) \
| |
| LCD_DATA_PORT &= 0b11100001; \
| |
| LCD_DATA_PORT |= (x & 0x0F) << 1
| |
| #define PORT_DATA_RD_H ((LCD_DATA_PIN & ((1 << LCD_D4_pin) | (1 << LCD_D5_pin) | (1 << LCD_D6_pin) | (1 << LCD_D7_pin))) << 3)
| |
| #define PORT_DATA_RD_L ((LCD_DATA_PIN & ((1 << LCD_D4_pin) | (1 << LCD_D5_pin) | (1 << LCD_D6_pin) | (1 << LCD_D7_pin))) >> 1)
| |
|
| |
| void lcd_init(void);
| |
| void lcd_putc(char ch);
| |
| void lcd_puts(char *str);
| |
|
| |
| #endif /* LCD_CH_H_ */
| |
|
| |
| </source></tab>
| |
| <tab name="uart.c"><source lang="c++" style="background: LightYellow;">
| |
| /* ************************************************************************* */
| |
| /* FileName: uart.c */
| |
| /* ************************************************************************* */
| |
|
| |
| #include <avr/io.h>
| |
| #include <util/delay.h>
| |
| #include "uart.h"
| |
|
| |
| void hw_init( void )
| |
| {
| |
| DDRB |= (1<<LED); // PORTB.5 kde je LED ma byt OUTPUT
| |
| /* sem si mozete dopisat svoje vlastne inicializacne prikazy */
| |
| }
| |
|
| |
| void uart_init( void )
| |
| {
| |
| // for different BAUD rate change the project settings, or uncomment
| |
| // following two lines:
| |
| // #undef BAUD // avoid compiler warning
| |
| // #define BAUD 115200
| |
|
| |
| #include <util/setbaud.h> // requires defined BAUD
| |
|
| |
| UBRR0H = UBRRH_VALUE;
| |
| UBRR0L = UBRRL_VALUE;
| |
| #if USE_2X // defined in setbaud.h
| |
| UCSR0A |= (1 << U2X0);
| |
| #else
| |
| UCSR0A &= ~(1 << U2X0);
| |
| #endif
| |
|
| |
|
| |
| UCSR0C = _BV(UCSZ01) | _BV(UCSZ00); /* 8-bit data */
| |
| UCSR0B = _BV(RXEN0) | _BV(TXEN0); /* Enable RX and TX */
| |
| }
| |
|
| |
|
| |
| int uart_putc( char c, FILE *stream )
| |
| {
| |
| if (c == '\n')
| |
| uart_putc('\r',stream);
| |
|
| |
| loop_until_bit_is_set(UCSR0A, UDRE0); /* Wait until data register empty. */
| |
| UDR0 = c;
| |
| return 0;
| |
| }
| |
|
| |
|
| |
| void uart_puts(const char *s, FILE *stream)
| |
| {
| |
| while(*s)
| |
| {
| |
| uart_putc(*s++, stream);
| |
| }
| |
| }
| |
|
| |
| char uart_getc(void)
| |
| {
| |
| loop_until_bit_is_set(UCSR0A, RXC0); /* Wait until data exists. */
| |
| return UDR0;
| |
| }
| |
|
| |
| void delay(int delay) // vlastna funkcia pre dlhsie casy
| |
| {
| |
| for (int i=1; i<=delay; i++)
| |
| _delay_ms(1);
| |
| }
| |
|
| |
| </source></tab>
| |
| <tab name="uart.h"><source lang="c++" style="background: LightYellow;">
| |
| /* ************************************************************************* */
| |
| /* FileName: uart.h */
| |
| /* ************************************************************************* */
| |
|
| |
| #define LED PB5 // internal on-board LED
| |
|
| |
| /* na testovanie su uz zadefinovane */
| |
| // bit_is_set(PINB, SW1)
| |
| // bit_is_clear(PINB, SW1)
| |
|
| |
| /* na cakanie su preddefinovane slucky */
| |
| // loop_until_bit_is_set(PINB, SW1); // cakanie na uvolnenie tlacitka
| |
| // loop_until_bit_is_clear(PINB, SW1); // cakanie na stlacenie tlacitka
| |
|
| |
|
| |
| #define set_bit(ADDRESS,BIT) (ADDRESS |= (1<<BIT))
| |
| #define clear_bit(ADDRESS,BIT) (ADDRESS &= ~(1<<BIT))
| |
|
| |
| #ifndef UART_H_
| |
| #define UART_H_
| |
|
| |
| #include <stdio.h>
| |
|
| |
| #define BAUD_PRESCALE (((F_CPU / (BAUDRATE * 16UL))) - 1) // vzor?ek z datasheetu
| |
|
| |
| void hw_init( void );
| |
| void uart_init( void );
| |
|
| |
| /* Following definition is compatible with STDIO.H, for more
| |
| * information see https://www.appelsiini.net/2011/simple-usart-with-avr-libc/
| |
| */
| |
|
| |
| int uart_putc( char c, FILE *stream );
| |
| void uart_puts( const char *s, FILE *stream);
| |
|
| |
| char uart_getc( void );
| |
|
| |
| void delay(int delay);
| |
|
| |
| #endif /* UART_H_ */
| |
|
| |
| </source></tab>
| |
| <tab name="i2cmaster.c"><source lang="c++" style="background: LightYellow;">
| |
| /*************************************************************************
| |
| * Title: I2C master library using hardware TWI interface
| |
| * Author: Peter Fleury <pfleury@gmx.ch> http://jump.to/fleury
| |
| * File: $Id: twimaster.c,v 1.4 2015/01/17 12:16:05 peter Exp $
| |
| * Software: AVR-GCC 3.4.3 / avr-libc 1.2.3
| |
| * Target: any AVR device with hardware TWI
| |
| * Usage: API compatible with I2C Software Library i2cmaster.h
| |
| **************************************************************************/
| |
| #include <inttypes.h>
| |
| #include <compat/twi.h>
| |
|
| |
| #include "i2cmaster.h"
| |
|
| |
|
| |
| /* define CPU frequency in hz here if not defined in Makefile */
| |
| #ifndef F_CPU
| |
| #define F_CPU 16000000UL
| |
| #endif
| |
|
| |
| /* I2C clock in Hz */
| |
| #define SCL_CLOCK 100000L
| |
|
| |
|
| |
| /*************************************************************************
| |
| Initialization of the I2C bus interface. Need to be called only once
| |
| *************************************************************************/
| |
| void i2c_init(void)
| |
| {
| |
| /* initialize TWI clock: 100 kHz clock, TWPS = 0 => prescaler = 1 */
| |
|
| |
| TWSR = 0; /* no prescaler */
| |
| TWBR = ((F_CPU / SCL_CLOCK) - 16) / 2; /* must be > 10 for stable operation */
| |
|
| |
| }/* i2c_init */
| |
|
| |
|
| |
| /*************************************************************************
| |
| Issues a start condition and sends address and transfer direction.
| |
| return 0 = device accessible, 1= failed to access device
| |
| *************************************************************************/
| |
| unsigned char i2c_start(unsigned char address)
| |
| {
| |
| uint8_t twst;
| |
|
| |
| // send START condition
| |
| TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN);
| |
|
| |
| // wait until transmission completed
| |
| while (!(TWCR & (1 << TWINT)));
| |
|
| |
| // check value of TWI Status Register. Mask prescaler bits.
| |
| twst = TW_STATUS & 0xF8;
| |
| if ((twst != TW_START) && (twst != TW_REP_START)) return 1;
| |
|
| |
| // send device address
| |
| TWDR = address;
| |
| TWCR = (1 << TWINT) | (1 << TWEN);
| |
|
| |
| // wail until transmission completed and ACK/NACK has been received
| |
| while (!(TWCR & (1 << TWINT)));
| |
|
| |
| // check value of TWI Status Register. Mask prescaler bits.
| |
| twst = TW_STATUS & 0xF8;
| |
| if ((twst != TW_MT_SLA_ACK) && (twst != TW_MR_SLA_ACK)) return 1;
| |
|
| |
| return 0;
| |
|
| |
| }/* i2c_start */
| |
|
| |
|
| |
| /*************************************************************************
| |
| Issues a start condition and sends address and transfer direction.
| |
| If device is busy, use ack polling to wait until device is ready
| |
|
| |
| Input: address and transfer direction of I2C device
| |
| *************************************************************************/
| |
| void i2c_start_wait(unsigned char address)
| |
| {
| |
| uint8_t twst;
| |
|
| |
|
| |
| while (1)
| |
| {
| |
| // send START condition
| |
| TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN);
| |
|
| |
| // wait until transmission completed
| |
| while (!(TWCR & (1 << TWINT)));
| |
|
| |
| // check value of TWI Status Register. Mask prescaler bits.
| |
| twst = TW_STATUS & 0xF8;
| |
| if ((twst != TW_START) && (twst != TW_REP_START)) continue;
| |
|
| |
| // send device address
| |
| TWDR = address;
| |
| TWCR = (1 << TWINT) | (1 << TWEN);
| |
|
| |
| // wail until transmission completed
| |
| while (!(TWCR & (1 << TWINT)));
| |
|
| |
| // check value of TWI Status Register. Mask prescaler bits.
| |
| twst = TW_STATUS & 0xF8;
| |
| if ((twst == TW_MT_SLA_NACK) || (twst == TW_MR_DATA_NACK))
| |
| {
| |
| /* device busy, send stop condition to terminate write operation */
| |
| TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
| |
|
| |
| // wait until stop condition is executed and bus released
| |
| while (TWCR & (1 << TWSTO));
| |
|
| |
| continue;
| |
| }
| |
| //if( twst != TW_MT_SLA_ACK) return 1;
| |
| break;
| |
| }
| |
|
| |
| }/* i2c_start_wait */
| |
|
| |
|
| |
| /*************************************************************************
| |
| Issues a repeated start condition and sends address and transfer direction
| |
|
| |
| Input: address and transfer direction of I2C device
| |
|
| |
| Return: 0 device accessible
| |
| 1 failed to access device
| |
| *************************************************************************/
| |
| unsigned char i2c_rep_start(unsigned char address)
| |
| {
| |
| return i2c_start(address);
| |
|
| |
| }/* i2c_rep_start */
| |
|
| |
|
| |
| /*************************************************************************
| |
| Terminates the data transfer and releases the I2C bus
| |
| *************************************************************************/
| |
| void i2c_stop(void)
| |
| {
| |
| /* send stop condition */
| |
| TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
| |
|
| |
| // wait until stop condition is executed and bus released
| |
| while (TWCR & (1 << TWSTO));
| |
|
| |
| }/* i2c_stop */
| |
|
| |
|
| |
| /*************************************************************************
| |
| Send one byte to I2C device
| |
|
| |
| Input: byte to be transfered
| |
| Return: 0 write successful
| |
| 1 write failed
| |
| *************************************************************************/
| |
| unsigned char i2c_write(unsigned char data)
| |
| {
| |
| uint8_t twst;
| |
|
| |
| // send data to the previously addressed device
| |
| TWDR = data;
| |
| TWCR = (1 << TWINT) | (1 << TWEN);
| |
|
| |
| // wait until transmission completed
| |
| while (!(TWCR & (1 << TWINT)));
| |
|
| |
| // check value of TWI Status Register. Mask prescaler bits
| |
| twst = TW_STATUS & 0xF8;
| |
| if (twst != TW_MT_DATA_ACK) return 1;
| |
| return 0;
| |
|
| |
| }/* i2c_write */
| |
|
| |
|
| |
| /*************************************************************************
| |
| Read one byte from the I2C device, request more data from device
| |
|
| |
| Return: byte read from I2C device
| |
| *************************************************************************/
| |
| unsigned char i2c_readAck(void)
| |
| {
| |
| TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWEA);
| |
| while (!(TWCR & (1 << TWINT)));
| |
|
| |
| return TWDR;
| |
|
| |
| }/* i2c_readAck */
| |
|
| |
|
| |
| /*************************************************************************
| |
| Read one byte from the I2C device, read is followed by a stop condition
| |
|
| |
| Return: byte read from I2C device
| |
| *************************************************************************/
| |
| unsigned char i2c_readNak(void)
| |
| {
| |
| TWCR = (1 << TWINT) | (1 << TWEN);
| |
| while (!(TWCR & (1 << TWINT)));
| |
|
| |
| return TWDR;
| |
|
| |
| }/* i2c_readNak */
| |
|
| |
| </source></tab>
| |
| <tab name="i2cmaster.h"><source lang="c++" style="background: LightYellow;">
| |
| #ifndef _I2CMASTER_H
| |
| #define _I2CMASTER_H
| |
| /*************************************************************************
| |
| * Title: C include file for the I2C master interface
| |
| * (i2cmaster.S or twimaster.c)
| |
| * Author: Peter Fleury <pfleury@gmx.ch>
| |
| * File: $Id: i2cmaster.h,v 1.12 2015/09/16 09:27:58 peter Exp $
| |
| * Software: AVR-GCC 4.x
| |
| * Target: any AVR device
| |
| * Usage: see Doxygen manual
| |
| **************************************************************************/
| |
|
| |
| /**
| |
| @file
| |
| @defgroup pfleury_ic2master I2C Master library
| |
| @code #include <i2cmaster.h> @endcode
| |
|
| |
| @brief I2C (TWI) Master Software Library
| |
|
| |
| Basic routines for communicating with I2C slave devices. This single master
| |
| implementation is limited to one bus master on the I2C bus.
| |
|
| |
| This I2c library is implemented as a compact assembler software implementation of the I2C protocol
| |
| which runs on any AVR (i2cmaster.S) and as a TWI hardware interface for all AVR with built-in TWI hardware (twimaster.c).
| |
| Since the API for these two implementations is exactly the same, an application can be linked either against the
| |
| software I2C implementation or the hardware I2C implementation.
| |
|
| |
| Use 4.7k pull-up resistor on the SDA and SCL pin.
| |
|
| |
| Adapt the SCL and SDA port and pin definitions and eventually the delay routine in the module
| |
| i2cmaster.S to your target when using the software I2C implementation !
| |
|
| |
| Adjust the CPU clock frequence F_CPU in twimaster.c or in the Makfile when using the TWI hardware implementaion.
| |
|
| |
| @note
| |
| The module i2cmaster.S is based on the Atmel Application Note AVR300, corrected and adapted
| |
| to GNU assembler and AVR-GCC C call interface.
| |
| Replaced the incorrect quarter period delays found in AVR300 with
| |
| half period delays.
| |
|
| |
| @author Peter Fleury pfleury@gmx.ch http://tinyurl.com/peterfleury
| |
| @copyright (C) 2015 Peter Fleury, GNU General Public License Version 3
| |
|
| |
| @par API Usage Example
| |
| The following code shows typical usage of this library, see example test_i2cmaster.c
| |
|
| |
| @code
| |
|
| |
| #include <i2cmaster.h>
| |
|
| |
|
| |
| #define Dev24C02 0xA2 // device address of EEPROM 24C02, see datasheet
| |
|
| |
| int main(void)
| |
| {
| |
| unsigned char ret;
| |
|
| |
| i2c_init(); // initialize I2C library
| |
|
| |
| // write 0x75 to EEPROM address 5 (Byte Write)
| |
| i2c_start_wait(Dev24C02+I2C_WRITE); // set device address and write mode
| |
| i2c_write(0x05); // write address = 5
| |
| i2c_write(0x75); // write value 0x75 to EEPROM
| |
| i2c_stop(); // set stop conditon = release bus
| |
|
| |
|
| |
| // read previously written value back from EEPROM address 5
| |
| i2c_start_wait(Dev24C02+I2C_WRITE); // set device address and write mode
| |
|
| |
| i2c_write(0x05); // write address = 5
| |
| i2c_rep_start(Dev24C02+I2C_READ); // set device address and read mode
| |
|
| |
| ret = i2c_readNak(); // read one byte from EEPROM
| |
| i2c_stop();
| |
|
| |
| for(;;);
| |
| }
| |
| @endcode
| |
|
| |
| */
| |
|
| |
|
| |
| /**@{*/
| |
|
| |
| #if (__GNUC__ * 100 + __GNUC_MINOR__) < 304
| |
| #error "This library requires AVR-GCC 3.4 or later, update to newer AVR-GCC compiler !"
| |
| #endif
| |
|
| |
| #include <avr/io.h>
| |
|
| |
| /** defines the data direction (reading from I2C device) in i2c_start(),i2c_rep_start() */
| |
| #define I2C_READ 1
| |
|
| |
| /** defines the data direction (writing to I2C device) in i2c_start(),i2c_rep_start() */
| |
| #define I2C_WRITE 0
| |
|
| |
|
| |
| /**
| |
| @brief initialize the I2C master interace. Need to be called only once
| |
| @return none
| |
| */
| |
| extern void i2c_init(void);
| |
|
| |
|
| |
| /**
| |
| @brief Terminates the data transfer and releases the I2C bus
| |
| @return none
| |
| */
| |
| extern void i2c_stop(void);
| |
|
| |
|
| |
| /**
| |
| @brief Issues a start condition and sends address and transfer direction
| |
|
| |
| @param addr address and transfer direction of I2C device
| |
| @retval 0 device accessible
| |
| @retval 1 failed to access device
| |
| */
| |
| extern unsigned char i2c_start(unsigned char addr);
| |
|
| |
|
| |
| /**
| |
| @brief Issues a repeated start condition and sends address and transfer direction
| |
|
| |
| @param addr address and transfer direction of I2C device
| |
| @retval 0 device accessible
| |
| @retval 1 failed to access device
| |
| */
| |
| extern unsigned char i2c_rep_start(unsigned char addr);
| |
|
| |
|
| |
| /**
| |
| @brief Issues a start condition and sends address and transfer direction
| |
|
| |
| If device is busy, use ack polling to wait until device ready
| |
| @param addr address and transfer direction of I2C device
| |
| @return none
| |
| */
| |
| extern void i2c_start_wait(unsigned char addr);
| |
|
| |
|
| |
| /**
| |
| @brief Send one byte to I2C device
| |
| @param data byte to be transfered
| |
| @retval 0 write successful
| |
| @retval 1 write failed
| |
| */
| |
| extern unsigned char i2c_write(unsigned char data);
| |
|
| |
|
| |
| /**
| |
| @brief read one byte from the I2C device, request more data from device
| |
| @return byte read from I2C device
| |
| */
| |
| extern unsigned char i2c_readAck(void);
| |
|
| |
| /**
| |
| @brief read one byte from the I2C device, read is followed by a stop condition
| |
| @return byte read from I2C device
| |
| */
| |
| extern unsigned char i2c_readNak(void);
| |
|
| |
| /**
| |
| @brief read one byte from the I2C device
| |
|
| |
| Implemented as a macro, which calls either @ref i2c_readAck or @ref i2c_readNak
| |
|
| |
| @param ack 1 send ack, request more data from device<br>
| |
| 0 send nak, read is followed by a stop condition
| |
| @return byte read from I2C device
| |
| */
| |
| extern unsigned char i2c_read(unsigned char ack);
| |
| #define i2c_read(ack) (ack) ? i2c_readAck() : i2c_readNak();
| |
|
| |
|
| |
|
| |
| /**@}*/
| |
| #endif
| |
|
| |
| </source></tab>
| |
| </tabs> | | </tabs> |
|
| |
|