родитель
							
								
									cfae2d4f9a
								
							
						
					
					
						коммит
						4f7b23c2b7
					
				
					 6 изменённых файлов: 522 добавлений и 9 удалений
				
			
		|  | @ -9,6 +9,15 @@ const ( | ||||||
| 	xoscFreq = 12 // MHz | 	xoscFreq = 12 // MHz | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | // I2C Pins. | ||||||
|  | const ( | ||||||
|  | 	I2C0_SDA_PIN = GPIO24 | ||||||
|  | 	I2C0_SCL_PIN = GPIO25 | ||||||
|  | 
 | ||||||
|  | 	I2C1_SDA_PIN = GPIO2 | ||||||
|  | 	I2C1_SCL_PIN = GPIO3 | ||||||
|  | ) | ||||||
|  | 
 | ||||||
| // SPI default pins | // SPI default pins | ||||||
| const ( | const ( | ||||||
| 	// Default Serial Clock Bus 0 for SPI communications | 	// Default Serial Clock Bus 0 for SPI communications | ||||||
|  |  | ||||||
|  | @ -49,8 +49,11 @@ const ( | ||||||
| 
 | 
 | ||||||
| // I2C pins | // I2C pins | ||||||
| const ( | const ( | ||||||
| 	SDA_PIN Pin = GPIO12 | 	I2C0_SDA_PIN Pin = GPIO12 | ||||||
| 	SCL_PIN Pin = GPIO13 | 	I2C0_SCL_PIN Pin = GPIO13 | ||||||
|  | 
 | ||||||
|  | 	I2C1_SDA_PIN Pin = GPIO18 | ||||||
|  | 	I2C1_SCL_PIN Pin = GPIO19 | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // SPI pins. SPI1 not available on Nano RP2040 Connect. | // SPI pins. SPI1 not available on Nano RP2040 Connect. | ||||||
|  |  | ||||||
|  | @ -38,6 +38,15 @@ const ( | ||||||
| 	xoscFreq = 12 // MHz | 	xoscFreq = 12 // MHz | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | // I2C Default pins on Raspberry Pico. | ||||||
|  | const ( | ||||||
|  | 	I2C0_SDA_PIN = GP4 | ||||||
|  | 	I2C0_SCL_PIN = GP5 | ||||||
|  | 
 | ||||||
|  | 	I2C1_SDA_PIN = GP2 | ||||||
|  | 	I2C1_SCL_PIN = GP3 | ||||||
|  | ) | ||||||
|  | 
 | ||||||
| // SPI default pins | // SPI default pins | ||||||
| const ( | const ( | ||||||
| 	// Default Serial Clock Bus 0 for SPI communications | 	// Default Serial Clock Bus 0 for SPI communications | ||||||
|  |  | ||||||
|  | @ -1,4 +1,4 @@ | ||||||
| // +build atmega nrf sam stm32 fe310 k210 | // +build atmega nrf sam stm32 fe310 k210 rp2040 | ||||||
| 
 | 
 | ||||||
| package machine | package machine | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -47,14 +47,27 @@ type pinFunc uint8 | ||||||
| // GPIO function selectors | // GPIO function selectors | ||||||
| const ( | const ( | ||||||
| 	fnJTAG pinFunc = 0 | 	fnJTAG pinFunc = 0 | ||||||
| 	fnSPI  pinFunc = 1 | 	fnSPI  pinFunc = 1 // Connect one of the internal PL022 SPI peripherals to GPIO | ||||||
| 	fnUART pinFunc = 2 | 	fnUART pinFunc = 2 | ||||||
| 	fnI2C  pinFunc = 3 | 	fnI2C  pinFunc = 3 | ||||||
| 	fnPWM  pinFunc = 4 | 	// Connect a PWM slice to GPIO. There are eight PWM slices, | ||||||
| 	fnSIO  pinFunc = 5 | 	// each with two outputchannels (A/B). The B pin can also be used as an input, | ||||||
| 	fnPIO0 pinFunc = 6 | 	// for frequency and duty cyclemeasurement | ||||||
| 	fnPIO1 pinFunc = 7 | 	fnPWM pinFunc = 4 | ||||||
|  | 	// Software control of GPIO, from the single-cycle IO (SIO) block. | ||||||
|  | 	// The SIO function (F5)must be selected for the processors to drive a GPIO, | ||||||
|  | 	// but the input is always connected,so software can check the state of GPIOs at any time. | ||||||
|  | 	fnSIO pinFunc = 5 | ||||||
|  | 	// Connect one of the programmable IO blocks (PIO) to GPIO. PIO can implement a widevariety of interfaces, | ||||||
|  | 	// and has its own internal pin mapping hardware, allowing flexibleplacement of digital interfaces on bank 0 GPIOs. | ||||||
|  | 	// The PIO function (F6, F7) must beselected for PIO to drive a GPIO, but the input is always connected, | ||||||
|  | 	// so the PIOs canalways see the state of all pins. | ||||||
|  | 	fnPIO0, fnPIO1 pinFunc = 6, 7 | ||||||
|  | 	// General purpose clock inputs/outputs. Can be routed to a number of internal clock domains onRP2040, | ||||||
|  | 	// e.g. Input: to provide a 1 Hz clock for the RTC, or can be connected to an internalfrequency counter. | ||||||
|  | 	// e.g. Output: optional integer divide | ||||||
| 	fnGPCK pinFunc = 8 | 	fnGPCK pinFunc = 8 | ||||||
|  | 	// USB power control signals to/from the internal USB controller | ||||||
| 	fnUSB  pinFunc = 9 | 	fnUSB  pinFunc = 9 | ||||||
| 	fnNULL pinFunc = 0x1f | 	fnNULL pinFunc = 0x1f | ||||||
| 
 | 
 | ||||||
|  | @ -68,6 +81,7 @@ const ( | ||||||
| 	PinInputPullup | 	PinInputPullup | ||||||
| 	PinAnalog | 	PinAnalog | ||||||
| 	PinUART | 	PinUART | ||||||
|  | 	PinI2C | ||||||
| 	PinSPI | 	PinSPI | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | @ -91,7 +105,7 @@ func (p Pin) xor() { | ||||||
| 
 | 
 | ||||||
| // get returns the pin value | // get returns the pin value | ||||||
| func (p Pin) get() bool { | func (p Pin) get() bool { | ||||||
| 	return rp.SIO.GPIO_IN.HasBits(uint32(1) << p) | 	return rp.SIO.GPIO_IN.HasBits(1 << p) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (p Pin) ioCtrl() *volatile.Register32 { | func (p Pin) ioCtrl() *volatile.Register32 { | ||||||
|  | @ -117,6 +131,17 @@ func (p Pin) pulloff() { | ||||||
| 	p.padCtrl().ClearBits(rp.PADS_BANK0_GPIO0_PUE) | 	p.padCtrl().ClearBits(rp.PADS_BANK0_GPIO0_PUE) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // setSlew sets pad slew rate control. | ||||||
|  | // true sets to fast. false sets to slow. | ||||||
|  | func (p Pin) setSlew(sr bool) { | ||||||
|  | 	p.padCtrl().ReplaceBits(boolToBit(sr)<<rp.PADS_BANK0_GPIO0_SLEWFAST_Pos, rp.PADS_BANK0_GPIO0_SLEWFAST_Msk, 0) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // setSchmitt enables or disables Schmitt trigger. | ||||||
|  | func (p Pin) setSchmitt(trigger bool) { | ||||||
|  | 	p.padCtrl().ReplaceBits(boolToBit(trigger)<<rp.PADS_BANK0_GPIO0_SCHMITT_Pos, rp.PADS_BANK0_GPIO0_SCHMITT_Msk, 0) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // setFunc will set pin function to fn. | // setFunc will set pin function to fn. | ||||||
| func (p Pin) setFunc(fn pinFunc) { | func (p Pin) setFunc(fn pinFunc) { | ||||||
| 	// Set input enable, Clear output disable | 	// Set input enable, Clear output disable | ||||||
|  | @ -156,6 +181,12 @@ func (p Pin) Configure(config PinConfig) { | ||||||
| 		p.pulloff() | 		p.pulloff() | ||||||
| 	case PinUART: | 	case PinUART: | ||||||
| 		p.setFunc(fnUART) | 		p.setFunc(fnUART) | ||||||
|  | 	case PinI2C: | ||||||
|  | 		// IO config according to 4.3.1.3 of rp2040 datasheet. | ||||||
|  | 		p.setFunc(fnI2C) | ||||||
|  | 		p.pullup() | ||||||
|  | 		p.setSchmitt(true) | ||||||
|  | 		p.setSlew(false) | ||||||
| 	case PinSPI: | 	case PinSPI: | ||||||
| 		p.setFunc(fnSPI) | 		p.setFunc(fnSPI) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
							
								
								
									
										461
									
								
								src/machine/machine_rp2040_i2c.go
									
										
									
									
									
										Обычный файл
									
								
							
							
						
						
									
										461
									
								
								src/machine/machine_rp2040_i2c.go
									
										
									
									
									
										Обычный файл
									
								
							|  | @ -0,0 +1,461 @@ | ||||||
|  | // +build rp2040 | ||||||
|  | 
 | ||||||
|  | package machine | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"device/rp" | ||||||
|  | 	"errors" | ||||||
|  | 	"strconv" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // I2C on the RP2040. | ||||||
|  | var ( | ||||||
|  | 	I2C0  = &_I2C0 | ||||||
|  | 	_I2C0 = I2C{ | ||||||
|  | 		Bus: rp.I2C0, | ||||||
|  | 	} | ||||||
|  | 	I2C1  = &_I2C1 | ||||||
|  | 	_I2C1 = I2C{ | ||||||
|  | 		Bus: rp.I2C1, | ||||||
|  | 	} | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // Features: Taken from datasheet. | ||||||
|  | // Default master mode, with slave mode available (not simulataneously). | ||||||
|  | // Default slave address of RP2040: 0x055 | ||||||
|  | // Supports 10-bit addressing in Master mode | ||||||
|  | // 16-element transmit buffer | ||||||
|  | // 16-element receive buffer | ||||||
|  | // Can be driven from DMA | ||||||
|  | // Can generate interrupts | ||||||
|  | // Fast mode plus max transfer speed (1000kb/s) | ||||||
|  | 
 | ||||||
|  | // GPIO config | ||||||
|  | // Each controller must connect its clock SCL and data SDA to one pair of GPIOs. | ||||||
|  | // The I2C standard requires that drivers drivea signal low, or when not driven the signal will be pulled high. | ||||||
|  | // This applies to SCL and SDA. The GPIO pads should beconfigured for: | ||||||
|  | //  Pull-up enabled | ||||||
|  | //  Slew rate limited | ||||||
|  | //  Schmitt trigger enabled | ||||||
|  | // Note: There should also be external pull-ups on the board as the internal pad pull-ups may not be strong enough to pull upexternal circuits. | ||||||
|  | 
 | ||||||
|  | // I2CConfig is used to store config info for I2C. | ||||||
|  | type I2CConfig struct { | ||||||
|  | 	Frequency uint32 | ||||||
|  | 	// SDA/SCL Serial Data and clock pins. Refer to datasheet to see | ||||||
|  | 	// which pins match the desired bus. | ||||||
|  | 	SDA, SCL Pin | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type I2C struct { | ||||||
|  | 	Bus           *rp.I2C0_Type | ||||||
|  | 	restartOnNext bool | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | var ( | ||||||
|  | 	ErrInvalidI2CBaudrate = errors.New("invalid i2c baudrate") | ||||||
|  | 	ErrInvalidTgtAddr     = errors.New("invalid target i2c address not in 0..0x80 or is reserved") | ||||||
|  | 	ErrI2CGeneric         = errors.New("i2c error") | ||||||
|  | 	ErrRP2040I2CDisable   = errors.New("i2c rp2040 peripheral timeout in disable") | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // Tx performs a write and then a read transfer placing the result in | ||||||
|  | // in r. | ||||||
|  | // | ||||||
|  | // Passing a nil value for w or r skips the transfer corresponding to write | ||||||
|  | // or read, respectively. | ||||||
|  | // | ||||||
|  | //  i2c.Tx(addr, nil, r) | ||||||
|  | // Performs only a read transfer. | ||||||
|  | // | ||||||
|  | //  i2c.Tx(addr, w, nil) | ||||||
|  | // Performs only a write transfer. | ||||||
|  | func (i2c *I2C) Tx(addr uint16, w, r []byte) error { | ||||||
|  | 	// timeout in microseconds. | ||||||
|  | 	const timeout = 40 * 1000 // 40ms is a reasonable time for a real-time system. | ||||||
|  | 	if len(w) > 0 { | ||||||
|  | 		if err := i2c.tx(uint8(addr), w, false, timeout); nil != err { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if len(r) > 0 { | ||||||
|  | 		if err := i2c.rx(uint8(addr), r, false, timeout); nil != err { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Configure initializes i2c peripheral and configures I2C config's pins passed. | ||||||
|  | // Here's a list of valid SDA and SCL GPIO pins on bus I2C0 of the rp2040: | ||||||
|  | //  SDA: 0, 4, 8, 12, 16, 20 | ||||||
|  | //  SCL: 1, 5, 9, 13, 17, 21 | ||||||
|  | // Same as above for I2C1 bus: | ||||||
|  | //  SDA: 2, 6, 10, 14, 18, 26 | ||||||
|  | //  SCL: 3, 7, 11, 15, 19, 27 | ||||||
|  | func (i2c *I2C) Configure(config I2CConfig) error { | ||||||
|  | 	if config.SCL == 0 { | ||||||
|  | 		// If config pins are zero valued or clock pin is invalid then we set default values. | ||||||
|  | 		switch i2c.Bus { | ||||||
|  | 		case rp.I2C0: | ||||||
|  | 			config.SCL = I2C0_SCL_PIN | ||||||
|  | 			config.SDA = I2C0_SDA_PIN | ||||||
|  | 		case rp.I2C1: | ||||||
|  | 			config.SCL = I2C1_SCL_PIN | ||||||
|  | 			config.SDA = I2C1_SDA_PIN | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	config.SDA.Configure(PinConfig{PinI2C}) | ||||||
|  | 	config.SCL.Configure(PinConfig{PinI2C}) | ||||||
|  | 	return i2c.init(config) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // SetBaudrate sets the I2C frequency. It has the side effect of also | ||||||
|  | // enabling the I2C hardware if disabled beforehand. | ||||||
|  | //go:inline | ||||||
|  | func (i2c *I2C) SetBaudrate(br uint32) error { | ||||||
|  | 	const freqin uint32 = 125 * MHz | ||||||
|  | 	// Find smallest prescale value which puts o | ||||||
|  | 
 | ||||||
|  | 	// TODO there are some subtleties to I2C timing which we are completely ignoring here | ||||||
|  | 	period := (freqin + br/2) / br | ||||||
|  | 	lcnt := period * 3 / 5 // oof this one hurts | ||||||
|  | 	hcnt := period - lcnt | ||||||
|  | 	// Check for out-of-range divisors: | ||||||
|  | 	if hcnt > rp.I2C0_IC_FS_SCL_HCNT_IC_FS_SCL_HCNT_Msk || hcnt < 8 || lcnt > rp.I2C0_IC_FS_SCL_LCNT_IC_FS_SCL_LCNT_Msk || lcnt < 8 { | ||||||
|  | 		return ErrInvalidI2CBaudrate | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Per I2C-bus specification a device in standard or fast mode must | ||||||
|  | 	// internally provide a hold time of at least 300ns for the SDA signal to | ||||||
|  | 	// bridge the undefined region of the falling edge of SCL. A smaller hold | ||||||
|  | 	// time of 120ns is used for fast mode plus. | ||||||
|  | 
 | ||||||
|  | 	// sda_tx_hold_count = freq_in [cycles/s] * 300ns * (1s / 1e9ns) | ||||||
|  | 	// Reduce 300/1e9 to 3/1e7 to avoid numbers that don't fit in uint. | ||||||
|  | 	// Add 1 to avoid division truncation. | ||||||
|  | 	sdaTxHoldCnt := ((freqin * 3) / 10000000) + 1 | ||||||
|  | 	if br >= 1_000_000 { | ||||||
|  | 		// sda_tx_hold_count = freq_in [cycles/s] * 120ns * (1s / 1e9ns) | ||||||
|  | 		// Reduce 120/1e9 to 3/25e6 to avoid numbers that don't fit in uint. | ||||||
|  | 		// Add 1 to avoid division truncation. | ||||||
|  | 		sdaTxHoldCnt = ((freqin * 3) / 25000000) + 1 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if sdaTxHoldCnt > lcnt-2 { | ||||||
|  | 		return ErrInvalidI2CBaudrate | ||||||
|  | 	} | ||||||
|  | 	err := i2c.disable() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	// Always use "fast" mode (<= 400 kHz, works fine for standard mode too) | ||||||
|  | 
 | ||||||
|  | 	i2c.Bus.IC_CON.ReplaceBits(rp.I2C0_IC_CON_SPEED_FAST<<rp.I2C0_IC_CON_SPEED_Pos, rp.I2C0_IC_CON_SPEED_Msk, 0) | ||||||
|  | 	i2c.Bus.IC_FS_SCL_HCNT.Set(hcnt) | ||||||
|  | 	i2c.Bus.IC_FS_SCL_LCNT.Set(lcnt) | ||||||
|  | 
 | ||||||
|  | 	i2c.Bus.IC_FS_SPKLEN.Set(u32max(1, lcnt/16)) | ||||||
|  | 
 | ||||||
|  | 	i2c.Bus.IC_SDA_HOLD.ReplaceBits(sdaTxHoldCnt<<rp.I2C0_IC_SDA_HOLD_IC_SDA_TX_HOLD_Pos, rp.I2C0_IC_SDA_HOLD_IC_SDA_TX_HOLD_Msk, 0) | ||||||
|  | 	i2c.enable() | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //go:inline | ||||||
|  | func (i2c *I2C) enable() { | ||||||
|  | 	i2c.Bus.IC_ENABLE.ReplaceBits(rp.I2C0_IC_ENABLE_ENABLE<<rp.I2C0_IC_ENABLE_ENABLE_Pos, rp.I2C0_IC_ENABLE_ENABLE_Msk, 0) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Implemented as per 4.3.10.3. Disabling DW_apb_i2c section. | ||||||
|  | //go:inline | ||||||
|  | func (i2c *I2C) disable() error { | ||||||
|  | 	const MAX_T_POLL_COUNT = 64 // 64 us timeout corresponds to around 1000kb/s i2c transfer rate. | ||||||
|  | 	deadline := ticks() + MAX_T_POLL_COUNT | ||||||
|  | 	i2c.Bus.IC_ENABLE.Set(0) | ||||||
|  | 	for i2c.Bus.IC_ENABLE_STATUS.Get()&1 != 0 { | ||||||
|  | 		if ticks() > deadline { | ||||||
|  | 			return ErrRP2040I2CDisable | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //go:inline | ||||||
|  | func (i2c *I2C) init(config I2CConfig) error { | ||||||
|  | 	i2c.reset() | ||||||
|  | 	if err := i2c.disable(); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	i2c.restartOnNext = false | ||||||
|  | 	// Configure as a fast-mode master with RepStart support, 7-bit addresses | ||||||
|  | 	i2c.Bus.IC_CON.Set((rp.I2C0_IC_CON_SPEED_FAST << rp.I2C0_IC_CON_SPEED_Pos) | | ||||||
|  | 		rp.I2C0_IC_CON_MASTER_MODE | rp.I2C0_IC_CON_IC_SLAVE_DISABLE | | ||||||
|  | 		rp.I2C0_IC_CON_IC_RESTART_EN | rp.I2C0_IC_CON_TX_EMPTY_CTRL) // sets TX_EMPTY_CTRL to enable TX_EMPTY interrupt status | ||||||
|  | 
 | ||||||
|  | 	// Set FIFO watermarks to 1 to make things simpler. This is encoded by a register value of 0. | ||||||
|  | 	i2c.Bus.IC_TX_TL.Set(0) | ||||||
|  | 	i2c.Bus.IC_RX_TL.Set(0) | ||||||
|  | 
 | ||||||
|  | 	// Always enable the DREQ signalling -- harmless if DMA isn't listening | ||||||
|  | 	i2c.Bus.IC_DMA_CR.Set(rp.I2C0_IC_DMA_CR_TDMAE | rp.I2C0_IC_DMA_CR_RDMAE) | ||||||
|  | 	return i2c.SetBaudrate(config.Frequency) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // reset sets I2C register RESET bits in the reset peripheral and then clears them. | ||||||
|  | //go:inline | ||||||
|  | func (i2c *I2C) reset() { | ||||||
|  | 	resetVal := i2c.deinit() | ||||||
|  | 	rp.RESETS.RESET.ClearBits(resetVal) | ||||||
|  | 	// Wait until reset is done. | ||||||
|  | 	for !rp.RESETS.RESET_DONE.HasBits(resetVal) { | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // deinit sets reset bit for I2C. Must call reset to reenable I2C after deinit. | ||||||
|  | //go:inline | ||||||
|  | func (i2c *I2C) deinit() (resetVal uint32) { | ||||||
|  | 	switch { | ||||||
|  | 	case i2c.Bus == rp.I2C0: | ||||||
|  | 		resetVal = rp.RESETS_RESET_I2C0 | ||||||
|  | 	case i2c.Bus == rp.I2C1: | ||||||
|  | 		resetVal = rp.RESETS_RESET_I2C1 | ||||||
|  | 	} | ||||||
|  | 	// Perform I2C reset. | ||||||
|  | 	rp.RESETS.RESET.SetBits(resetVal) | ||||||
|  | 
 | ||||||
|  | 	return resetVal | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // tx is a primitive i2c blocking write to bus routine. timeout is time to wait | ||||||
|  | // in microseconds since calling this function for write to finish. | ||||||
|  | func (i2c *I2C) tx(addr uint8, tx []byte, nostop bool, timeout uint64) (err error) { | ||||||
|  | 	deadline := ticks() + timeout | ||||||
|  | 	if addr >= 0x80 || isReservedI2CAddr(addr) { | ||||||
|  | 		return ErrInvalidTgtAddr | ||||||
|  | 	} | ||||||
|  | 	tlen := len(tx) | ||||||
|  | 	// Quick return if possible. | ||||||
|  | 	if tlen == 0 { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	err = i2c.disable() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	i2c.Bus.IC_TAR.Set(uint32(addr)) | ||||||
|  | 	i2c.enable() | ||||||
|  | 	// If no timeout was passed timeoutCheck is false. | ||||||
|  | 	abort := false | ||||||
|  | 	var abortReason uint32 | ||||||
|  | 	byteCtr := 0 | ||||||
|  | 	for ; byteCtr < tlen; byteCtr++ { | ||||||
|  | 		first := byteCtr == 0 | ||||||
|  | 		last := byteCtr == tlen-1 | ||||||
|  | 		i2c.Bus.IC_DATA_CMD.Set( | ||||||
|  | 			(boolToBit(first && i2c.restartOnNext) << rp.I2C0_IC_DATA_CMD_RESTART_Pos) | | ||||||
|  | 				(boolToBit(last && !nostop) << rp.I2C0_IC_DATA_CMD_STOP_Pos) | | ||||||
|  | 				uint32(tx[byteCtr])) | ||||||
|  | 
 | ||||||
|  | 		// Wait until the transmission of the address/data from the internal | ||||||
|  | 		// shift register has completed. For this to function correctly, the | ||||||
|  | 		// TX_EMPTY_CTRL flag in IC_CON must be set. The TX_EMPTY_CTRL flag | ||||||
|  | 		// was set in i2c_init. | ||||||
|  | 
 | ||||||
|  | 		// IC_RAW_INTR_STAT_TX_EMPTY: This bit is set to 1 when the transmit buffer is at or below | ||||||
|  | 		// the threshold value set in the IC_TX_TL register and the | ||||||
|  | 		// transmission of the address/data from the internal shift | ||||||
|  | 		// register for the most recently popped command is | ||||||
|  | 		// completed. It is automatically cleared by hardware when | ||||||
|  | 		// the buffer level goes above the threshold. When | ||||||
|  | 		// IC_ENABLE[0] is set to 0, the TX FIFO is flushed and held | ||||||
|  | 		// in reset. There the TX FIFO looks like it has no data within | ||||||
|  | 		// it, so this bit is set to 1, provided there is activity in the | ||||||
|  | 		// master or slave state machines. When there is no longer | ||||||
|  | 		// any activity, then with ic_en=0, this bit is set to 0. | ||||||
|  | 		for !i2c.interrupted(rp.I2C0_IC_RAW_INTR_STAT_TX_EMPTY) { | ||||||
|  | 			if ticks() > deadline { | ||||||
|  | 				i2c.restartOnNext = nostop | ||||||
|  | 				println(1) | ||||||
|  | 				return errI2CWriteTimeout // If there was a timeout, don't attempt to do anything else. | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		abortReason = i2c.getAbortReason() | ||||||
|  | 		if abortReason != 0 { | ||||||
|  | 			i2c.clearAbortReason() | ||||||
|  | 			abort = true | ||||||
|  | 		} | ||||||
|  | 		if abort || (last && !nostop) { | ||||||
|  | 			// If the transaction was aborted or if it completed | ||||||
|  | 			// successfully wait until the STOP condition has occured. | ||||||
|  | 
 | ||||||
|  | 			// TODO Could there be an abort while waiting for the STOP | ||||||
|  | 			// condition here? If so, additional code would be needed here | ||||||
|  | 			// to take care of the abort. | ||||||
|  | 			for !i2c.interrupted(rp.I2C0_IC_RAW_INTR_STAT_STOP_DET) { | ||||||
|  | 				if ticks() > deadline { | ||||||
|  | 					println(2) | ||||||
|  | 					i2c.restartOnNext = nostop | ||||||
|  | 					return errI2CWriteTimeout | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			i2c.Bus.IC_CLR_STOP_DET.Get() | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// From Pico SDK: A lot of things could have just happened due to the ingenious and | ||||||
|  | 	// creative design of I2C. Try to figure things out. | ||||||
|  | 	if abort { | ||||||
|  | 		switch { | ||||||
|  | 		case abortReason == 0 || abortReason&rp.I2C0_IC_TX_ABRT_SOURCE_ABRT_7B_ADDR_NOACK != 0: | ||||||
|  | 			// No reported errors - seems to happen if there is nothing connected to the bus. | ||||||
|  | 			// Address byte not acknowledged | ||||||
|  | 			err = ErrI2CGeneric | ||||||
|  | 		case abortReason&rp.I2C0_IC_TX_ABRT_SOURCE_ABRT_TXDATA_NOACK != 0: | ||||||
|  | 			// Address acknowledged, some data not acknowledged | ||||||
|  | 			fallthrough | ||||||
|  | 		default: | ||||||
|  | 			// panic("unknown i2c abortReason:" + strconv.Itoa(abortReason) | ||||||
|  | 			err = makeI2CBuffError(byteCtr) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// nostop means we are now at the end of a *message* but not the end of a *transfer* | ||||||
|  | 	i2c.restartOnNext = nostop | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // rx is a primitive i2c blocking read routine. timeout is time to wait | ||||||
|  | // in microseconds since calling this function for read to finish. | ||||||
|  | func (i2c *I2C) rx(addr uint8, rx []byte, nostop bool, timeout uint64) (err error) { | ||||||
|  | 	deadline := ticks() + timeout | ||||||
|  | 	if addr >= 0x80 || isReservedI2CAddr(addr) { | ||||||
|  | 		return ErrInvalidTgtAddr | ||||||
|  | 	} | ||||||
|  | 	rlen := len(rx) | ||||||
|  | 	// Quick return if possible. | ||||||
|  | 	if rlen == 0 { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	err = i2c.disable() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	i2c.Bus.IC_TAR.Set(uint32(addr)) | ||||||
|  | 	i2c.enable() | ||||||
|  | 	// If no timeout was passed timeoutCheck is false. | ||||||
|  | 	abort := false | ||||||
|  | 	var abortReason uint32 | ||||||
|  | 	byteCtr := 0 | ||||||
|  | 	for ; byteCtr < rlen; byteCtr++ { | ||||||
|  | 		first := byteCtr == 0 | ||||||
|  | 		last := byteCtr == rlen-1 | ||||||
|  | 		for i2c.writeAvailable() == 0 { | ||||||
|  | 		} | ||||||
|  | 		i2c.Bus.IC_DATA_CMD.Set( | ||||||
|  | 			boolToBit(first && i2c.restartOnNext)<<rp.I2C0_IC_DATA_CMD_RESTART_Pos | | ||||||
|  | 				boolToBit(last && !nostop)<<rp.I2C0_IC_DATA_CMD_STOP_Pos | | ||||||
|  | 				rp.I2C0_IC_DATA_CMD_CMD) // -> 1 for read | ||||||
|  | 
 | ||||||
|  | 		for !abort && i2c.readAvailable() == 0 { | ||||||
|  | 			abortReason = i2c.getAbortReason() | ||||||
|  | 			i2c.clearAbortReason() | ||||||
|  | 			if abortReason != 0 { | ||||||
|  | 				abort = true | ||||||
|  | 			} | ||||||
|  | 			if ticks() > deadline { | ||||||
|  | 				i2c.restartOnNext = nostop | ||||||
|  | 				return errI2CReadTimeout // If there was a timeout, don't attempt to do anything else. | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if abort { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 		rx[byteCtr] = uint8(i2c.Bus.IC_DATA_CMD.Get()) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if abort { | ||||||
|  | 		switch { | ||||||
|  | 		case abortReason == 0 || abortReason&rp.I2C0_IC_TX_ABRT_SOURCE_ABRT_7B_ADDR_NOACK != 0: | ||||||
|  | 			// No reported errors - seems to happen if there is nothing connected to the bus. | ||||||
|  | 			// Address byte not acknowledged | ||||||
|  | 			err = ErrI2CGeneric | ||||||
|  | 		default: | ||||||
|  | 			// undefined abort sequence | ||||||
|  | 			err = makeI2CBuffError(byteCtr) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	i2c.restartOnNext = nostop | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // writeAvailable determines non-blocking write space available | ||||||
|  | //go:inline | ||||||
|  | func (i2c *I2C) writeAvailable() uint32 { | ||||||
|  | 	return rp.I2C0_IC_COMP_PARAM_1_TX_BUFFER_DEPTH_Pos - i2c.Bus.IC_TXFLR.Get() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // readAvailable determines number of bytes received | ||||||
|  | //go:inline | ||||||
|  | func (i2c *I2C) readAvailable() uint32 { | ||||||
|  | 	return i2c.Bus.IC_RXFLR.Get() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Equivalent to IC_CLR_TX_ABRT.Get() (side effect clears ABORT_REASON) | ||||||
|  | //go:inline | ||||||
|  | func (i2c *I2C) clearAbortReason() { | ||||||
|  | 	// Note clearing the abort flag also clears the reason, and | ||||||
|  | 	// this instance of flag is clear-on-read! Note also the | ||||||
|  | 	// IC_CLR_TX_ABRT register always reads as 0. | ||||||
|  | 	i2c.Bus.IC_CLR_TX_ABRT.Get() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //go:inline | ||||||
|  | func (i2c *I2C) getAbortReason() uint32 { | ||||||
|  | 	return i2c.Bus.IC_TX_ABRT_SOURCE.Get() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // returns true if RAW_INTR_STAT bits in mask are all set. performs: | ||||||
|  | //  RAW_INTR_STAT & mask == mask | ||||||
|  | //go:inline | ||||||
|  | func (i2c *I2C) interrupted(mask uint32) bool { | ||||||
|  | 	reg := i2c.Bus.IC_RAW_INTR_STAT.Get() | ||||||
|  | 	return reg&mask == mask | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type i2cBuffError int | ||||||
|  | 
 | ||||||
|  | func (b i2cBuffError) Error() string { | ||||||
|  | 	return "i2c err after addr ack at data " + strconv.Itoa(int(b)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //go:inline | ||||||
|  | func makeI2CBuffError(idx int) error { | ||||||
|  | 	return i2cBuffError(idx) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //go:inline | ||||||
|  | func boolToBit(a bool) uint32 { | ||||||
|  | 	if a { | ||||||
|  | 		return 1 | ||||||
|  | 	} | ||||||
|  | 	return 0 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //go:inline | ||||||
|  | func u32max(a, b uint32) uint32 { | ||||||
|  | 	if a > b { | ||||||
|  | 		return a | ||||||
|  | 	} | ||||||
|  | 	return b | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //go:inline | ||||||
|  | func isReservedI2CAddr(addr uint8) bool { | ||||||
|  | 	return (addr&0x78) == 0 || (addr&0x78) == 0x78 | ||||||
|  | } | ||||||
		Загрузка…
	
	Создание таблицы
		
		Сослаться в новой задаче
	
	 Patricio Whittingslow
						Patricio Whittingslow