Blame view

software/common_class/mcp3551.py 5.03 KB
14b996da3   benny   First commit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
  # -*- coding: utf-8 -*-
  
  """
  author  Benoit Dubois
  licence GPL v3+
  brief   STM32 driver for MCP3551 ADC device
  """
  import pyb
  
  
  class Mcp3551(object):
      """Handle communication with ADC MCP3551 through SPI bus on STM32F4.
      Note: baudrate_max =  MHz
      """
  
      NUM_BITS = 22
      MAX_VALUE = (1 << (NUM_BITS-1)) - 1
      MIN_VALUE = -(1 << (NUM_BITS-1))
  
      def __init__(self, bus, baudrate, rdy_b_pin, cs_pin, v_ref):
          """
          :param bus: SPI bus number (int)
          :param baudrate: SPI bus (data) baudrate (int)
          :param rdy_b_pin: Pin in use for rdy_b (SDO/MISO) signal (str)
          :param cs_pin: Pin in use for cs signal (str)
          :param v_ref: Reference voltage value of ADC (float)
          """
          self.baudrate = baudrate
          self.v_ref = v_ref
          self.rdy_b = pyb.Pin(rdy_b_pin, pyb.Pin.IN)
          self.cs_b = pyb.Pin(cs_pin, pyb.Pin.OUT_PP)
          self.spi = pyb.SPI(bus)
          self.cs_b.high()
          self.cs_b.low()
  
      def single_read(self):
          """Read one value then stop acquisition.
          """
          self.cs_b.low()
          data = self.read()
          self.cs_b.high()
          return data
  
       def _bin2volt(self, value):
          """Note: does not check sign of data, so negative value are
          affected of a small error due to normalisation with MAX_VALUE
          instead of MIN_VALUE.
          """
          return value * self.v_ref / self.MAX_VALUE
  
      def volt(self):
          """Note: does not check sign of data, so negative value are
          affected of a small error due to normalisation with MAX_VALUE
          instead of MIN_VALUE.
          """
          return self._bin2volt(self.single_read(), self.v_ref, self.MAX_VALUE)
  
      def continous_mode(self, value=True):
          """Set/Deset continous acquisition mode.
          """
          if value is False:
              self.cs_b.high()
          else:
              self.cs_b.low()
  
      def read(self):
          """Note1: The ADC allows operation over [-Vref; +Vref] range.
          This capability is not implemented.
          Note2: If ADC does not respond (i.e. /RDY does not falling), return 0.
          """
          wd = 0
          buf = bytearray(3)
          while self.rdy_b.value() == 1:
              if wd > 100: # Watchdog on MCP response
                  return 0
              else:
                  wd += 1
                  pyb.delay(1)
          self.spi.init(pyb.SPI.MASTER, baudrate=self.baudrate,
                        polarity=1, phase=1)
          self.spi.recv(buf, timeout=500)
          self.spi.deinit()
          if buf[0] & 256 == 1: # Overflow high
              return self.MAX_VALUE
          if buf[0] & 128 == 1: # Overflow low
              return self.MIN_VALUE
          data = (buf[0] << 16) + (buf[1] << 8) + buf[2]
          return self.twos_comp(data, self.NUM_BITS)
  
      @staticmethod
      def twos_comp(val, bits):
          """compute the 2's complement of int value val of lenght bits.
          """
          if (val & (1 << (bits - 1))) != 0: # if sign bit is set
              val = val - (1 << bits)        # compute negative value
          return val                         # return positive value as is
  
  
  class Mcp3551Temp(Mcp3551):
      """Handle "slow_adc_shield" of Cyrus Rocher ie a board including
      conditionning of a RTD and the A to D conversion with a MCP3551 device.
      """
  
      def __init__(self, bus, baudrate, rdy_b_pin, cs_pin, v_ref=2.5,
                   r_0=100, alpha=0.00385, r_ref=2.5e3):
          """
          :param r_0: resistance of RTD @ 0°C (int)
          :param alpha: 1st order sensitivity of RTD (ohm/ohm/°C) (float)
          :param r_ref: resistance defining current through RTD (int)
          """
          super().__init__(bus, baudrate, rdy_b_pin, cs_pin, v_ref)
          self.r_0 = r_0
          self.r_ref = r_ref
          self.k = 1 / alpha / r_0
  
      @staticmethod
      def bin2resistance(bin_value, r_ref, bin_max):
          """Convert binary value to resistance value.
          """
          return bin_value * r_ref / bin_max
  
      @staticmethod
      def bin2temperature(bin_value, k, r_0, r_ref, bin_max):
          """Convert binary value to temperature value.
          """
          return k * (bin_value * r_ref / bin_max - r_0)
  
      def resistance(self):
          """Convert binary value to resistance value.
          """
          return self.bin2resistance(self.single_read(),
                                     self.r_ref,
                                     self.MAX_VALUE)
  
      def temperature(self):
          """Convert binary value to temperature value.
          """
          return self.bin2temperature(self.single_read(),
                                      self.k, self.r_0,
                                      self.r_ref, self.MAX_VALUE)
  
  
  if __name__ == '__main__':
      ADC_SPI_CH = 1
      ADC_V_REF = 2.5
      ADC_SPI_BAUDRATE = 1000000
      ADC_PIN_CS = 'PA4'
      ADC_PIN_RDY_B = 'PA6'
  
      ADC = Mcp3551(bus=ADC_SPI_CH, baudrate=ADC_SPI_BAUDRATE,
                    rdy_b_pin=ADC_PIN_RDY_B, cs_pin=ADC_PIN_CS,
                    v_ref=ADC_V_REF)
      ADC.continous_mode(True)
  
      ACQ_NB = 0
  
      while True:
          pyb.delay(10) # in ms
          print(ACQ_NB, ADC.read())
          ACQ_NB += 1