@johntalton/ftdi 1.1.0 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/README.md +88 -16
  2. package/lib/eeprom.d.ts +5 -5
  3. package/lib/eeprom.d.ts.map +1 -1
  4. package/lib/eeprom.js +2 -1
  5. package/lib/eeprom.js.map +1 -1
  6. package/lib/ftdi.d.ts +1 -1
  7. package/lib/ftdi.d.ts.map +1 -1
  8. package/lib/ftdi.js +16 -16
  9. package/lib/ftdi.js.map +1 -1
  10. package/lib/i2c/i2c-template.d.ts +19 -1
  11. package/lib/i2c/i2c-template.d.ts.map +1 -1
  12. package/lib/i2c/i2c-template.js +24 -13
  13. package/lib/i2c/i2c-template.js.map +1 -1
  14. package/lib/i2c/i2c.d.ts +2 -2
  15. package/lib/i2c/i2c.d.ts.map +1 -1
  16. package/lib/i2c/i2c.js +14 -8
  17. package/lib/i2c/i2c.js.map +1 -1
  18. package/lib/mpsse/command.d.ts.map +1 -1
  19. package/lib/mpsse/command.js +2 -2
  20. package/lib/mpsse/command.js.map +1 -1
  21. package/lib/mpsse/mpsse-template.d.ts +4 -2
  22. package/lib/mpsse/mpsse-template.d.ts.map +1 -1
  23. package/lib/mpsse/mpsse-template.js +36 -10
  24. package/lib/mpsse/mpsse-template.js.map +1 -1
  25. package/lib/mpsse/mpsse.d.ts +2 -2
  26. package/lib/mpsse/mpsse.d.ts.map +1 -1
  27. package/lib/mpsse/mpsse.js +2 -2
  28. package/lib/spi/index.d.ts +14 -0
  29. package/lib/spi/index.d.ts.map +1 -0
  30. package/lib/spi/index.js +74 -0
  31. package/lib/spi/index.js.map +1 -0
  32. package/lib/util.d.ts +4 -4
  33. package/lib/util.d.ts.map +1 -1
  34. package/lib/util.js +9 -8
  35. package/lib/util.js.map +1 -1
  36. package/package.json +14 -3
  37. package/src/eeprom.ts +10 -9
  38. package/src/ftdi.ts +16 -17
  39. package/src/i2c/i2c-template.ts +32 -13
  40. package/src/i2c/i2c.ts +15 -12
  41. package/src/mpsse/command.ts +2 -3
  42. package/src/mpsse/mpsse-template.ts +27 -10
  43. package/src/mpsse/mpsse.ts +3 -3
  44. package/src/spi/index.ts +113 -0
  45. package/src/util.ts +10 -9
package/src/ftdi.ts CHANGED
@@ -1,4 +1,4 @@
1
- /** biome-ignore-all lint/style/noNestedTernary: <explanation> */
1
+ /** biome-ignore-all lint/style/noNestedTernary: makes code better */
2
2
  import type { BitMode, ModemControl, RequestType } from './consts.ts'
3
3
  import { REQUESTS, RESET_USB } from './consts.ts'
4
4
  import { MPSSE } from './mpsse/mpsse.ts'
@@ -112,16 +112,16 @@ export class FTDIDevice {
112
112
  }
113
113
 
114
114
  async getModemStatus(): Promise<DeviceStatusInfo|undefined> {
115
- const status = await this.#requestIn(REQUESTS.POLL_MODEM_STATUS, STATUS_PREFIX_LENGTH)
116
- if(status.status !== USB_TRANSFER_OK) { throw new Error('status not ok') }
117
- if(status.data === undefined) { throw new Error('undefined data') }
118
- assertDataViewNotShared(status.data)
119
- return DeviceStatus.parse(status.data)
115
+ const result = await this.#requestIn(REQUESTS.POLL_MODEM_STATUS, STATUS_PREFIX_LENGTH)
116
+ if(result.status !== USB_TRANSFER_OK) { throw new Error(`unexpected status: ${result.status}`) }
117
+ if(result.data === undefined) { throw new Error('undefined data') }
118
+ assertDataViewNotShared(result.data)
119
+ return DeviceStatus.parse(result.data)
120
120
  }
121
121
 
122
122
  async getLatencyTimer(): Promise<number> {
123
123
  const result = await this.#requestIn(REQUESTS.GET_LATENCY_TIMER, 1)
124
- if(result.status !== USB_TRANSFER_OK) { throw new Error('status not ok') }
124
+ if(result.status !== USB_TRANSFER_OK) { throw new Error(`unexpected status: ${result.status}`) }
125
125
  if(result.data === undefined) { throw new Error('undefined data') }
126
126
 
127
127
  const latency = result.data.getUint8(0)
@@ -133,12 +133,12 @@ export class FTDIDevice {
133
133
  if(latency < 0 || latency > 255) { throw new RangeError('latency out of range') }
134
134
 
135
135
  const result = await this.#requestOut(REQUESTS.SET_LATENCY_TIMER, latency)
136
- if(result.status !== USB_TRANSFER_OK) { throw new Error('status not ok') }
136
+ if(result.status !== USB_TRANSFER_OK) { throw new Error(`unexpected status: ${result.status}`) }
137
137
  }
138
138
 
139
139
  async readPins(): Promise<number> {
140
140
  const result = await this.#requestIn(REQUESTS.READ_PINS, 1)
141
- if(result.status !== USB_TRANSFER_OK) { throw new Error('status not ok') }
141
+ if(result.status !== USB_TRANSFER_OK) { throw new Error(`unexpected status: ${result.status}`) }
142
142
  if(result.data === undefined) { throw new Error('undefined data') }
143
143
  return result.data.getUint8(0)
144
144
  }
@@ -157,7 +157,7 @@ export class FTDIDevice {
157
157
  const value = dtrValue | rtsValue
158
158
 
159
159
  const result = await this.#requestOut(REQUESTS.SET_MODEM_CTRL, value)
160
- if(result.status !== USB_TRANSFER_OK) { throw new Error('status not ok') }
160
+ if(result.status !== USB_TRANSFER_OK) { throw new Error(`unexpected status: ${result.status}`) }
161
161
  }
162
162
 
163
163
  async setEventChar(data: number, enable = true): Promise<void> {
@@ -166,7 +166,7 @@ export class FTDIDevice {
166
166
  const value = enable ? ENABLE_BIT | data : data
167
167
 
168
168
  const result = await this.#requestOut(REQUESTS.SET_EVENT_CHAR, value)
169
- if(result.status !== USB_TRANSFER_OK) { throw new Error('status not ok') }
169
+ if(result.status !== USB_TRANSFER_OK) { throw new Error(`unexpected status: ${result.status}`) }
170
170
  }
171
171
 
172
172
  async setErrorChar(data: number, enable = true): Promise<void> {
@@ -175,20 +175,19 @@ export class FTDIDevice {
175
175
  const value = enable ? ENABLE_BIT | data : data
176
176
 
177
177
  const result = await this.#requestOut(REQUESTS.SET_ERROR_CHAR, value)
178
- if(result.status !== USB_TRANSFER_OK) { throw new Error('status not ok') }
178
+ if(result.status !== USB_TRANSFER_OK) { throw new Error(`unexpected status: ${result.status}`) }
179
179
 
180
180
  }
181
181
 
182
-
183
182
  async sendData(data: BufferSource): Promise<number> {
184
183
  const result = await this.#device.transferOut(this.#endpointBulkOut, data)
185
- if(result.status !== USB_TRANSFER_OK) { throw new Error('failure sending data') }
184
+ if(result.status !== USB_TRANSFER_OK) { throw new Error(`unexpected status sending data: ${result.status}`) }
186
185
  return result.bytesWritten
187
186
  }
188
187
 
189
188
  async readData(length: number): Promise<DataView<ArrayBuffer>> {
190
189
  const result = await this.#device.transferIn(this.#endpointBulkIn, length)
191
- if(result.status !== USB_TRANSFER_OK) { throw new Error('failure to read data') }
190
+ if(result.status !== USB_TRANSFER_OK) { throw new Error(`unexpected status reading data: ${result.status}`) }
192
191
  if(result.data === undefined) { throw new Error('result data undefined') }
193
192
  assertDataViewNotShared(result.data)
194
193
  return result.data
@@ -196,7 +195,7 @@ export class FTDIDevice {
196
195
 
197
196
  async readEEPROM(address: number, length: number): Promise<DataView<ArrayBuffer>> {
198
197
  const result = await this.#requestIn(REQUESTS.READ_EEPROM, length, address)
199
- if(result.status !== USB_TRANSFER_OK) { throw new Error('failure to read eeprom') }
198
+ if(result.status !== USB_TRANSFER_OK) { throw new Error(`unexpected status reading eeprom: ${result.status}`) }
200
199
  if(result.data === undefined) { throw new Error('result data undefined') }
201
200
  assertDataViewNotShared(result.data)
202
201
 
@@ -205,7 +204,7 @@ export class FTDIDevice {
205
204
 
206
205
  async writeEEPROM(address: number, value: number): Promise<number> {
207
206
  const result = await this.#requestOut(REQUESTS.WRITE_EEPROM, value, address)
208
- if(result.status !== USB_TRANSFER_OK) { throw new Error('failure to read eeprom') }
207
+ if(result.status !== USB_TRANSFER_OK) { throw new Error(`unexpected status writing eeprom: ${result.status}`) }
209
208
  return result.bytesWritten
210
209
  }
211
210
 
@@ -1,4 +1,4 @@
1
- /** biome-ignore-all lint/style/useConsistentArrayType: <explanation> */
1
+ /** biome-ignore-all lint/style/useConsistentArrayType: miss-classified */
2
2
  import {
3
3
  CLOCK_BASE_12,
4
4
  CLOCK_BASE_60,
@@ -14,20 +14,37 @@ import {
14
14
  } from '../mpsse/command.ts'
15
15
  import { MPSSETemplate } from '../mpsse/mpsse-template.ts'
16
16
 
17
- const SDA_PIN_MASK = 0b0000_0010
18
- const SCL_PIN_MASK = 0b0000_0001
17
+ export const SCL_PIN_MASK = 0b0000_0001 // D0
18
+ export const SDA_PIN_MASK = 0b0000_0010 // D1
19
+ export const TX_PIN_MASK = 0b0010_0000 // D5
20
+ export const RX_PIN_MASK = 0b0100_0000 // D6
19
21
 
20
- const SCL_HIGH_SDA_HIGH: number = SDA_PIN_MASK | SCL_PIN_MASK
21
- const SCL_HIGH_SDA_LOW: number = SCL_PIN_MASK
22
- const SCL_LOW_SDA_HIGH: number = SDA_PIN_MASK
23
- const SCL_LOW_SDA_LOW = 0
24
22
 
25
- const SDA_DIRECTION_OUT = 0b0000_0010
26
- const SCL_DIRECTION_OUT = 0b0000_0001
27
- const SDA_SCL_DIRECTION_OUT: number = SDA_DIRECTION_OUT | SCL_DIRECTION_OUT
28
23
 
29
- const OPEN_DRAIN_LOW_PIN_MASK = 0b0000_0111
30
- const OPEN_DRAIN_HIGH_PIN_MASK = 0b0000_0000
24
+ export const TX_LOW_RX_LOW = 0
25
+ export const TX_LOW_RX_HIGH: number = RX_PIN_MASK
26
+ export const TX_HIGH_RX_LOW: number = TX_PIN_MASK
27
+ export const TX_HIGH_RX_HIGH: number = RX_PIN_MASK | TX_PIN_MASK
28
+
29
+
30
+ export const SCL_HIGH_SDA_HIGH: number = SDA_PIN_MASK | SCL_PIN_MASK | TX_HIGH_RX_HIGH
31
+ export const SCL_HIGH_SDA_LOW: number = SCL_PIN_MASK | TX_HIGH_RX_HIGH
32
+ export const SCL_LOW_SDA_HIGH: number = SDA_PIN_MASK | TX_HIGH_RX_HIGH
33
+ export const SCL_LOW_SDA_LOW: number = 0 | TX_HIGH_RX_HIGH
34
+
35
+ // export const SCL_LOW_SDA_HIGH_RX: number = SDA_PIN_MASK | TX_HIGH_RX_LOW
36
+ // export const SCL_LOW_SDA_HIGH_TX: number = SDA_PIN_MASK | TX_LOW_RX_HIGH
37
+
38
+
39
+
40
+
41
+ export const SDA_DIRECTION_OUT = 0b0000_0010
42
+ export const SCL_DIRECTION_OUT = 0b0000_0001
43
+ export const RX_TX_DIRECTION = 0b0110_0000
44
+ export const SDA_SCL_DIRECTION_OUT: number = SDA_DIRECTION_OUT | SCL_DIRECTION_OUT | RX_TX_DIRECTION
45
+
46
+ export const OPEN_DRAIN_LOW_PIN_MASK = 0b0110_0111
47
+ export const OPEN_DRAIN_HIGH_PIN_MASK = 0b0000_0000
31
48
 
32
49
  export class I2CTemplate {
33
50
  static initI2C(targetHertz: number, enableClockDivideBy5 = false): Uint8Array<ArrayBuffer> {
@@ -98,7 +115,9 @@ export class I2CTemplate {
98
115
 
99
116
  static writeByte(data: number): Uint8Array<ArrayBuffer> {
100
117
  return Uint8Array.from([
118
+ // ...MPSSETemplate.gpioSetLow(SCL_LOW_SDA_HIGH_TX, SDA_SCL_DIRECTION_OUT),
101
119
  ...MPSSETemplate.shiftOutByte(SHIFT_COMMAND.OUT_MSB_BYTES_NEGATIVE_VE, 1, data),
120
+ // ...MPSSETemplate.gpioSetLow(SCL_LOW_SDA_LOW, SDA_SCL_DIRECTION_OUT),
102
121
  ...I2CTemplate.#consumeAck(),
103
122
  HOST_AND_MPSSE_MODE_COMMANDS.SEND_IMMEDIATE
104
123
  ])
@@ -116,7 +135,7 @@ export class I2CTemplate {
116
135
  ])
117
136
  }
118
137
 
119
- static readData(ack = true): Uint8Array<ArrayBuffer> {
138
+ static readData(ack: boolean): Uint8Array<ArrayBuffer> {
120
139
  const MSB_ACK_BIT = 0x00
121
140
  const MSB_NACK_BIT = 0x80
122
141
  const data = ack ? MSB_ACK_BIT : MSB_NACK_BIT
package/src/i2c/i2c.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /** biome-ignore-all lint/style/noNestedTernary: preserve const assign */
2
2
  /** biome-ignore-all lint/performance/noAwaitInLoops: simplify impl */
3
- /** biome-ignore-all lint/style/useConsistentArrayType: <explanation> */
3
+ /** biome-ignore-all lint/style/useConsistentArrayType: miss-classified */
4
4
  import type {
5
5
  I2CAddress,
6
6
  I2CBufferSource,
@@ -40,9 +40,6 @@ export async function readData(device: FTDIDevice, length: number, targetBuffer?
40
40
  new Uint8Array(targetBuffer.buffer, targetBuffer.byteOffset, length) :
41
41
  new Uint8Array(targetBuffer, 0, length))
42
42
 
43
- // c0 low - RX-LED-emulation
44
- // await device.mpsse.setGpioHigh(0b0000_0000, 0b0000_0001)
45
-
46
43
  for(let offset = 0; offset < length; offset += 1) {
47
44
  const ack = offset + 1 < length // is last byte
48
45
 
@@ -53,9 +50,6 @@ export async function readData(device: FTDIDevice, length: number, targetBuffer?
53
50
  buffer.set(new Uint8Array(byteReadResponse.buffer, byteReadResponse.byteOffset, 1), offset)
54
51
  }
55
52
 
56
- // c0 high - RX-LED-emulation
57
- // await device.mpsse.setGpioHigh(0b0000_0001, 0b0000_0001)
58
-
59
53
  return buffer
60
54
  }
61
55
 
@@ -99,8 +93,8 @@ export class FT232HBus implements I2CBus {
99
93
  this.#device = device
100
94
  }
101
95
 
102
- // biome-ignore lint/nursery/useThisInClassMethods: close is a noop
103
- close(): void {
96
+ // biome-ignore lint/style/useThisInClassMethods: close is a noop
97
+ close(): void {
104
98
  // noop
105
99
  }
106
100
 
@@ -121,9 +115,18 @@ export class FT232HBus implements I2CBus {
121
115
  return result
122
116
  }
123
117
 
124
- // biome-ignore lint/nursery/useThisInClassMethods: no impl
125
- async sendByte(_address: I2CAddress, _byteValue: number): Promise<void> {
126
- throw new Error('Method not implemented.')
118
+ async sendByte(address: I2CAddress, byteValue: number): Promise<void> {
119
+
120
+ //
121
+ const startAck = await sendAndReadACK(this.#device, I2CTemplate.startWithAddress(address, true))
122
+ if(!startAck) { throw new Error('start with address nack') }
123
+
124
+ //
125
+ const writeAck = await sendAndReadACK(this.#device, I2CTemplate.writeByte(byteValue))
126
+ if(!writeAck) { throw new Error('write data nack') }
127
+
128
+ //
129
+ await this.#device.sendData(I2CTemplate.stop())
127
130
  }
128
131
 
129
132
  async readI2cBlock(address: I2CAddress, cmd: I2CCommand, length: number, targetBuffer?: I2CBufferSource): Promise<I2CReadResult> {
@@ -14,7 +14,7 @@ export const SHIFT_COMMANDS_DEFINITION = {
14
14
 
15
15
  export type ShiftCommand = number
16
16
 
17
- export const SHIFT_COMMAND = {
17
+ export const SHIFT_COMMAND = /** @type {const} */ ({
18
18
  OUT_MSB_BYTES_POSITIVE_VE: 0x10, // WRITE_TDI
19
19
  OUT_MSB_BYTES_NEGATIVE_VE: 0x11, // WRITE_TDI | WRITE_NEGATIVE_EDGE
20
20
  OUT_MSB_BITS_POSITIVE_VE: 0x12, // WRITE_TDI | BIT_MODE
@@ -45,8 +45,7 @@ export const SHIFT_COMMAND = {
45
45
  IN_OUT_LSB_BYTES_OUT_POSITIVE_IN_NEGATIVE_VE: 0x3C,
46
46
  IN_OUT_LSB_BITS_OUT_NEGATIVE_IN_POSITIVE_VE: 0x3B,
47
47
  IN_OUT_LSB_BITS_OUT_POSITIVE_IN_NEGATIVE_VE: 0x3E,
48
-
49
- }
48
+ })
50
49
 
51
50
  // export const LSB_FIRST_TMS_COMMANDS = {
52
51
  // CLOCK_DATA_NO_READ_POSITIVE_VE: 0x4A,
@@ -1,4 +1,5 @@
1
- /** biome-ignore-all lint/style/useConsistentArrayType: <explanation> */
1
+ /** biome-ignore-all lint/style/useConsistentArrayType: miss-classified */
2
+ /** biome-ignore-all lint/nursery/noMisleadingReturnType: false-positive */
2
3
  import {
3
4
  CLOCK_DIVISOR_COMMANDS,
4
5
  FT232H_ONLY_COMMANDS,
@@ -11,23 +12,39 @@ import {
11
12
 
12
13
  export class MPSSETemplate {
13
14
  static shiftOutByte(command: ShiftCommand, length: number, data: number): Array<number> {
14
- const lengthL = (length - 1) & 0x0F
15
- const lengthH = ((length - 1) & 0xF0) >> 8
15
+ if(length <= 0) { throw new RangeError('can not read zero length') }
16
+ const lengthMinusOne = length - 1
17
+ const lengthL = lengthMinusOne & 0xFF
18
+ const lengthH = (lengthMinusOne >> 8) & 0xFF
16
19
  return [ command, lengthL, lengthH, data ]
17
20
  }
18
21
 
22
+ static shiftOutBytes(command: ShiftCommand, length: number, data: Array<number>): Array<number> {
23
+ if(length <= 0) { throw new RangeError('can not read zero length') }
24
+ const lengthMinusOne = length - 1
25
+ const lengthL = lengthMinusOne & 0xFF
26
+ const lengthH = (lengthMinusOne >> 8) & 0xFF
27
+ return [ command, lengthL, lengthH, ...data ]
28
+ }
29
+
19
30
  static shiftInByte(command: ShiftCommand, length: number): Array<number> {
20
- const lengthL = (length - 1) & 0x0F
21
- const lengthH = ((length - 1) & 0xF0) >> 8
31
+ if(length <= 0) { throw new RangeError('can not read zero length') }
32
+ const lengthMinusOne = length - 1
33
+ const lengthL = lengthMinusOne & 0xFF
34
+ const lengthH = (lengthMinusOne >> 8) & 0xFF
22
35
  return [ command, lengthL, lengthH ]
23
36
  }
24
37
 
25
38
  static shiftOutBits(command: ShiftCommand, length: number, data: number): Array<number> {
26
- return [ command, length - 1, data ]
39
+ if(length <= 0) { throw new RangeError('can not read zero length') }
40
+ const lengthMinusOne = length - 1
41
+ return [ command, lengthMinusOne, data ]
27
42
  }
28
43
 
29
44
  static shiftInBits(command: ShiftCommand, length: number): Array<number> {
30
- return [ command, length - 1 ]
45
+ if(length <= 0) { throw new RangeError('can not read zero length') }
46
+ const lengthMinusOne = length - 1
47
+ return [ command, lengthMinusOne ]
31
48
  }
32
49
 
33
50
  static gpioSetHigh(pins: number, directions: number): Array<number> {
@@ -54,9 +71,9 @@ export class MPSSETemplate {
54
71
  return [ PIN_STATE_COMMANDS.READ_DATA_BITS_LOW_BYTE ]
55
72
  }
56
73
 
57
- static setClockDivisor(divider: number): Array<number> {
58
- const divisorL = divider & 0xFF
59
- const divisorH = (divider >> 8) & 0xFF
74
+ static setClockDivisor(divisor: number): Array<number> {
75
+ const divisorL = divisor & 0xFF
76
+ const divisorH = (divisor >> 8) & 0xFF
60
77
 
61
78
  return [
62
79
  CLOCK_DIVISOR_COMMANDS.SET_CLK_DIVISOR,
@@ -1,4 +1,4 @@
1
- /** biome-ignore-all lint/style/useConsistentArrayType: <explanation> */
1
+ /** biome-ignore-all lint/style/useConsistentArrayType: miss-classified */
2
2
  import type { FTDIDevice } from '../ftdi.ts'
3
3
  import { Util } from '../util.ts'
4
4
  import { BAD_COMMAND, BAD_COMMAND_RESPONSE } from './command.ts'
@@ -49,8 +49,8 @@ export class MPSSE {
49
49
  }
50
50
 
51
51
 
52
- async setClockDivisor(divider: number): Promise<void> {
53
- const command = MPSSETemplate.setClockDivisor(divider)
52
+ async setClockDivisor(divisor: number): Promise<void> {
53
+ const command = MPSSETemplate.setClockDivisor(divisor)
54
54
  return this.executeCommands(command)
55
55
  }
56
56
 
@@ -0,0 +1,113 @@
1
+ /** biome-ignore-all lint/style/noNestedTernary: we like them */
2
+ import type {
3
+ SPIBus,
4
+ SPIOptions,
5
+ TransferRequest,
6
+ TransferResult,
7
+ } from '@johntalton/and-other-delights'
8
+ import { DEFAULT_SPI_MODE, MODE_0 } from '@johntalton/and-other-delights'
9
+ import type { FTDIDevice } from '../ftdi.ts'
10
+ import {
11
+ CLOCK_BASE_60,
12
+ CLOCK_DEFAULT_STANDARD_MODE_100_kHz,
13
+ Clock
14
+ } from '../mpsse/clock.ts'
15
+ import { H_COMMANDS, HOST_AND_MPSSE_MODE_COMMANDS, SHIFT_COMMAND } from '../mpsse/command.ts'
16
+ import { MPSSETemplate } from '../mpsse/mpsse-template.ts'
17
+ import { Util } from '../util.ts'
18
+
19
+
20
+ // const CLOCK_PIN_MASK = 0b0000_0001
21
+ // const MOSI_PIN_MASK = 0b0000_0010 // host out
22
+ // const MISO_PIN_MASK = 0b0000_0100 // host in
23
+ const CS0_PIN_MASK = 0b0000_1000
24
+
25
+ // const CS1_PIN_MASK = 0b0001_0000
26
+ // const LED_TX_PIN_MASK = 0b0010_0000
27
+ // const LED_RX_PIN_MASK = 0b0100_0000
28
+ // const CLOCK_MONITOR_PIN_MASK = 0b1000_0000
29
+
30
+ const SPI_PIN_DIRECTION = 0b0000_1011
31
+
32
+ export const delayMs = (ms: number): Promise<void> => new Promise(resolve => setTimeout(resolve, ms))
33
+
34
+ export interface FT232HSPIOptions extends SPIOptions {
35
+ chipSelect1?: [boolean]
36
+ ledRxTx?: [boolean]
37
+ }
38
+
39
+ export class FTDISPIBus implements SPIBus {
40
+ readonly #device: FTDIDevice
41
+ readonly #options: Partial<FT232HSPIOptions>
42
+
43
+ constructor(device: FTDIDevice, options?: Partial<FT232HSPIOptions>) {
44
+ this.#device = device
45
+ this.#options = options ?? {}
46
+ }
47
+
48
+ async transfer(request: TransferRequest, options?: Partial<FT232HSPIOptions>): Promise<TransferResult> {
49
+ const {
50
+ mode, speedHz
51
+ } = {
52
+ mode: DEFAULT_SPI_MODE,
53
+ speedHz: CLOCK_DEFAULT_STANDARD_MODE_100_kHz,
54
+ ...this.#options,
55
+ ...(options ?? {})
56
+ }
57
+
58
+ const {
59
+ sendBuffer,
60
+ receiveBufferLength
61
+ } = request
62
+
63
+ if(mode !== MODE_0) { throw new Error('currently mode 0 only') }
64
+
65
+ const writeLength = sendBuffer?.byteLength ?? 0
66
+ const sendBufferU8 = (sendBuffer === undefined) ?
67
+ [] :
68
+ ArrayBuffer.isView(sendBuffer) ?
69
+ new Uint8Array(sendBuffer.buffer, sendBuffer.byteOffset, sendBuffer.byteLength) :
70
+ new Uint8Array(sendBuffer)
71
+
72
+ // calc clock divider for non-div-5
73
+ const divisor = Clock.clockDivisor(speedHz, CLOCK_BASE_60)
74
+
75
+ const totalBytesWritten = await this.#device.sendData(Uint8Array.from([
76
+ // preamble to set state
77
+ H_COMMANDS.DISABLE_CLOCK_DIVIDE_BY_FIVE,
78
+ ...MPSSETemplate.setClockDivisor(divisor),
79
+ H_COMMANDS.DISABLE_THREE_PHASE_CLOCKING,
80
+ H_COMMANDS.DISABLE_ADAPTIVE_CLOCKING,
81
+
82
+ // prep pins
83
+ ...MPSSETemplate.gpioSetLow(CS0_PIN_MASK, SPI_PIN_DIRECTION), // CS High / Clock Low
84
+ ...MPSSETemplate.gpioSetLow(0, SPI_PIN_DIRECTION), // CS Low / Clock Low
85
+
86
+ // transfer data
87
+ ...MPSSETemplate.shiftOutBytes(SHIFT_COMMAND.OUT_MSB_BYTES_NEGATIVE_VE, writeLength, [ ...sendBufferU8 ]),
88
+ ...((receiveBufferLength === 0) ? [] : MPSSETemplate.shiftInByte(SHIFT_COMMAND.IN_MSB_BYTES_POSITIVE_VE, receiveBufferLength)),
89
+
90
+ // return pins to idle state
91
+ ...MPSSETemplate.gpioSetLow(CS0_PIN_MASK, SPI_PIN_DIRECTION), // CS high / Clock Low
92
+
93
+ HOST_AND_MPSSE_MODE_COMMANDS.SEND_IMMEDIATE // do it now
94
+ ]))
95
+
96
+ if(totalBytesWritten === 0) { throw new Error('no bytes written') }
97
+
98
+ if(receiveBufferLength === 0) {
99
+ return {
100
+ bytesWritten: writeLength
101
+ }
102
+ }
103
+
104
+ // consume buffer
105
+ const result = await Util.pollData(this.#device, receiveBufferLength)
106
+
107
+ return {
108
+ bytesWritten: writeLength,
109
+ readBuffer: result
110
+ }
111
+ }
112
+
113
+ }
package/src/util.ts CHANGED
@@ -1,7 +1,7 @@
1
- /** biome-ignore-all lint/performance/noAwaitInLoops: <explanation> */
2
- /** biome-ignore-all lint/style/useConsistentArrayType: <explanation> */
3
- import type { FTDIDevice } from "./ftdi.ts"
4
- import { STATUS_PREFIX_LENGTH } from "./status.ts"
1
+ /** biome-ignore-all lint/performance/noAwaitInLoops: simplifies code */
2
+ /** biome-ignore-all lint/style/useConsistentArrayType: miss-classified */
3
+ import type { FTDIDevice } from './ftdi.ts'
4
+ import { STATUS_PREFIX_LENGTH } from './status.ts'
5
5
 
6
6
  export interface ChipInfo {
7
7
  major: number
@@ -57,14 +57,15 @@ export class Util {
57
57
  return info
58
58
  }
59
59
 
60
- static async pollData(device: FTDIDevice, attempts = DEFAULT_MAX_POLL_ATTEMPTS): Promise<Uint8Array<ArrayBuffer>> {
60
+ static async pollData(device: FTDIDevice, length = DEFAULT_DATA_READ_SIZE, attempts = DEFAULT_MAX_POLL_ATTEMPTS): Promise<Uint8Array<ArrayBuffer>> {
61
+ // console.log('pollData', length)
61
62
  for(let i = 0; i < attempts; i += 1) {
62
- const response = await device.readData(DEFAULT_DATA_READ_SIZE)
63
- if(response.byteLength === STATUS_PREFIX_LENGTH) { continue }
63
+ const response = await device.readData(STATUS_PREFIX_LENGTH + length)
64
64
  if(response === undefined) { throw new Error('no response') }
65
+ if(response.byteLength === STATUS_PREFIX_LENGTH) { continue }
65
66
  // const status = DeviceStatus.parse(response)
66
- // console.log('attempts', i)
67
- return new Uint8Array(response.buffer, STATUS_PREFIX_LENGTH)
67
+ // console.log('attempts', i, status)
68
+ return new Uint8Array(response.buffer, response.byteOffset + STATUS_PREFIX_LENGTH)
68
69
  }
69
70
 
70
71
  throw new Error('no valid data acquired')