Add i2c capability for AVR
This commit is contained in:
		
							
								
								
									
										163
									
								
								src/arch/avr/i2c.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								src/arch/avr/i2c.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,163 @@ | |||||||
|  | #include <stdio.h> | ||||||
|  | #include <avr/io.h> | ||||||
|  | #include <util/twi.h> | ||||||
|  |  | ||||||
|  | #include <arch/i2c.h> | ||||||
|  | #include <board/cpu.h> | ||||||
|  |  | ||||||
|  | #define TIMEOUT (F_CPU/1000) | ||||||
|  |  | ||||||
|  | #define I2C_READ 0x01 | ||||||
|  | #define I2C_WRITE 0x00 | ||||||
|  |  | ||||||
|  | uint8_t i2c_start(uint8_t addr, uint8_t rw) { | ||||||
|  | 	uint32_t count; | ||||||
|  |  | ||||||
|  | 	// reset TWI control register | ||||||
|  | 	TWCR = 0; | ||||||
|  | 	// transmit START condition | ||||||
|  | 	TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN); | ||||||
|  | 	// wait for end of transmission | ||||||
|  | 	count = TIMEOUT; | ||||||
|  | 	while(!(TWCR & (1<<TWINT)) && count > 0) count -= 1; | ||||||
|  | 	if (count == 0) { | ||||||
|  | 		printf("i2c_start timed out waiting to send start to 0x%X\n", addr); | ||||||
|  | 		return 1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// check if the start condition was successfully transmitted | ||||||
|  | 	if((TWSR & 0xF8) != TW_START){ | ||||||
|  | 		printf("i2c_start failed to send start condition to 0x%X\n", addr); | ||||||
|  | 		return 1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// load slave addr into data register | ||||||
|  | 	TWDR = ((addr << 1) | rw); | ||||||
|  | 	// start transmission of addr | ||||||
|  | 	TWCR = (1<<TWINT) | (1<<TWEN); | ||||||
|  | 	// wait for end of transmission | ||||||
|  | 	count = TIMEOUT; | ||||||
|  | 	while(!(TWCR & (1<<TWINT)) && count > 0) count -= 1; | ||||||
|  | 	if (count == 0) { | ||||||
|  | 		printf("i2c_start timed out waiting to send addr to 0x%X\n", addr); | ||||||
|  | 		return 1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// check if the device has acknowledged the READ / WRITE mode | ||||||
|  | 	uint8_t twst = TW_STATUS & 0xF8; | ||||||
|  | 	if ((twst != TW_MT_SLA_ACK) && (twst != TW_MR_SLA_ACK)) { | ||||||
|  | 		printf("i2c_start failed to receive ack from 0x%X\n", addr); | ||||||
|  | 		return 1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void i2c_stop() { | ||||||
|  | 	// transmit STOP condition | ||||||
|  | 	TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | uint8_t i2c_write(uint8_t data) { | ||||||
|  | 	uint32_t count; | ||||||
|  |  | ||||||
|  | 	// load data into data register | ||||||
|  | 	TWDR = data; | ||||||
|  | 	// start transmission of data | ||||||
|  | 	TWCR = (1<<TWINT) | (1<<TWEN); | ||||||
|  | 	// wait for end of transmission | ||||||
|  | 	count = TIMEOUT; | ||||||
|  | 	while(!(TWCR & (1<<TWINT)) && count > 0) count -= 1; | ||||||
|  | 	if (count == 0) { | ||||||
|  | 		printf("i2c_write timed out waiting to send 0x%X\n", data); | ||||||
|  | 		return 1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if( (TWSR & 0xF8) != TW_MT_DATA_ACK ){ | ||||||
|  | 		printf("i2c_write failed to receive ack for 0x%X\n", data); | ||||||
|  | 		return 1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | uint8_t i2c_read_ack() { | ||||||
|  | 	// start TWI module and acknowledge data after reception | ||||||
|  | 	TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA); | ||||||
|  | 	// wait for end of transmission | ||||||
|  | 	while( !(TWCR & (1<<TWINT)) ); | ||||||
|  | 	// return received data from TWDR | ||||||
|  | 	return TWDR; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | uint8_t i2c_read_nack() { | ||||||
|  | 	// start receiving without acknowledging reception | ||||||
|  | 	TWCR = (1<<TWINT) | (1<<TWEN); | ||||||
|  | 	// wait for end of transmission | ||||||
|  | 	while( !(TWCR & (1<<TWINT)) ); | ||||||
|  | 	// return received data from TWDR | ||||||
|  | 	return TWDR; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | uint8_t i2c_recv(uint8_t addr, uint8_t* data, uint16_t length) { | ||||||
|  | 	if (i2c_start(addr, I2C_READ)) return 1; | ||||||
|  |  | ||||||
|  | 	uint16_t i; | ||||||
|  | 	for (i = 0; i < (length-1); i++) | ||||||
|  | 	{ | ||||||
|  | 		data[i] = i2c_read_ack(); | ||||||
|  | 	} | ||||||
|  | 	data[(length-1)] = i2c_read_nack(); | ||||||
|  |  | ||||||
|  | 	i2c_stop(); | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | uint8_t i2c_send(uint8_t addr, uint8_t* data, uint16_t length) { | ||||||
|  | 	if (i2c_start(addr, I2C_WRITE)) return 1; | ||||||
|  |  | ||||||
|  | 	uint16_t i; | ||||||
|  | 	for (i = 0; i < length; i++) { | ||||||
|  | 		if (i2c_write(data[i])) return 1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	i2c_stop(); | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | uint8_t i2c_get(uint8_t addr, uint8_t reg, uint8_t* data, uint16_t length) { | ||||||
|  | 	if (i2c_start(addr, I2C_WRITE)) return 1; | ||||||
|  |  | ||||||
|  | 	if (i2c_write(reg)) return 1; | ||||||
|  |  | ||||||
|  | 	if (i2c_start(addr, I2C_READ)) return 1; | ||||||
|  |  | ||||||
|  | 	uint16_t i; | ||||||
|  | 	for (i = 0; i < (length-1); i++) | ||||||
|  | 	{ | ||||||
|  | 		data[i] = i2c_read_ack(); | ||||||
|  | 	} | ||||||
|  | 	data[(length-1)] = i2c_read_nack(); | ||||||
|  |  | ||||||
|  | 	i2c_stop(); | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | uint8_t i2c_set(uint8_t addr, uint8_t reg, uint8_t* data, uint16_t length) { | ||||||
|  | 	if (i2c_start(addr, I2C_WRITE)) return 1; | ||||||
|  |  | ||||||
|  | 	if(i2c_write(reg)) return 1; | ||||||
|  |  | ||||||
|  | 	uint16_t i; | ||||||
|  | 	for (i = 0; i < length; i++) | ||||||
|  | 	{ | ||||||
|  | 		if (i2c_write(data[i])) return 1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	i2c_stop(); | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
							
								
								
									
										10
									
								
								src/arch/avr/include/arch/i2c.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/arch/avr/include/arch/i2c.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | #ifndef _ARCH_I2C_H | ||||||
|  | #define _ARCH_I2C_H | ||||||
|  |  | ||||||
|  | uint8_t i2c_recv(uint8_t addr, uint8_t* data, uint16_t length); | ||||||
|  | uint8_t i2c_send(uint8_t addr, uint8_t* data, uint16_t length); | ||||||
|  |  | ||||||
|  | uint8_t i2c_get(uint8_t addr, uint8_t reg, uint8_t* data, uint16_t length); | ||||||
|  | uint8_t i2c_set(uint8_t addr, uint8_t reg, uint8_t* data, uint16_t length); | ||||||
|  |  | ||||||
|  | #endif // _ARCH_I2C_H | ||||||
							
								
								
									
										43
									
								
								src/board/arduino/mega2560/battery.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/board/arduino/mega2560/battery.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | |||||||
|  | #include <stdio.h> | ||||||
|  |  | ||||||
|  | #include <board/i2c.h> | ||||||
|  |  | ||||||
|  | uint8_t smbus_read(uint8_t address, uint8_t command, uint16_t * data) { | ||||||
|  |     return i2c_get(address, command, (uint8_t *)data, 2); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void battery_debug(void) { | ||||||
|  |     uint16_t data = 0; | ||||||
|  |     uint8_t err = 0; | ||||||
|  |  | ||||||
|  |     #define command(N, A, V) { \ | ||||||
|  |         printf(#N ": "); \ | ||||||
|  |         err = smbus_read(A, V, &data); \ | ||||||
|  |         if (err) { \ | ||||||
|  |             printf("ERROR %02X\n", err); \ | ||||||
|  |             return; \ | ||||||
|  |         } \ | ||||||
|  |         printf("%04X\n", data); \ | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     printf("Battery:\n"); | ||||||
|  |     command(Temperature, 0x0B, 0x08); | ||||||
|  |     command(Voltage, 0x0B, 0x09); | ||||||
|  |     command(Current, 0x0B, 0x0A); | ||||||
|  |     command(Charge, 0x0B, 0x0D); | ||||||
|  |  | ||||||
|  |     printf("Charger:\n"); | ||||||
|  |     command(ChargeOption0, 0x09, 0x12); | ||||||
|  |     command(ChargeOption1, 0x09, 0x3B); | ||||||
|  |     command(ChargeOption2, 0x09, 0x38); | ||||||
|  |     command(ChargeOption3, 0x09, 0x37); | ||||||
|  |     command(ChargeCurrent, 0x09, 0x14); | ||||||
|  |     command(ChargeVoltage, 0x09, 0x15); | ||||||
|  |     command(DishargeCurrent, 0x09, 0x39); | ||||||
|  |     command(InputCurrent, 0x09, 0x3F); | ||||||
|  |     command(ProchotOption0, 0x09, 0x3C); | ||||||
|  |     command(ProchotOption1, 0x09, 0x3D); | ||||||
|  |     command(ProchotStatus, 0x09, 0x3A); | ||||||
|  |  | ||||||
|  |     #undef command | ||||||
|  | } | ||||||
							
								
								
									
										10
									
								
								src/board/arduino/mega2560/i2c.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/board/arduino/mega2560/i2c.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | #include <avr/io.h> | ||||||
|  |  | ||||||
|  | #include <board/cpu.h> | ||||||
|  | #include <board/i2c.h> | ||||||
|  |  | ||||||
|  | void i2c_init(unsigned long baud) { | ||||||
|  | 	TWAR = 0; | ||||||
|  | 	TWBR = (uint8_t)(((F_CPU / baud) - 16 ) / 2); | ||||||
|  | 	TWCR = 0; | ||||||
|  | } | ||||||
							
								
								
									
										6
									
								
								src/board/arduino/mega2560/include/board/battery.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/board/arduino/mega2560/include/board/battery.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | |||||||
|  | #ifndef _BOARD_BATTERY_H | ||||||
|  | #define _BOARD_BATTERY_H | ||||||
|  |  | ||||||
|  | void battery_debug(void); | ||||||
|  |  | ||||||
|  | #endif // _BOARD_BATTERY_H | ||||||
							
								
								
									
										6
									
								
								src/board/arduino/mega2560/include/board/i2c.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/board/arduino/mega2560/include/board/i2c.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | |||||||
|  | #ifndef _BOARD_I2C_H | ||||||
|  | #define _BOARD_I2C_H | ||||||
|  |  | ||||||
|  | void i2c_init(unsigned long baud); | ||||||
|  |  | ||||||
|  | #endif // _BOARD_I2C_H | ||||||
| @@ -2,9 +2,12 @@ | |||||||
|  |  | ||||||
| #include <arch/gpio.h> | #include <arch/gpio.h> | ||||||
| #include <arch/uart.h> | #include <arch/uart.h> | ||||||
|  | #include <board/battery.h> | ||||||
|  | #include <board/i2c.h> | ||||||
|  |  | ||||||
| void init(void) { | void init(void) { | ||||||
|     uart_stdio_init(0, __CONSOLE_BAUD__); |     uart_stdio_init(0, __CONSOLE_BAUD__); | ||||||
|  |     i2c_init(50000); | ||||||
| } | } | ||||||
|  |  | ||||||
| struct Gpio LED = GPIO(B, 7); | struct Gpio LED = GPIO(B, 7); | ||||||
| @@ -15,10 +18,14 @@ int main(void) { | |||||||
|     gpio_set_dir(&LED, true); |     gpio_set_dir(&LED, true); | ||||||
|     gpio_set(&LED, false); |     gpio_set(&LED, false); | ||||||
|     printf("Hello from System76 EC for the Arduino Mega 2560!\n"); |     printf("Hello from System76 EC for the Arduino Mega 2560!\n"); | ||||||
|  |  | ||||||
|  |     battery_debug(); | ||||||
|  |  | ||||||
|     for (;;) { |     for (;;) { | ||||||
|         int c = getchar(); |         int c = getchar(); | ||||||
|         if (c == '\r') { |         if (c == '\r') { | ||||||
|             putchar('\n'); |             putchar('\n'); | ||||||
|  |             battery_debug(); | ||||||
|         } else if (c > 0) { |         } else if (c > 0) { | ||||||
|             putchar(c); |             putchar(c); | ||||||
|         } |         } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user