python模拟SPI

来源:互联网 发布:js slibings 编辑:程序博客网 时间:2024/06/08 13:12

SPI是串行外设接口(Serial Peripheral Interface的缩写I,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,如今越来越多的芯片集成了这种通信协议。

下面给个用python写的软件模拟SPI通信程序。程序如下:

import operator

import time

 

MSBFIRST = 0

LSBFIRST = 1

 

class BitBang(object):

    """GPIO口上用软件模拟实现SPI通信协议"""

 

    def __init__(self, gpio, sclk, mosi=None, miso=None, ss=None):

        """初始化基于软件的SPI。需要提供一个关于GPIO的基类,一个SPI时钟以及可以选择的MOSIMISOSS(片选)。"""

        self._gpio = gpio

        self._sclk = sclk

        self._mosi = mosi

        self._miso = miso

        self._ss = ss

        # Set pins as outputs/inputs.

        gpio.setup(sclk, GPIO.OUT)

        if mosi is not None:

            gpio.setup(mosi, GPIO.OUT)

        if miso is not None:

            gpio.setup(miso, GPIO.IN)

        if ss is not None:

            gpio.setup(ss, GPIO.OUT)

            # Assert SS high to start with device communication off.

            gpio.set_high(ss)

        # Assume mode 0.

        self.set_mode(0)

        # Assume most significant bit first order.

        self.set_bit_order(MSBFIRST)

#SPI通信中的四种模式,一般情况下使用模式0(时钟线先拉低,当时钟线上升沿到来时,输入输出数据)

    def set_mode(self, mode):

        """Set SPI mode which controls clock polarity and phase.  Should be a

        numeric value 0, 1, 2, or 3.  See wikipedia page for details on meaning:

        http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus

        """

        if mode < 0 or mode > 3:

            raise ValueError('Mode must be a value 0, 1, 2, or 3.')

        if mode & 0x02:

            # Clock is normally high in mode 2 and 3.

            self._clock_base = GPIO.HIGH

        else:

            # Clock is normally low in mode 0 and 1.

            self._clock_base = GPIO.LOW

        if mode & 0x01:

            # Read on trailing edge in mode 1 and 3.

            self._read_leading = False

        else:

            # Read on leading edge in mode 0 and 2.

            self._read_leading = True

        # Put clock into its base state.

        self._gpio.output(self._sclk, self._clock_base)

 

    def set_bit_order(self, order):

        """Set order of bits to be read/written over serial lines.  Should be

        either MSBFIRST for most-significant first, or LSBFIRST for

        least-signifcant first.

        """

        # Set self._mask to the bitmask which points at the appropriate bit to

        # read or write, and appropriate left/right shift operator function for

        # reading/writing.

        if order == MSBFIRST:

            self._mask = 0x80

            self._write_shift = operator.lshift

            self._read_shift = operator.rshift

        elif order == LSBFIRST:

            self._mask = 0x01

            self._write_shift = operator.rshift

            self._read_shift = operator.lshift

        else:

            raise ValueError('Order must be MSBFIRST or LSBFIRST.')

 

    def close(self):

        """Close the SPI connection.  Unused in the bit bang implementation."""

        pass

#半双工写,主机只往从机写数据,不接收数据

    def write(self, data, assert_ss=True, deassert_ss=True):

        """Half-duplex SPI write.  If assert_ss is True, the SS line will be

        asserted low, the specified bytes will be clocked out the MOSI line, and

        if deassert_ss is True the SS line be put back high.

        """

        # Fail MOSI is not specified.

        if self._mosi is None:

            raise RuntimeError('Write attempted with no MOSI pin specified.')

        if assert_ss and self._ss is not None:

            self._gpio.set_low(self._ss)

        for byte in data:

            for i in range(8):

                # Write bit to MOSI.

                if self._write_shift(byte, i) & self._mask:

                    self._gpio.set_high(self._mosi)

                else:

                    self._gpio.set_low(self._mosi)

                # Flip clock off base.

                self._gpio.output(self._sclk, not self._clock_base)

                # Return clock to base.

                self._gpio.output(self._sclk, self._clock_base)

        if deassert_ss and self._ss is not None:

            self._gpio.set_high(self._ss)

 

    def read(self, length, assert_ss=True, deassert_ss=True):

        """Half-duplex SPI read.  If assert_ss is true, the SS line will be

        asserted low, the specified length of bytes will be clocked in the MISO

        line, and if deassert_ss is true the SS line will be put back high.

        Bytes which are read will be returned as a bytearray object.

        """

        if self._miso is None:

            raise RuntimeError('Read attempted with no MISO pin specified.')

        if assert_ss and self._ss is not None:

            self._gpio.set_low(self._ss)

        result = bytearray(length)

        for i in range(length):

            for j in range(8):

                # Flip clock off base.

                self._gpio.output(self._sclk, not self._clock_base)

                # Handle read on leading edge of clock.

                if self._read_leading:

                    if self._gpio.is_high(self._miso):

                        # Set bit to 1 at appropriate location.

                        result[i] |= self._read_shift(self._mask, j)

                    else:

                        # Set bit to 0 at appropriate location.

                        result[i] &= ~self._read_shift(self._mask, j)

                # Return clock to base.

                self._gpio.output(self._sclk, self._clock_base)

                # Handle read on trailing edge of clock.

                if not self._read_leading:

                    if self._gpio.is_high(self._miso):

                        # Set bit to 1 at appropriate location.

                        result[i] |= self._read_shift(self._mask, j)

                    else:

                        # Set bit to 0 at appropriate location.

                        result[i] &= ~self._read_shift(self._mask, j)

        if deassert_ss and self._ss is not None:

            self._gpio.set_high(self._ss)

        return result

#全双工读写数据

    def transfer(self, data, assert_ss=True, deassert_ss=True):

        """Full-duplex SPI read and write.  If assert_ss is true, the SS line

        will be asserted low, the specified bytes will be clocked out the MOSI

        line while bytes will also be read from the MISO line, and if

        deassert_ss is true the SS line will be put back high.  Bytes which are

        read will be returned as a bytearray object.

        """

        if self._mosi is None:

            raise RuntimeError('Write attempted with no MOSI pin specified.')

        if self._mosi is None:

            raise RuntimeError('Read attempted with no MISO pin specified.')

        if assert_ss and self._ss is not None:

            self._gpio.set_low(self._ss)

        result = bytearray(len(data))

        for i in range(len(data)):

            for j in range(8):

                # Write bit to MOSI.

                if self._write_shift(data[i], j) & self._mask:

                    self._gpio.set_high(self._mosi)

                else:

                    self._gpio.set_low(self._mosi)

                # Flip clock off base.

                self._gpio.output(self._sclk, not self._clock_base)

                # Handle read on leading edge of clock.

                if self._read_leading:

                    if self._gpio.is_high(self._miso):

                        # Set bit to 1 at appropriate location.

                        result[i] |= self._read_shift(self._mask, j)

                    else:

                        # Set bit to 0 at appropriate location.

                        result[i] &= ~self._read_shift(self._mask, j)

                # Return clock to base.

                self._gpio.output(self._sclk, self._clock_base)

                # Handle read on trailing edge of clock.

                if not self._read_leading:

                    if self._gpio.is_high(self._miso):

                        # Set bit to 1 at appropriate location.

                        result[i] |= self._read_shift(self._mask, j)

                    else:

                        # Set bit to 0 at appropriate location.

                        result[i] &= ~self._read_shift(self._mask, j)

        if deassert_ss and self._ss is not None:

            self._gpio.set_high(self._ss)

        return result

0 0