https://obrazki.elektroda.pl/6101373600_1546371743_thumb.jpg Dzień dobry. Dziś prezentuję kolejny drobny projekt, jaki wykonałem na potrzeby własne. Jest to mały serwer do różnorakich celów testowych mojej platformy składającej się z ośmiu innych komputerów (niebawem także i ją zaprezentuję). Płyta główna pochodzi z laptopa Dell E6330, a obudowa natomiast ze starego tunera SAT BCT1530. Prócz koniecznych modyfikacji konstrukcyjnych, okazało się konieczne dorobienie układu symulującego ładowarkę Dell po protokole 1-wire, w przeciwnym razie układ ładowania nie pracował. Na przednim panelu zostały dodane dwa wyświetlacze OLED na I2C sterowane z portu VGA oraz w miejscu CDROMu został dodany dodatkowy dysk twardy na obrazy dockerowe. Dysk ten wymaga 12V, więc została dodana przetwornica step-down, a całość zasilania jest sterowana z odpowiedniego pinu złącza CD-romu, tak aby w chwili przejścia komputera w stan uśpienia dysk był automatycznie odłączany od zasilania. Zasilacz pochodził z jakiś starszych zapasów i jak widać na zdjęciach został wyposażony w dodatkowy radiator i większej mocy układ mostka. https://obrazki.elektroda.pl/2051998000_1546369395_thumb.jpg https://obrazki.elektroda.pl/1350811800_1546369395_thumb.jpg https://obrazki.elektroda.pl/2416669500_1546369402_thumb.jpg https://obrazki.elektroda.pl/4913243500_1546369403_thumb.jpg https://obrazki.elektroda.pl/2766575100_1546369407_thumb.jpg https://obrazki.elektroda.pl/5027412800_1546369381_thumb.jpg https://obrazki.elektroda.pl/1420711800_1546369381_thumb.jpg https://obrazki.elektroda.pl/5730975100_1546369381_thumb.jpg https://obrazki.elektroda.pl/8152547700_1546369384_thumb.jpg https://obrazki.elektroda.pl/3215516200_1546369385_thumb.jpg https://obrazki.elektroda.pl/3503831900_1546369388_thumb.jpg https://obrazki.elektroda.pl/9341221800_1546369388_thumb.jpg https://obrazki.elektroda.pl/8181566000_1546369388_thumb.jpg https://obrazki.elektroda.pl/9389589100_1546369391_thumb.jpg https://obrazki.elektroda.pl/2281787300_1546369391_thumb.jpg https://obrazki.elektroda.pl/4693659400_1546369394_thumb.jpg https://obrazki.elektroda.pl/5784967000_1546369384_thumb.jpg https://obrazki.elektroda.pl/8797812100_1546369397_thumb.jpg https://obrazki.elektroda.pl/2459506000_1546369398_thumb.jpg https://obrazki.elektroda.pl/5723178000_1546369400_thumb.jpg https://obrazki.elektroda.pl/7477817700_1546369400_thumb.jpg https://obrazki.elektroda.pl/5024550700_1546369400_thumb.jpg https://obrazki.elektroda.pl/9947376600_1546369404_thumb.jpg https://obrazki.elektroda.pl/6427137400_1546369405_thumb.jpg https://obrazki.elektroda.pl/2457783400_1546369406_thumb.jpg https://obrazki.elektroda.pl/7392239400_1546369406_thumb.jpghttps://obrazki.elektroda.pl/1726118700_1546369410_thumb.jpg https://obrazki.elektroda.pl/1576070700_1546369410_thumb.jpg https://obrazki.elektroda.pl/6779753300_1546371817_thumb.jpg https://filmy.elektroda.pl/32_1546370274.mp4 https://filmy.elektroda.pl/72_1546370302.mp4 W załączniku dodaję kod do symulatora ładowarki:
#include & lt; avr/pgmspace.h & gt;
#include " crc8.h "
static const unsigned char PROGMEM Crc8Table[256] = {
0, 94, 188, 226, 97, 63, 221, 131, 194, 156, 126, 32, 163, 253, 31, 65,
157, 195, 33, 127, 252, 162, 64, 30, 95, 1, 227, 189, 62, 96, 130, 220,
35, 125, 159, 193, 66, 28, 254, 160, 225, 191, 93, 3, 128, 222, 60, 98,
190, 224, 2, 92, 223, 129, 99, 61, 124, 34, 192, 158, 29, 67, 161, 255,
70, 24, 250, 164, 39, 121, 155, 197, 132, 218, 56, 102, 229, 187, 89, 7,
219, 133, 103, 57, 186, 228, 6, 88, 25, 71, 165, 251, 120, 38, 196, 154,
101, 59, 217, 135, 4, 90, 184, 230, 167, 249, 27, 69, 198, 152, 122, 36,
248, 166, 68, 26, 153, 199, 37, 123, 58, 100, 134, 216, 91, 5, 231, 185,
140, 210, 48, 110, 237, 179, 81, 15, 78, 16, 242, 172, 47, 113, 147, 205,
17, 79, 173, 243, 112, 46, 204, 146, 211, 141, 111, 49, 178, 236, 14, 80,
175, 241, 19, 77, 206, 144, 114, 44, 109, 51, 209, 143, 12, 82, 176, 238,
50, 108, 142, 208, 83, 13, 239, 177, 240, 174, 76, 18, 145, 207, 45, 115,
202, 148, 118, 40, 171, 245, 23, 73, 8, 86, 180, 234, 105, 55, 213, 139,
87, 9, 235, 181, 54, 104, 138, 212, 149, 203, 41, 119, 244, 170, 72, 22,
233, 183, 85, 11, 136, 214, 52, 106, 43, 117, 151, 201, 74, 20, 246, 168,
116, 42, 200, 150, 21, 75, 169, 247, 182, 232, 10, 84, 215, 137, 107, 53
};
unsigned char Crc8(unsigned char *pcBlock, unsigned char len) {
unsigned char crc = 0x00;
while (len--) {
crc = pgm_read_byte(Crc8Table + (crc ^ *pcBlock++));
}
return crc;
}
#pragma once
unsigned char Crc8(unsigned char *pcBlock, unsigned char len);
#include & lt; stddef.h & gt;
#include & lt; avr/io.h & gt;
#include & lt; avr/interrupt.h & gt;
#include & lt; avr/sleep.h & gt;
#include & lt; avr/pgmspace.h & gt;
#include & lt; avr/eeprom.h & gt;
#include & lt; util/delay.h & gt;
#include " crc8.h "
#define EEPROM_DATA_LENGTH 128
//Offset Length Content Description
//0 4 DELL Manufacturer identifier
//4 4 00AC Adapter type
//8 3 045 Watts (45W)
//11 3 195 Tenths of a volt (19.5V)
//14 3 023 Tenths of amps (2.3A)
//17 23 CN0CDF577243865Q27F2A05 Serial number
//40 2 0x3D 0x94 CRC-16/ARC (LSB first)
//'3', 'C', 'N', '0', 'C', 'D', 'F', '5', '7', '7', '2', '4', '3', '8', '6', '5',
static const uint8_t ow_address[8] PROGMEM = { 0x09, 0x52, 0x8D, 0xED, 0x65, 0x00, 0x00, 0xEF };
static const uint8_t default_eeprom_data[EEPROM_DATA_LENGTH] PROGMEM = {
'D', 'E', 'L', 'L', '0', '0', 'A', 'C', '0', '6', '5', '1', '9', '5', '0', '3',
'3', 'C', 'N', '0', 'C', 'D', 'F', '5', '7', '7', '2', '4', '3', '8', '6', '5',
'Q', '2', '7', 'F', '2', 'A', '0', '5', '=', 0x94, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
enum {
OW_STATE_IDLE,
OW_STATE_RESET,
OW_STATE_PRESENCE,
OW_STATE_RX,
OW_STATE_TX
};
enum {
OW_DATA_SOURCE_RAM,
OW_DATA_SOURCE_ROM,
OW_DATA_SOURCE_EEPROM
};
static struct {
uint8_t state;
uint8_t bit_state;
uint8_t current_byte;
uint8_t current_bit;
uint8_t current_value;
union {
const uint8_t *tx_buffer;
uint8_t *rx_buffer;
};
uint8_t buffer_size;
uint8_t buffer_source;
void (*callback)(void);
uint8_t command;
uint8_t selected;
uint8_t pull_low_next;
uint8_t arg_buffer[8];
} ow = {
.state = OW_STATE_IDLE,
.bit_state = 1
};
#define OW_RELEASE() do { DDRB & = ~_BV(PB2); PORTB |= _BV(PB2); } while (0)
#define OW_PULL_LOW() do { PORTB & = ~_BV(PB2); DDRB |= _BV(PB2); } while (0)
static inline void ow_start_timer(void) {
TCNT0 = 0;
#if F_CPU == 1000000
TCCR0B = _BV(CS00);
#elif (F_CPU & gt; = 8000000) & & (F_CPU & lt; = 9000000)
TCCR0B = _BV(CS01);
#else
#error F_CPU should be 1 MHz or 8..9 MHz
#endif
}
static inline void ow_rx(uint8_t *buffer, uint8_t count, void (*callback)(void)) {
ow.state = OW_STATE_RX;
ow.rx_buffer = buffer;
ow.buffer_size = count;
ow.callback = callback;
ow.current_byte = 0;
ow.current_bit = 0;
ow.current_value = 0;
}
static inline void ow_fetch_current_byte_from_buffer() {
switch (ow.buffer_source) {
case OW_DATA_SOURCE_RAM:
ow.current_value = ow.tx_buffer[ow.current_byte];
break;
case OW_DATA_SOURCE_ROM:
ow.current_value = pgm_read_byte(ow.tx_buffer + ow.current_byte);
break;
case OW_DATA_SOURCE_EEPROM:
ow.current_value = eeprom_read_byte(ow.tx_buffer + ow.current_byte);
break;
}
}
static inline void ow_tx(const uint8_t *buffer, uint8_t count, uint8_t source, void (*callback)(void)) {
ow.state = OW_STATE_TX;
ow.tx_buffer = buffer;
ow.buffer_size = count;
ow.buffer_source = source;
ow.callback = callback;
ow.current_byte = 0;
ow.current_bit = 0;
ow_fetch_current_byte_from_buffer();
ow.pull_low_next = !(ow.current_value & 1);
}
static void ow_read_real_mem(void) {
uint16_t offset = (((uint16_t) ow.arg_buffer[1]) & lt; & lt; 8) | ow.arg_buffer[0];
uint8_t max_len = EEPROM_DATA_LENGTH - offset;
const uint8_t *base = (const uint8_t*) offset;
ow_tx(base, max_len, OW_DATA_SOURCE_EEPROM, NULL);
}
static void ow_read_mem(void) {
uint8_t tmp[] = { ow.command, ow.arg_buffer[0], ow.arg_buffer[1] };
ow.arg_buffer[2] = Crc8(tmp, sizeof(tmp));
ow_tx(ow.arg_buffer + 2, 1, OW_DATA_SOURCE_RAM, ow_read_real_mem);
}
static void ow_write_real_mem(void) {
uint16_t offset = (((uint16_t) ow.arg_buffer[1]) & lt; & lt; 8) | ow.arg_buffer[0];
uint8_t data = ow.arg_buffer[2];
uint8_t *base = (uint8_t*) offset;
eeprom_write_byte(base, data);
}
static void ow_write_mem(void) {
uint8_t tmp[] = { ow.command, ow.arg_buffer[0], ow.arg_buffer[1], ow.arg_buffer[2] };
ow.arg_buffer[3] = Crc8(tmp, sizeof(tmp));
ow_tx(ow.arg_buffer + 2, 1, OW_DATA_SOURCE_RAM, ow_write_real_mem);
}
static void ow_command_received(void) {
switch (ow.command) {
case 0x33: // READ ROM
ow_tx(ow_address, 8, OW_DATA_SOURCE_ROM, NULL);
break;
case 0xCC: // SKIP ROM
ow.selected = 1;
ow_rx( & (ow.command), sizeof(ow.command), ow_command_received);
break;
case 0xF0: // READ MEM
if (ow.selected) {
ow_rx(ow.arg_buffer, 2, ow_read_mem);
}
break;
case 0x0F: // WRITE MEM
if (ow.selected) {
ow_rx(ow.arg_buffer, 3, ow_write_mem);
}
}
}
static void ow_bit_change(uint8_t bit) {
switch (ow.state) {
case OW_STATE_RESET:
if (bit) {
ow.state = OW_STATE_PRESENCE;
OW_PULL_LOW();
OCR0A = 200;
}
break;
case OW_STATE_RX:
if (!bit) {
_delay_us(10);
uint8_t cur_bit = ow.current_bit;
if (PINB & _BV(PB2)) {
ow.current_value |= _BV(cur_bit);
}
cur_bit++;
if (cur_bit == 8) {
uint8_t cur_byte = ow.current_byte;
ow.rx_buffer[cur_byte++] = ow.current_value;
ow.current_value = 0;
cur_bit = 0;
if (cur_byte == ow.buffer_size) {
ow.state = OW_STATE_IDLE;
}
ow.current_byte = cur_byte;
}
ow.current_bit = cur_bit;
if (ow.state == OW_STATE_IDLE) {
if (ow.callback) {
ow.callback();
}
}
}
break;
case OW_STATE_TX:
if (!bit) {
_delay_us(30);
uint8_t cur_bit = ow.current_bit;
cur_bit++;
if (cur_bit == 8) {
uint8_t cur_byte = ow.current_byte;
cur_byte++;
cur_bit = 0;
if (cur_byte == ow.buffer_size) {
ow.state = OW_STATE_IDLE;
} else {
ow.current_byte = cur_byte;
ow_fetch_current_byte_from_buffer();
}
}
if (ow.state != OW_STATE_IDLE) {
ow.pull_low_next = !(ow.current_value & _BV(cur_bit));
}
ow.current_bit = cur_bit;
OW_RELEASE();
if (ow.state == OW_STATE_IDLE) {
if (ow.callback) {
ow.callback();
}
}
}
break;
}
if (!bit) {
ow_start_timer();
} else {
_delay_us(10);
}
}
ISR(INT0_vect) {
uint8_t bit = ow.bit_state;
do {
GIFR = _BV(INTF0);
bit = !bit;
if (!bit & & ow.pull_low_next) {
OW_PULL_LOW();
ow.pull_low_next = 0;
}
ow_bit_change(bit);
} while (GIFR & _BV(INTF0));
ow.bit_state = (PINB & _BV(PB2)) ? 1 : 0;
}
ISR(TIMER0_OVF_vect) {
TCCR0B = 0;
TIFR = _BV(OCF0A);
if (!ow.bit_state) {
ow.state = OW_STATE_RESET;
ow.selected = 0;
}
}
ISR(TIMER0_COMPA_vect) {
switch (ow.state) {
case OW_STATE_PRESENCE:
OW_RELEASE();
ow_rx( & ow.command, sizeof(ow.command), ow_command_received);
break;
}
}
int main(void) {
// Disable analog comparator.
ACSR |= _BV(ACD);
// Disable ADC, TIM1 and USI.
PRR |= _BV(PRADC) | _BV(PRUSI) | _BV(PRTIM1);
// All pins: Input no pullup, except PB2.
DDRB = 0;
PORTB = 0;
PORTB |= (1 & lt; & lt; PB2);
// Check EEPROM and load default values if needed
OW_PULL_LOW();
if (eeprom_read_byte((const uint8_t*) 0) == 0xFF) {
//if (1) {
for (uint8_t i = 0; i & lt; EEPROM_DATA_LENGTH; i++) {
eeprom_write_byte(((uint8_t*) 0) + i, pgm_read_byte(default_eeprom_data + i));
}
} else {
_delay_us(10);
}
OW_RELEASE();
// TIM0: overflow and compare A interrupts.
TIMSK |= _BV(TOIE0) | _BV(OCIE0A);
OCR0A = 0;
// INT0: Any change.
MCUCR = (MCUCR | _BV(ISC00)) & ~_BV(ISC01);
GIMSK |= _BV(INT0);
// Read current line state.
GIFR = _BV(INTF0);
ow.bit_state = (PINB & _BV(PB2)) ? 1 : 0;
// Enable interrupts.
sei();
// Main loop.
set_sleep_mode(SLEEP_MODE_IDLE);
while (1) {
sleep_bod_disable();
sleep_mode();
}
}