github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/machine/machine_nrf52xxx.go (about) 1 //go:build nrf52 || nrf52840 || nrf52833 2 3 package machine 4 5 import ( 6 "device/nrf" 7 "runtime/volatile" 8 "unsafe" 9 ) 10 11 func CPUFrequency() uint32 { 12 return 64000000 13 } 14 15 // InitADC initializes the registers needed for ADC. 16 func InitADC() { 17 return // no specific setup on nrf52 machine. 18 } 19 20 // Configure configures an ADC pin to be able to read analog data. 21 func (a ADC) Configure(config ADCConfig) { 22 // Enable ADC. 23 // The ADC does not consume a noticeable amount of current simply by being 24 // enabled. 25 nrf.SAADC.ENABLE.Set(nrf.SAADC_ENABLE_ENABLE_Enabled << nrf.SAADC_ENABLE_ENABLE_Pos) 26 27 // Use fixed resolution of 12 bits. 28 // TODO: is it useful for users to change this? 29 nrf.SAADC.RESOLUTION.Set(nrf.SAADC_RESOLUTION_VAL_12bit) 30 31 var configVal uint32 = nrf.SAADC_CH_CONFIG_RESP_Bypass<<nrf.SAADC_CH_CONFIG_RESP_Pos | 32 nrf.SAADC_CH_CONFIG_RESP_Bypass<<nrf.SAADC_CH_CONFIG_RESN_Pos | 33 nrf.SAADC_CH_CONFIG_REFSEL_Internal<<nrf.SAADC_CH_CONFIG_REFSEL_Pos | 34 nrf.SAADC_CH_CONFIG_MODE_SE<<nrf.SAADC_CH_CONFIG_MODE_Pos 35 36 switch config.Reference { 37 case 150: // 0.15V 38 configVal |= nrf.SAADC_CH_CONFIG_GAIN_Gain4 << nrf.SAADC_CH_CONFIG_GAIN_Pos 39 case 300: // 0.3V 40 configVal |= nrf.SAADC_CH_CONFIG_GAIN_Gain2 << nrf.SAADC_CH_CONFIG_GAIN_Pos 41 case 600: // 0.6V 42 configVal |= nrf.SAADC_CH_CONFIG_GAIN_Gain1 << nrf.SAADC_CH_CONFIG_GAIN_Pos 43 case 1200: // 1.2V 44 configVal |= nrf.SAADC_CH_CONFIG_GAIN_Gain1_2 << nrf.SAADC_CH_CONFIG_GAIN_Pos 45 case 1800: // 1.8V 46 configVal |= nrf.SAADC_CH_CONFIG_GAIN_Gain1_3 << nrf.SAADC_CH_CONFIG_GAIN_Pos 47 case 2400: // 2.4V 48 configVal |= nrf.SAADC_CH_CONFIG_GAIN_Gain1_4 << nrf.SAADC_CH_CONFIG_GAIN_Pos 49 case 3000, 0: // 3.0V (default) 50 configVal |= nrf.SAADC_CH_CONFIG_GAIN_Gain1_5 << nrf.SAADC_CH_CONFIG_GAIN_Pos 51 case 3600: // 3.6V 52 configVal |= nrf.SAADC_CH_CONFIG_GAIN_Gain1_6 << nrf.SAADC_CH_CONFIG_GAIN_Pos 53 default: 54 // TODO: return an error 55 } 56 57 // Source resistance, according to table 89 on page 364 of the nrf52832 datasheet. 58 // https://infocenter.nordicsemi.com/pdf/nRF52832_PS_v1.4.pdf 59 if config.SampleTime <= 3 { // <= 10kΩ 60 configVal |= nrf.SAADC_CH_CONFIG_TACQ_3us << nrf.SAADC_CH_CONFIG_TACQ_Pos 61 } else if config.SampleTime <= 5 { // <= 40kΩ 62 configVal |= nrf.SAADC_CH_CONFIG_TACQ_5us << nrf.SAADC_CH_CONFIG_TACQ_Pos 63 } else if config.SampleTime <= 10 { // <= 100kΩ 64 configVal |= nrf.SAADC_CH_CONFIG_TACQ_10us << nrf.SAADC_CH_CONFIG_TACQ_Pos 65 } else if config.SampleTime <= 15 { // <= 200kΩ 66 configVal |= nrf.SAADC_CH_CONFIG_TACQ_15us << nrf.SAADC_CH_CONFIG_TACQ_Pos 67 } else if config.SampleTime <= 20 { // <= 400kΩ 68 configVal |= nrf.SAADC_CH_CONFIG_TACQ_20us << nrf.SAADC_CH_CONFIG_TACQ_Pos 69 } else { // <= 800kΩ 70 configVal |= nrf.SAADC_CH_CONFIG_TACQ_40us << nrf.SAADC_CH_CONFIG_TACQ_Pos 71 } 72 73 // Oversampling configuration. 74 burst := true 75 switch config.Samples { 76 default: // no oversampling 77 nrf.SAADC.OVERSAMPLE.Set(nrf.SAADC_OVERSAMPLE_OVERSAMPLE_Bypass) 78 burst = false 79 case 2: 80 nrf.SAADC.OVERSAMPLE.Set(nrf.SAADC_OVERSAMPLE_OVERSAMPLE_Over2x) 81 case 4: 82 nrf.SAADC.OVERSAMPLE.Set(nrf.SAADC_OVERSAMPLE_OVERSAMPLE_Over4x) 83 case 8: 84 nrf.SAADC.OVERSAMPLE.Set(nrf.SAADC_OVERSAMPLE_OVERSAMPLE_Over8x) 85 case 16: 86 nrf.SAADC.OVERSAMPLE.Set(nrf.SAADC_OVERSAMPLE_OVERSAMPLE_Over16x) 87 case 32: 88 nrf.SAADC.OVERSAMPLE.Set(nrf.SAADC_OVERSAMPLE_OVERSAMPLE_Over32x) 89 case 64: 90 nrf.SAADC.OVERSAMPLE.Set(nrf.SAADC_OVERSAMPLE_OVERSAMPLE_Over64x) 91 case 128: 92 nrf.SAADC.OVERSAMPLE.Set(nrf.SAADC_OVERSAMPLE_OVERSAMPLE_Over128x) 93 case 256: 94 nrf.SAADC.OVERSAMPLE.Set(nrf.SAADC_OVERSAMPLE_OVERSAMPLE_Over256x) 95 } 96 if burst { 97 // BURST=1 is needed when oversampling 98 configVal |= nrf.SAADC_CH_CONFIG_BURST 99 } 100 101 // Configure channel 0, which is the only channel we use. 102 nrf.SAADC.CH[0].CONFIG.Set(configVal) 103 } 104 105 // Get returns the current value of a ADC pin in the range 0..0xffff. 106 func (a ADC) Get() uint16 { 107 var pwmPin uint32 108 var rawValue volatile.Register16 109 110 switch a.Pin { 111 case 2: 112 pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput0 113 114 case 3: 115 pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput1 116 117 case 4: 118 pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput2 119 120 case 5: 121 pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput3 122 123 case 28: 124 pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput4 125 126 case 29: 127 pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput5 128 129 case 30: 130 pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput6 131 132 case 31: 133 pwmPin = nrf.SAADC_CH_PSELP_PSELP_AnalogInput7 134 135 default: 136 return 0 137 } 138 139 // Set pin to read. 140 nrf.SAADC.CH[0].PSELN.Set(pwmPin) 141 nrf.SAADC.CH[0].PSELP.Set(pwmPin) 142 143 // Destination for sample result. 144 nrf.SAADC.RESULT.PTR.Set(uint32(uintptr(unsafe.Pointer(&rawValue)))) 145 nrf.SAADC.RESULT.MAXCNT.Set(1) // One sample 146 147 // Start tasks. 148 nrf.SAADC.TASKS_START.Set(1) 149 for nrf.SAADC.EVENTS_STARTED.Get() == 0 { 150 } 151 nrf.SAADC.EVENTS_STARTED.Set(0x00) 152 153 // Start the sample task. 154 nrf.SAADC.TASKS_SAMPLE.Set(1) 155 156 // Wait until the sample task is done. 157 for nrf.SAADC.EVENTS_END.Get() == 0 { 158 } 159 nrf.SAADC.EVENTS_END.Set(0x00) 160 161 // Stop the ADC 162 nrf.SAADC.TASKS_STOP.Set(1) 163 for nrf.SAADC.EVENTS_STOPPED.Get() == 0 { 164 } 165 nrf.SAADC.EVENTS_STOPPED.Set(0) 166 167 value := int16(rawValue.Get()) 168 if value < 0 { 169 value = 0 170 } 171 172 // Return 16-bit result from 12-bit value. 173 return uint16(value << 4) 174 } 175 176 // SPI on the NRF. 177 type SPI struct { 178 Bus *nrf.SPIM_Type 179 buf *[1]byte // 1-byte buffer for the Transfer method 180 } 181 182 // There are 3 SPI interfaces on the NRF528xx. 183 var ( 184 SPI0 = SPI{Bus: nrf.SPIM0, buf: new([1]byte)} 185 SPI1 = SPI{Bus: nrf.SPIM1, buf: new([1]byte)} 186 SPI2 = SPI{Bus: nrf.SPIM2, buf: new([1]byte)} 187 ) 188 189 // SPIConfig is used to store config info for SPI. 190 type SPIConfig struct { 191 Frequency uint32 192 SCK Pin 193 SDO Pin 194 SDI Pin 195 LSBFirst bool 196 Mode uint8 197 } 198 199 // Configure is intended to setup the SPI interface. 200 func (spi SPI) Configure(config SPIConfig) error { 201 // Disable bus to configure it 202 spi.Bus.ENABLE.Set(nrf.SPIM_ENABLE_ENABLE_Disabled) 203 204 // Pick a default frequency. 205 if config.Frequency == 0 { 206 config.Frequency = 4000000 // 4MHz 207 } 208 209 // set frequency 210 var freq uint32 211 switch { 212 case config.Frequency >= 8000000: 213 freq = nrf.SPIM_FREQUENCY_FREQUENCY_M8 214 case config.Frequency >= 4000000: 215 freq = nrf.SPIM_FREQUENCY_FREQUENCY_M4 216 case config.Frequency >= 2000000: 217 freq = nrf.SPIM_FREQUENCY_FREQUENCY_M2 218 case config.Frequency >= 1000000: 219 freq = nrf.SPIM_FREQUENCY_FREQUENCY_M1 220 case config.Frequency >= 500000: 221 freq = nrf.SPIM_FREQUENCY_FREQUENCY_K500 222 case config.Frequency >= 250000: 223 freq = nrf.SPIM_FREQUENCY_FREQUENCY_K250 224 default: // below 250kHz, default to the lowest speed available 225 freq = nrf.SPIM_FREQUENCY_FREQUENCY_K125 226 } 227 spi.Bus.FREQUENCY.Set(freq) 228 229 var conf uint32 230 231 // set bit transfer order 232 if config.LSBFirst { 233 conf = (nrf.SPIM_CONFIG_ORDER_LsbFirst << nrf.SPIM_CONFIG_ORDER_Pos) 234 } 235 236 // set mode 237 switch config.Mode { 238 case 0: 239 conf &^= (nrf.SPIM_CONFIG_CPOL_ActiveHigh << nrf.SPIM_CONFIG_CPOL_Pos) 240 conf &^= (nrf.SPIM_CONFIG_CPHA_Leading << nrf.SPIM_CONFIG_CPHA_Pos) 241 case 1: 242 conf &^= (nrf.SPIM_CONFIG_CPOL_ActiveHigh << nrf.SPIM_CONFIG_CPOL_Pos) 243 conf |= (nrf.SPIM_CONFIG_CPHA_Trailing << nrf.SPIM_CONFIG_CPHA_Pos) 244 case 2: 245 conf |= (nrf.SPIM_CONFIG_CPOL_ActiveLow << nrf.SPIM_CONFIG_CPOL_Pos) 246 conf &^= (nrf.SPIM_CONFIG_CPHA_Leading << nrf.SPIM_CONFIG_CPHA_Pos) 247 case 3: 248 conf |= (nrf.SPIM_CONFIG_CPOL_ActiveLow << nrf.SPIM_CONFIG_CPOL_Pos) 249 conf |= (nrf.SPIM_CONFIG_CPHA_Trailing << nrf.SPIM_CONFIG_CPHA_Pos) 250 default: // to mode 251 conf &^= (nrf.SPIM_CONFIG_CPOL_ActiveHigh << nrf.SPIM_CONFIG_CPOL_Pos) 252 conf &^= (nrf.SPIM_CONFIG_CPHA_Leading << nrf.SPIM_CONFIG_CPHA_Pos) 253 } 254 spi.Bus.CONFIG.Set(conf) 255 256 // set pins 257 if config.SCK == 0 && config.SDO == 0 && config.SDI == 0 { 258 config.SCK = SPI0_SCK_PIN 259 config.SDO = SPI0_SDO_PIN 260 config.SDI = SPI0_SDI_PIN 261 } 262 spi.Bus.PSEL.SCK.Set(uint32(config.SCK)) 263 spi.Bus.PSEL.MOSI.Set(uint32(config.SDO)) 264 spi.Bus.PSEL.MISO.Set(uint32(config.SDI)) 265 266 // Re-enable bus now that it is configured. 267 spi.Bus.ENABLE.Set(nrf.SPIM_ENABLE_ENABLE_Enabled) 268 269 return nil 270 } 271 272 // Transfer writes/reads a single byte using the SPI interface. 273 func (spi SPI) Transfer(w byte) (byte, error) { 274 buf := spi.buf[:] 275 buf[0] = w 276 err := spi.Tx(buf[:], buf[:]) 277 return buf[0], err 278 } 279 280 // Tx handles read/write operation for SPI interface. Since SPI is a syncronous 281 // write/read interface, there must always be the same number of bytes written 282 // as bytes read. Therefore, if the number of bytes don't match it will be 283 // padded until they fit: if len(w) > len(r) the extra bytes received will be 284 // dropped and if len(w) < len(r) extra 0 bytes will be sent. 285 func (spi SPI) Tx(w, r []byte) error { 286 // Unfortunately the hardware (on the nrf52832) only supports up to 255 287 // bytes in the buffers, so if either w or r is longer than that the 288 // transfer needs to be broken up in pieces. 289 // The nrf52840 supports far larger buffers however, which isn't yet 290 // supported. 291 for len(r) != 0 || len(w) != 0 { 292 // Prepare the SPI transfer: set the DMA pointers and lengths. 293 // read buffer 294 nr := uint32(len(r)) 295 if nr > 0 { 296 if nr > 255 { 297 nr = 255 298 } 299 spi.Bus.RXD.PTR.Set(uint32(uintptr(unsafe.Pointer(&r[0])))) 300 r = r[nr:] 301 } 302 spi.Bus.RXD.MAXCNT.Set(nr) 303 304 // write buffer 305 nw := uint32(len(w)) 306 if nw > 0 { 307 if nw > 255 { 308 nw = 255 309 } 310 spi.Bus.TXD.PTR.Set(uint32(uintptr(unsafe.Pointer(&w[0])))) 311 w = w[nw:] 312 } 313 spi.Bus.TXD.MAXCNT.Set(nw) 314 315 // Do the transfer. 316 // Note: this can be improved by not waiting until the transfer is 317 // finished if the transfer is send-only (a common case). 318 spi.Bus.TASKS_START.Set(1) 319 for spi.Bus.EVENTS_END.Get() == 0 { 320 } 321 spi.Bus.EVENTS_END.Set(0) 322 } 323 324 return nil 325 } 326 327 // PWM is one PWM peripheral, which consists of a counter and multiple output 328 // channels (that can be connected to actual pins). You can set the frequency 329 // using SetPeriod, but only for all the channels in this PWM peripheral at 330 // once. 331 type PWM struct { 332 PWM *nrf.PWM_Type 333 334 channelValues [4]volatile.Register16 335 } 336 337 // Configure enables and configures this PWM. 338 // On the nRF52 series, the maximum period is around 0.26s. 339 func (pwm *PWM) Configure(config PWMConfig) error { 340 // Enable the peripheral. 341 pwm.PWM.ENABLE.Set(nrf.PWM_ENABLE_ENABLE_Enabled << nrf.PWM_ENABLE_ENABLE_Pos) 342 343 // Use up counting only. TODO: allow configuring as up-and-down. 344 pwm.PWM.MODE.Set(nrf.PWM_MODE_UPDOWN_Up << nrf.PWM_MODE_UPDOWN_Pos) 345 346 // Indicate there are four channels that each have a different value. 347 pwm.PWM.DECODER.Set(nrf.PWM_DECODER_LOAD_Individual<<nrf.PWM_DECODER_LOAD_Pos | nrf.PWM_DECODER_MODE_RefreshCount<<nrf.PWM_DECODER_MODE_Pos) 348 349 err := pwm.setPeriod(config.Period, true) 350 if err != nil { 351 return err 352 } 353 354 // Set the EasyDMA buffer, which has 4 values (one for each channel). 355 pwm.PWM.SEQ[0].PTR.Set(uint32(uintptr(unsafe.Pointer(&pwm.channelValues[0])))) 356 pwm.PWM.SEQ[0].CNT.Set(4) 357 358 // SEQ[0] is not yet started, it will be started on the first 359 // PWMChannel.Set() call. 360 361 return nil 362 } 363 364 // SetPeriod updates the period of this PWM peripheral. 365 // To set a particular frequency, use the following formula: 366 // 367 // period = 1e9 / frequency 368 // 369 // If you use a period of 0, a period that works well for LEDs will be picked. 370 // 371 // SetPeriod will not change the prescaler, but also won't change the current 372 // value in any of the channels. This means that you may need to update the 373 // value for the particular channel. 374 // 375 // Note that you cannot pick any arbitrary period after the PWM peripheral has 376 // been configured. If you want to switch between frequencies, pick the lowest 377 // frequency (longest period) once when calling Configure and adjust the 378 // frequency here as needed. 379 func (pwm *PWM) SetPeriod(period uint64) error { 380 return pwm.setPeriod(period, false) 381 } 382 383 func (pwm *PWM) setPeriod(period uint64, updatePrescaler bool) error { 384 const maxTop = 0x7fff // 15 bits counter 385 386 // The top value is the number of PWM ticks a PWM period takes. It is 387 // initially picked assuming an unlimited COUNTERTOP and no PWM prescaler. 388 var top uint64 389 if period == 0 { 390 // The period is 0, which means "pick something reasonable for LEDs". 391 top = maxTop 392 } else { 393 // The formula below calculates the following formula, optimized: 394 // period * (16e6 / 1e9) 395 // The max frequency (16e6 or 16MHz) is set by the hardware. 396 top = period * 2 / 125 397 } 398 399 // The ideal PWM period may be larger than would fit in the PWM counter, 400 // which is only 15 bits (see maxTop). Therefore, try to make the PWM clock 401 // speed lower with a prescaler to make the top value fit the COUNTERTOP. 402 if updatePrescaler { 403 // This function was called during Configure(). 404 switch { 405 case top <= maxTop: 406 pwm.PWM.PRESCALER.Set(nrf.PWM_PRESCALER_PRESCALER_DIV_1) 407 case top/2 <= maxTop: 408 pwm.PWM.PRESCALER.Set(nrf.PWM_PRESCALER_PRESCALER_DIV_2) 409 top /= 2 410 case top/4 <= maxTop: 411 pwm.PWM.PRESCALER.Set(nrf.PWM_PRESCALER_PRESCALER_DIV_4) 412 top /= 4 413 case top/8 <= maxTop: 414 pwm.PWM.PRESCALER.Set(nrf.PWM_PRESCALER_PRESCALER_DIV_8) 415 top /= 8 416 case top/16 <= maxTop: 417 pwm.PWM.PRESCALER.Set(nrf.PWM_PRESCALER_PRESCALER_DIV_16) 418 top /= 16 419 case top/32 <= maxTop: 420 pwm.PWM.PRESCALER.Set(nrf.PWM_PRESCALER_PRESCALER_DIV_32) 421 top /= 32 422 case top/64 <= maxTop: 423 pwm.PWM.PRESCALER.Set(nrf.PWM_PRESCALER_PRESCALER_DIV_64) 424 top /= 64 425 case top/128 <= maxTop: 426 pwm.PWM.PRESCALER.Set(nrf.PWM_PRESCALER_PRESCALER_DIV_128) 427 top /= 128 428 default: 429 return ErrPWMPeriodTooLong 430 } 431 } else { 432 // Do not update the prescaler, but use the already-configured 433 // prescaler. This is the normal SetPeriod case, where the prescaler 434 // must not be changed. 435 prescaler := pwm.PWM.PRESCALER.Get() 436 switch prescaler { 437 case nrf.PWM_PRESCALER_PRESCALER_DIV_1: 438 top /= 1 439 case nrf.PWM_PRESCALER_PRESCALER_DIV_2: 440 top /= 2 441 case nrf.PWM_PRESCALER_PRESCALER_DIV_4: 442 top /= 4 443 case nrf.PWM_PRESCALER_PRESCALER_DIV_8: 444 top /= 8 445 case nrf.PWM_PRESCALER_PRESCALER_DIV_16: 446 top /= 16 447 case nrf.PWM_PRESCALER_PRESCALER_DIV_32: 448 top /= 32 449 case nrf.PWM_PRESCALER_PRESCALER_DIV_64: 450 top /= 64 451 case nrf.PWM_PRESCALER_PRESCALER_DIV_128: 452 top /= 128 453 } 454 if top > maxTop { 455 return ErrPWMPeriodTooLong 456 } 457 } 458 pwm.PWM.COUNTERTOP.Set(uint32(top)) 459 460 // Apparently this is needed to apply the new COUNTERTOP. 461 pwm.PWM.TASKS_SEQSTART[0].Set(1) 462 463 return nil 464 } 465 466 // Top returns the current counter top, for use in duty cycle calculation. It 467 // will only change with a call to Configure or SetPeriod, otherwise it is 468 // constant. 469 // 470 // The value returned here is hardware dependent. In general, it's best to treat 471 // it as an opaque value that can be divided by some number and passed to 472 // pwm.Set (see pwm.Set for more information). 473 func (pwm *PWM) Top() uint32 { 474 return pwm.PWM.COUNTERTOP.Get() 475 } 476 477 // Channel returns a PWM channel for the given pin. 478 func (pwm *PWM) Channel(pin Pin) (uint8, error) { 479 config := uint32(pin) 480 for ch := uint8(0); ch < 4; ch++ { 481 channelConfig := pwm.PWM.PSEL.OUT[ch].Get() 482 if channelConfig == 0xffffffff { 483 // Unused channel. Configure it. 484 pwm.PWM.PSEL.OUT[ch].Set(config) 485 // Configure the pin (required by the reference manual). 486 pin.Configure(PinConfig{Mode: PinOutput}) 487 // Set channel to zero and non-inverting. 488 pwm.channelValues[ch].Set(0x8000) 489 return ch, nil 490 } else if channelConfig == config { 491 // This channel is already configured for this pin. 492 return ch, nil 493 } 494 } 495 496 // All four pins are already in use with other pins. 497 return 0, ErrInvalidOutputPin 498 } 499 500 // SetInverting sets whether to invert the output of this channel. 501 // Without inverting, a 25% duty cycle would mean the output is high for 25% of 502 // the time and low for the rest. Inverting flips the output as if a NOT gate 503 // was placed at the output, meaning that the output would be 25% low and 75% 504 // high with a duty cycle of 25%. 505 func (pwm *PWM) SetInverting(channel uint8, inverting bool) { 506 ptr := &pwm.channelValues[channel] 507 if inverting { 508 ptr.Set(ptr.Get() &^ 0x8000) 509 } else { 510 ptr.Set(ptr.Get() | 0x8000) 511 } 512 } 513 514 // Set updates the channel value. This is used to control the channel duty 515 // cycle. For example, to set it to a 25% duty cycle, use: 516 // 517 // ch.Set(ch.Top() / 4) 518 // 519 // ch.Set(0) will set the output to low and ch.Set(ch.Top()) will set the output 520 // to high, assuming the output isn't inverted. 521 func (pwm *PWM) Set(channel uint8, value uint32) { 522 // Update the channel value while retaining the polarity bit. 523 ptr := &pwm.channelValues[channel] 524 ptr.Set(ptr.Get()&0x8000 | uint16(value)&0x7fff) 525 526 // Start the PWM, if it isn't already running. 527 pwm.PWM.TASKS_SEQSTART[0].Set(1) 528 }