ソースを参照

Fixed i2c communication with external mcu
* Adjusted timing (config.h / adc different shunt timings)
* i2c target fixed protocol

Heinrich Blatt 7 ヶ月 前
コミット
1c24f24635
5 ファイル変更83 行追加21 行削除
  1. 4 3
      src/config.h
  2. 14 15
      src/interfaces/i2c_target.c
  3. 1 2
      src/interfaces/i2c_target.h
  4. 5 1
      src/peripherals/adc/adc.c
  5. 59 0
      test-mcu.py

+ 4 - 3
src/config.h

@@ -19,7 +19,7 @@
 // production e.g. 320000 (10ms) (Validate that this is really the case!)
 // should be large for debugging, (e.g. )
 // small for production
-#define MAINLOOP_DELAY (3200000)
+#define MAINLOOP_DELAY (3200)
 
 // i2c address for acting as target
 // (based on the GPIO 1 integer is added)
@@ -47,7 +47,7 @@
 #define DEBUG_CTRL 1
 
 // printf trace: put also the transition messages
-//#define DEBUG_TRACE_CTRL 1
+#define DEBUG_TRACE_CTRL 1
 
 // printf i2c errors
 #define DEBUG_I2C_ERR 1
@@ -88,7 +88,8 @@
 // ensure that the measurement is ready. (set the define to 0 if it should be one-shot)
 // (it could be the case that the channel is switched and wrong data is fetched)
 #define ADC_MEASUREMENT_IS_CONTINUOUS 1
-#define ADC_CONTINUOUS_DELAY_CYCLES (320000) // 32000 is 1ms (32MHz clock)
+#define ADC_CONTINUOUS_DELAY_CYCLES_VOLTAGES (32000*10) // 32000 is 1ms (32MHz clock)
+#define ADC_CONTINUOUS_DELAY_CYCLES_SHUNT (32000*75)
 
 // Packet buffer sizes for RX and TX for the
 // controller mode only

+ 14 - 15
src/interfaces/i2c_target.c

@@ -33,9 +33,12 @@ void initialize_target_address() {
 // need to select the right one, passing a pointer as an argument
 
 void mcu_i2c_handle(I2C_Regs *i2c) {
-    uint8_t receivedCommand = DL_I2C_receiveTargetData(i2c);
+    uint8_t receivedByte = DL_I2C_receiveTargetData(i2c);
+    uint8_t receivedCommand = (receivedByte & 0x0F);
+    uint8_t slot = (receivedByte & 0xF0);
+
 #ifdef DEBUG_TARGET
-    printf("[SLAVE] Received Command: 0x%02X\n", receivedCommand);
+    printf("[SLAVE] Received Byte: 0x%02X\n", receivedByte);
 #endif
     uint8_t tx_buffer[8] = {0};
     // changed to volatile variable, so that the compiler cannot optimize the
@@ -43,8 +46,7 @@ void mcu_i2c_handle(I2C_Regs *i2c) {
     volatile uint8_t rx_buffer[8] = {0};
     /*Handling GET commands with bitmasking*/
     // GET command for ADC(Battery Measurement): Voltage, Current, Temperature
-    if ((receivedCommand & 0x0F) == CMD_GET_MEASUREMENT) {
-        uint8_t slot = receivedCommand & 0xF0;
+    if (receivedCommand == CMD_GET_MEASUREMENT) {
         if (slot > NUM_SLOTS) {
             DL_I2C_flushTargetTXFIFO(i2c);
             return;
@@ -61,7 +63,7 @@ void mcu_i2c_handle(I2C_Regs *i2c) {
     } else if (receivedCommand == CMD_SET_CURRENT) {
         // Read incoming bytes from the Controller:
         uint8_t rx_index = 0;
-        while (rx_index < 4) {
+        while (rx_index < 2) {
             // TODO: Need to have a workaround, currently the code is getting stuck on
             // the first trigger and provides result on the second trigger
             if (!DL_I2C_isTargetRXFIFOEmpty(i2c)) {
@@ -69,23 +71,21 @@ void mcu_i2c_handle(I2C_Regs *i2c) {
               rx_index++;
             }
         }
-        // we expect 4 bytes:
-        // 1. command
-        // 2. slot_id
-        // 3 + 4. current (int16)
-        if (rx_index != 4) {
+        // we expect 3 bytes:
+        // 1. <slot_id><command>
+        // 2 + 3. current (int16)
+        if (rx_index != 2) {
 #ifdef DEBUG_TARGET
-            printf("ERROR: Incomplete I2C Rx: received %d%zu bytes\n", rx_index, 4);
+            printf("ERROR: Incomplete I2C Rx: received %d bytes\n", rx_index);
 #endif
             DL_I2C_flushTargetRXFIFO(i2c);
             rx_index = 0;
             return;
         }
-        uint8_t slot = rx_buffer[1]; // first byte is the slot id (0..3)
-        battery_slots[slot].set_current = *((int16_t*)(&rx_buffer[2])); // byte 3+4 is the current
+        battery_slots[slot].set_current = *((int16_t*)(&rx_buffer[0])); // byte 2+3 is the current
         
 #ifdef DEBUG_TARGET
-        printf("Slot id: %d, Current: %" SCNd16 "\n", slot, battery_slots[slot].set_current);
+        printf("Slot id: %d, Current: %" SCNd16 " (Bytes 0x%02X 0x%02X)\n", slot, battery_slots[slot].set_current, rx_buffer[0], rx_buffer[1]);
 #endif
         /*
         // This code is for debugging:
@@ -104,7 +104,6 @@ void mcu_i2c_handle(I2C_Regs *i2c) {
 #endif
         } */
     } else if (receivedCommand == CMD_CELAR_ERR) {
-        uint8_t slot = receivedCommand & 0xF0;
         if (slot > NUM_SLOTS) {
             DL_I2C_flushTargetTXFIFO(i2c);
             return;

+ 1 - 2
src/interfaces/i2c_target.h

@@ -10,8 +10,7 @@
 typedef enum{
     CMD_SET_CURRENT= 0x05,
     CMD_GET_MEASUREMENT= 0x06, 
-    CMD_GET_BATTERY_STATE= 0x07, 
-    CMD_CELAR_ERR= 0x08
+    CMD_CELAR_ERR= 0x07
 }mcu_I2C_command;
 
 void initialize_target_address();

+ 5 - 1
src/peripherals/adc/adc.c

@@ -42,7 +42,11 @@ uint16_t read_adc_channel(uint8_t slot, uint8_t channel) {
             } else {
                 // in continuous mode we can directly read
                 adc_state = ADC_STATE_READ;
-                delay_cycles(ADC_CONTINUOUS_DELAY_CYCLES);
+                if (adc_params.resolution == 16) {
+                    delay_cycles(ADC_CONTINUOUS_DELAY_CYCLES_SHUNT);
+                } else {
+                    delay_cycles(ADC_CONTINUOUS_DELAY_CYCLES_VOLTAGES);
+                }
             }
             break;
 

+ 59 - 0
test-mcu.py

@@ -0,0 +1,59 @@
+from smbus2 import SMBus, i2c_msg
+import struct
+import time
+
+# Beispiel: I2C-Adresse des Geräts
+DEVICE_ADDRESS = 0x48  # z.B. EEPROM oder Sensoradresse
+I2C_BUS = 1            # I2C-1 ist meist Standard beim Raspberry Pi
+
+slot = input("Slot id (default: 0): ")
+if slot == "": slot = 0
+else: slot = int(slot)
+
+def read_status(bus):
+    data_to_write = [(0x06 | slot << 4)]
+
+    write = i2c_msg.write(DEVICE_ADDRESS, data_to_write)
+    bus.i2c_rdwr(write)
+
+    # Lesen von 3 Bytes roher Binärdaten
+    read = i2c_msg.read(DEVICE_ADDRESS, 8)
+    bus.i2c_rdwr(read)
+    data_read = bytes(read)
+    print("data:", data_read.hex())
+
+    data = struct.unpack("<HhHH", data_read)
+
+    # data[0] is voltage in mV
+    print(f"Spannung: {data[0]/1000:.2f}V, Strom: {data[1]}mA, Temperatur: {data[2]}°C, Error: 0x{data[3]:02X}")
+
+with SMBus(I2C_BUS) as bus:
+    # Schreiben von Rohdaten
+
+    while True:
+        
+        print("Mögliche Aktionen:")
+        print("  r <n>     n Zyklen lesen")
+        print("  w <i>     i mA Strom positiv: laden (DAC), negativ entladen: (PWM)")
+        act = input("nächste Aktion: ")
+        if act[0] == "r":
+            n = int(act.split(" ")[1])
+            for i in range(n):
+                time.sleep(1)
+                try:
+                    read_status(bus)
+                except:
+                    print("Bus error.")
+        elif act[0] == "w":
+            i = int(act.split(" ")[1])
+            # 5 is the command for setting the current
+            byte_str = struct.pack("<Bh", (5 + (slot << 4)), int(i))
+            print("Sending bytes", byte_str.hex())
+
+            write = i2c_msg.write(DEVICE_ADDRESS, list(byte_str))
+            bus.i2c_rdwr(write)
+
+
+        else:
+            "Ungültiges Kommando."
+