diff --git a/module06/ex00/header.h b/module06/ex00/header.h index 839651a..bc53dce 100644 --- a/module06/ex00/header.h +++ b/module06/ex00/header.h @@ -15,13 +15,6 @@ // // GLOBAL // -typedef enum { - WAITING, - MASTER, - SLAVE -} Role; -extern volatile Role role; -extern volatile uint8_t received_data; // // PROTOTYPES diff --git a/module06/ex01/Makefile b/module06/ex01/Makefile new file mode 100644 index 0000000..2e40cba --- /dev/null +++ b/module06/ex01/Makefile @@ -0,0 +1 @@ +include ../../Makefile \ No newline at end of file diff --git a/module06/ex01/header.h b/module06/ex01/header.h new file mode 100644 index 0000000..e9ebeb7 --- /dev/null +++ b/module06/ex01/header.h @@ -0,0 +1,48 @@ +#ifndef HEADER_H +#define HEADER_H + +#include +#include +#include + +#include "utils.h" +#include "bitmanip.h" +#include "interrupt.h" +#include "timer.h" +#include "usart.h" +#include "adc.h" +#include "i2c.h" + +// +// GLOBAL +// + +// +// PROTOTYPES +// +// main.c +void print_hex_value(char c); +// i2c.c +void i2c_init(void); +void i2c_start(void); +void i2c_send_addr_w(uint8_t slave_address); +void i2c_send_addr_r(uint8_t slave_address); +void i2c_write(unsigned char data); +void i2c_read(void); +void i2c_stop(void); +// uart.c +void uart_init(); +void uart_tx(char c); +void uart_printstr(const char* str); +void uart_printstr_endl(const char* str); +void uart_printstr_itoa_base(uint64_t value, uint8_t base); +void uart_printstr_itoa_base_endl(uint64_t value, uint8_t base); +// math.c +void int_to_hex_string(uint64_t value, char *out, uint8_t num_digits); +void int_to_string_base(uint64_t value, char *buffer, uint8_t base); + +// +// MACROS +// + +#endif // HEADER_H \ No newline at end of file diff --git a/module06/ex01/i2c.c b/module06/ex01/i2c.c new file mode 100644 index 0000000..bb7c9e4 --- /dev/null +++ b/module06/ex01/i2c.c @@ -0,0 +1,101 @@ +#include "header.h" + +#define TWI_PRESCALE_VALUE 1 // can be 1, 4, 16, 64 +#define TWI_FREQ 100000UL // 100kHz I2C + +typedef enum { + START, + STOP +} I2C_status; +volatile I2C_status i2c_status = START; + +void i2c_init(void) { + TWSR = TWI_PRESCALE_SET(TWI_PRESCALE_VALUE); // 22.9.3 : (Status Register) set prescaler + TWBR = ((F_CPU / TWI_FREQ) - 16) / (2 * TWI_PRESCALE_VALUE); // 22.9.1 : (Bit Rate Register) set SCL frequency (formula from datasheet, 22.5.2) +} + +void i2c_start(void) { + i2c_status = START; + TWCR = SEND_START_CONDITION; // 22.9.2 : (Control Register) send Start condition (22.7.1) ! writting 1 to TWINT clears it (set it to 0) + while (!(TEST(TWCR, TWINT))); // p225 example code : Wait for TWINT Flag set. This indicates that the START condition has been transmitted + + uint8_t status = TWSR & 0b11111000; // Table 22-2. Status codes for Master Transmitter Mode + if (status != TW_START && status != TW_REP_START) { + return i2c_stop(); + } +} + +void i2c_send_addr_w(uint8_t slave_address) { + if (i2c_status == STOP) { + return; + } + uint8_t addr = (slave_address << 1) | TW_WRITE; // 22.3.3 : address format SLA+R/W + TWDR = addr; // 22.9.4 : (Data Register) load data into TWDR register + TWCR = SEND_CONTINUE_TRANSMISSION; // 22.7.1 : Master Transmitter Mode + while (!(TEST(TWCR, TWINT))); // 22.7.1 : Wait for TWINT Flag set. This indicates that the SLA+R/W has been transmitted, and ACK/NACK has been received + + uint8_t status = TWSR & 0b11111000; // Table 22-2 : check status for Transmitter mode, mask prescaler bits + if (status == TW_MT_SLA_ACK) { // status 0x18 : slave address transmitted, slave acknoledged (continue transmission) + return; + } else if (status == TW_MT_SLA_NACK) { // status 0x20 : slave address transmitted, slave did not acknoledge (retry or send STOP) + return i2c_stop(); + } else if (status == TW_MT_ARB_LOST) { // status 0x38 : Arbitration Lost, Another I2C master took control of the bus (wait for bus to be free and retry) + return i2c_stop(); + } else { + return i2c_stop(); + } +} + +void i2c_send_addr_r(uint8_t slave_address) { + if (i2c_status == STOP) { + return; + } + uint8_t addr = (slave_address << 1) | TW_READ; // 22.3.3 : address format SLA+R/W + TWDR = addr; // 22.9.4 : (Data Register) load data into TWDR register + TWCR = SEND_CONTINUE_TRANSMISSION; // 22.7.1 : Master Transmitter Mode + while (!(TEST(TWCR, TWINT))); // 22.7.1 : Wait for TWINT Flag set. This indicates that the SLA+R/W has been transmitted, and ACK/NACK has been received + + uint8_t status = TWSR & 0b11111000; // Table 22-3 : check status for Receiver mode, mask prescaler bits + if (status == TW_MR_SLA_ACK) { // status 0x40 : slave address transmitted, slave acknoledged (continue transmission) + return; + } else if (status == TW_MR_SLA_NACK) { // status 0x48 : slave address transmitted, slave did not acknoledge (retry or send STOP) + return i2c_stop(); + } else if (status == TW_MR_ARB_LOST) { // status 0x38 : Arbitration Lost, Another I2C master took control of the bus (wait for bus to be free and retry) + return i2c_stop(); + } else { + return i2c_stop(); + } +} + +void i2c_write(unsigned char data) { + if (i2c_status == STOP) { + return; + } + TWDR = data; // 22.9.4 : (Data Register) load data into TWDR register + TWCR = SEND_CONTINUE_TRANSMISSION; // p225 example code : Load DATA into TWDR Register. Clear TWINT bit in TWCR to start transmission of data + while (!(TEST(TWCR, TWINT))); // p225 example code : Wait for TWINT Flag set. This indicates that the DATA has been transmitted, and ACK/NACK has been received + + uint8_t status = TWSR & 0b11111000; // p225 example code : Check status for Receiver mode. Mask prescaler bits. If status different from MT_DATA_ACK go to ERROR + if (status != TW_MT_DATA_ACK) { + return i2c_stop(); + } +} + +void i2c_read(void) { + if (i2c_status == STOP) { + return; + } + // TWDR = data; // 22.9.4 : (Data Register) load data into TWDR register + // TWCR = SEND_CONTINUE_TRANSMISSION; // p225 example code : Load DATA into TWDR Register. Clear TWINT bit in TWCR to start transmission of data + // while (!(TEST(TWCR, TWINT))); // p225 example code : Wait for TWINT Flag set. This indicates that the DATA has been transmitted, and ACK/NACK has been received + + // uint8_t status = TWSR & 0b11111000; // p225 example code : Check status for Receiver mode. Mask prescaler bits. If status different from MT_DATA_ACK go to ERROR + // if (status != TW_MT_DATA_ACK) { + // return i2c_stop(); + // } +} + +void i2c_stop(void) { + TWCR = SEND_STOP_CONDITION; // 22.9.2 : send stop condition + i2c_status = STOP; +} diff --git a/module06/ex01/main.c b/module06/ex01/main.c new file mode 100644 index 0000000..3fe5830 --- /dev/null +++ b/module06/ex01/main.c @@ -0,0 +1,68 @@ +#include "header.h" + +// sqtatus codes : +// -- Master -- +// TW_BUS_ERROR 0x00 = 0b0 = 0 -> illegal start or stop condition +// TW_START 0x08 = 0b1000 = 8 -> start condition transmitted +// TW_REP_START 0x10 = 0b10000 = 16 -> repeated start condition transmitted +// -- Master Transmitter -- +// TW_MT_SLA_ACK 0x18 = 0b11000 = 24 -> SLA+W transmitted, ACK received +// TW_MT_SLA_NACK 0x20 = 0b100000 = 32 -> SLA+W transmitted, NACK received +// TW_MT_DATA_ACK 0x28 = 0b101000 = 40 -> data transmitted, ACK received +// TW_MT_DATA_NACK 0x30 = 0b110000 = 48 -> data transmitted, NACK received +// TW_MT_ARB_LOST 0x38 = 0b111000 = 56 -> arbitration lost in SLA+W or data +// -- Master Receiver -- +// TW_MR_ARB_LOST 0x38 = 0b111000 = 56 -> arbitration lost in SLA+R or NACK +// TW_MR_SLA_ACK 0x40 = 0b1000000 = 64 -> SLA+R transmitted, ACK received +// TW_MR_SLA_NACK 0x48 = 0b1001000 = 72 -> SLA+R transmitted, NACK received +// TW_MR_DATA_ACK 0x50 = 0b1010000 = 80 -> data received, ACK returned +// TW_MR_DATA_NACK 0x58 = 0b1011000 = 88 -> data received, NACK returned +// TW_ST_SLA_ACK 0xA8 = 0b10101000 = 168 -> SLA+R received, ACK returned +// TW_NO_INFO 0xF8 = 0b11111000 = 248 -> no state information available + +#define SLAVE_ADDRESS 0x38 // doc AHT20, 7.3 : address of thermistor + +void print_hex_value(char c) {} + +void print_status(char *str) { + uint8_t status; + status = TWSR & 0b11111000; // Table 22-2. Status codes for Master Transmitter Mode + uart_printstr(str); + uart_printstr(" : "); + uart_printstr_itoa_base_endl(status, 2); +} + +// description +int main() { + uart_init(); + + print_status("before init"); + i2c_init(); + print_status("after init"); + + i2c_start(); + print_status("after start"); + + i2c_send_addr_w(SLAVE_ADDRESS); + print_status("after SLA+W"); + + i2c_write('t'); + print_status("after write t"); + + i2c_write('e'); + print_status("after write e"); + + i2c_write('e'); + print_status("after write e"); + + i2c_write('a'); + print_status("after write a"); + + i2c_write('t'); + print_status("after write t"); + + i2c_stop(); + print_status("after stop"); + + while(1); +} diff --git a/module06/ex01/math.c b/module06/ex01/math.c new file mode 100644 index 0000000..bafd17b --- /dev/null +++ b/module06/ex01/math.c @@ -0,0 +1,41 @@ +#include "header.h" + +void int_to_hex_string(uint64_t value, char *out, uint8_t num_digits) { // num_digits : number of digit of the output, ex 2 for 3FF (1023) -> FF + for (uint8_t i = 0; i < num_digits; ++i) { + uint8_t shift = (num_digits - 1 - i) * 4; + out[i] = INT_TO_HEX_CHAR((value >> shift) & 0x0F); + } + out[num_digits] = '\0'; +} + +void int_to_string_base(uint64_t value, char *buffer, uint8_t base) { // buffer must have the right size + if (base < 2 || base > 36) { + buffer[0] = '\0'; // unsupported base + return; + } + + // handle zero case + if (value == 0) { + buffer[0] = '0'; + buffer[1] = '\0'; + return; + } + + uint8_t size = 0; + uint64_t copy = value; + + while (copy) { + copy /= base; + size++; + } + + buffer[size] = '\0'; // null-terminate the string + size--; // adjust index for last digit + + while (value) { + uint8_t digit = value % base; + buffer[size] = digit < 10 ? ('0' + digit) : ('A' + digit - 10); + value /= base; + size--; + } +} \ No newline at end of file diff --git a/module06/ex01/uart.c b/module06/ex01/uart.c new file mode 100644 index 0000000..541b05e --- /dev/null +++ b/module06/ex01/uart.c @@ -0,0 +1,88 @@ +#include "header.h" + +// MACROS +// USART +#define USART_BAUDRATE 115200 +#define INPUT_SIZE 7 + +// GLOBAL VARIABLES + +// +// FUNCTIONS +// +void uart_init() { + UBRR0H = (unsigned char) (BAUD_PRESCALER(USART_BAUDRATE) >> 8); // 20.11.5 : UBRRnL and UBRRnH – USART Baud Rate Registers + UBRR0L = (unsigned char) BAUD_PRESCALER(USART_BAUDRATE); + UCSR0C = DATA_EIGHT_BIT; // 20.11.4 : set Frame Format + UCSR0B = TRANSMITTER_ENABLED; // 20.11.3 : enable Receiver, Transmitter, and interrupts +} + +// char uart_rx(void) { +// while (!TEST(UCSR0A, RXC0)); // 20.11.2 : do nothing until there are unread data in the receive buffer (UDRn), (RXCn flag in UCSRnA register set to 1 when buffer has data) +// return UDR0; // 20.11.1 : get data in buffer, UDRn – USART I/O Data Register (read and write) +// } + +void uart_tx(char c) { + while (!TEST(UCSR0A, UDRE0)); // 20.11.2 : do nothing until UDRn buffer is empty, (UDREn flag in UCSRnA register set to 1 when buffer empty) + UDR0 = (unsigned char) c; // 20.11.1 : Put data into buffer, UDRn – USART I/O Data Register (read and write) +} + +void uart_printstr(const char* str) { + while (*str) { + uart_tx(*str); + str++; + } +} + +void uart_printstr_endl(const char* str) { + uart_printstr(str); + uart_printstr("\r\n"); +} + +void uart_printstr_itoa_base(uint64_t value, uint8_t base) { + char buffer[100] = {0}; + int_to_string_base((uint16_t)value, buffer, base); + uart_printstr(buffer); +} + +void uart_printstr_itoa_base_endl(uint64_t value, uint8_t base) { + uart_printstr_itoa_base(value, base); + uart_printstr("\r\n"); +} + +// ISR(USART_RX_vect) { // Table 12-6 : we select the code for USART Receive +// char received_char = UDR0; // read received character +// if (received_char == '\b' || received_char == 127) { // if backspace is received +// if (input_index <= 0) { +// return; +// } +// if (input_index >= INPUT_SIZE) { +// return; +// } +// remove_last_character(); +// input_index--; +// uart_tx('\b'); // Move cursor back +// uart_tx(' '); // Erase the character on screen +// uart_tx('\b'); // Move cursor back again +// } else if (received_char == '\n' || received_char == '\r') { // if enter is received +// if (input_index != INPUT_SIZE) { +// return; +// } +// set_color((char *)color_input); +// reset_input(); +// } else { // any other character +// if (input_index >= INPUT_SIZE) { +// return; +// } +// if (!is_valid_color_input(received_char)) { +// return; +// } +// fill_str(received_char); +// uart_tx(received_char); +// input_index++; +// } +// } + +// ISR(USART_TX_vect) { // Table 12-6 : we select the code for USART Transmit +// char received_char = UDR0; // read received character +// }