/* Teensy 4.x, 3.x, LC ADC library * https://github.com/pedvide/ADC * Copyright (c) 2020 Pedro Villanueva * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef ADC_ATOMIC_H #define ADC_ATOMIC_H /* int __builtin_ctz (unsigned int x): Returns the number of trailing 0-bits in x, starting at the least significant bit position. If x is 0, the result is undefined. */ /* int __builtin_clz (unsigned int x) Returns the number of leading 0-bits in x, starting at the most significant bit position. If x is 0, the result is undefined. */ /* int __builtin_popcount (unsigned int x) Returns the number of 1-bits in x. */ // kinetis.h has the following types for addresses: uint32_t, uint16_t, uint8_t, int32_t, int16_t //! Atomic set, clear, change, or get bit in a register namespace atomic { /////// Atomic bit set/clear /* Clear bit in address (make it zero), set bit (make it one), or return the value of that bit * changeBitFlag can change up to 2 bits in a flag at the same time * We can change this functions depending on the board. * Teensy 3.x use bitband while Teensy LC has a more advanced bit manipulation engine. * Teensy 4 also has bitband capabilities, but are not yet implemented, instead registers are * set and cleared manually. TODO: fix this. */ #if defined(KINETISK) // Teensy 3.x //! Bitband address /** Gets the aliased address of the bit-band register * \param reg Register in the bit-band area * \param bit Bit number of reg to read/modify * \return A pointer to the aliased address of the bit of reg */ template __attribute__((always_inline)) inline volatile T &bitband_address(volatile T ®, uint8_t bit) { return (*(volatile T *)(((uint32_t)® - 0x40000000) * 32 + bit * 4 + 0x42000000)); } template __attribute__((always_inline)) inline void setBit(volatile T ®, uint8_t bit) { bitband_address(reg, bit) = 1; } template __attribute__((always_inline)) inline void setBitFlag(volatile T ®, T flag) { // 31-__builtin_clzl(flag) = gets bit number in flag // __builtin_clzl works for long ints, which are guaranteed by standard to be at least 32 bit wide. // there's no difference in the asm emitted. bitband_address(reg, 31 - __builtin_clzl(flag)) = 1; if (__builtin_popcount(flag) > 1) { // __builtin_ctzl returns the number of trailing 0-bits in x, starting at the least significant bit position bitband_address(reg, __builtin_ctzl(flag)) = 1; } } template __attribute__((always_inline)) inline void clearBit(volatile T ®, uint8_t bit) { bitband_address(reg, bit) = 0; } template __attribute__((always_inline)) inline void clearBitFlag(volatile T ®, T flag) { bitband_address(reg, 31 - __builtin_clzl(flag)) = 0; if (__builtin_popcount(flag) > 1) { bitband_address(reg, __builtin_ctzl(flag)) = 0; } } template __attribute__((always_inline)) inline void changeBit(volatile T ®, uint8_t bit, bool state) { bitband_address(reg, bit) = state; } template __attribute__((always_inline)) inline void changeBitFlag(volatile T ®, T flag, T state) { bitband_address(reg, __builtin_ctzl(flag)) = (state >> __builtin_ctzl(flag)) & 0x1; if (__builtin_popcount(flag) > 1) { bitband_address(reg, 31 - __builtin_clzl(flag)) = (state >> (31 - __builtin_clzl(flag))) & 0x1; } } template __attribute__((always_inline)) inline volatile bool getBit(volatile T ®, uint8_t bit) { return (volatile bool)bitband_address(reg, bit); } template __attribute__((always_inline)) inline volatile bool getBitFlag(volatile T ®, T flag) { return (volatile bool)bitband_address(reg, 31 - __builtin_clzl(flag)); } #elif defined(__IMXRT1062__) // Teensy 4 template __attribute__((always_inline)) inline void setBitFlag(volatile T ®, T flag) { __disable_irq(); reg |= flag; __enable_irq(); } template __attribute__((always_inline)) inline void clearBitFlag(volatile T ®, T flag) { __disable_irq(); reg &= ~flag; __enable_irq(); } template __attribute__((always_inline)) inline void changeBitFlag(volatile T ®, T flag, T state) { // flag can be 1 or 2 bits wide // state can have one or two bits set if (__builtin_popcount(flag) == 1) { // 1 bit if (state) { setBitFlag(reg, flag); } else { clearBitFlag(reg, flag); } } else { // 2 bits // lsb first if ((state >> __builtin_ctzl(flag)) & 0x1) { // lsb of state is 1 setBitFlag(reg, (uint32_t)(1 << __builtin_ctzl(flag))); } else { // lsb is 0 clearBitFlag(reg, (uint32_t)(1 << __builtin_ctzl(flag))); } // msb if ((state >> (31 - __builtin_clzl(flag))) & 0x1) { // msb of state is 1 setBitFlag(reg, (uint32_t)(1 << (31 - __builtin_clzl(flag)))); } else { // msb is 0 clearBitFlag(reg, (uint32_t)(1 << (31 - __builtin_clzl(flag)))); } } } template __attribute__((always_inline)) inline volatile bool getBitFlag(volatile T ®, T flag) { return (volatile bool)((reg)&flag) >> (31 - __builtin_clzl(flag)); } #elif defined(KINETISL) // Teensy LC // bit manipulation engine template __attribute__((always_inline)) inline void setBit(volatile T ®, uint8_t bit) { //temp = *(uint32_t *)((uint32_t)(reg) | (1<<26) | (bit<<21)); // LAS *(volatile T *)((uint32_t)(®) | (1 << 27)) = 1 << bit; // OR } template __attribute__((always_inline)) inline void setBitFlag(volatile T ®, uint32_t flag) { *(volatile T *)((uint32_t)® | (1 << 27)) = flag; // OR } template __attribute__((always_inline)) inline void clearBit(volatile T ®, uint8_t bit) { //temp = *(uint32_t *)((uint32_t)(reg) | (3<<27) | (bit<<21)); // LAC *(volatile T *)((uint32_t)(®) | (1 << 26)) = ~(1 << bit); // AND } template __attribute__((always_inline)) inline void clearBitFlag(volatile T ®, uint32_t flag) { //temp = *(uint32_t *)((uint32_t)(reg) | (3<<27) | (bit<<21)); // LAC *(volatile T *)((uint32_t)(®) | (1 << 26)) = ~flag; // AND } template __attribute__((always_inline)) inline void changeBit(volatile T ®, uint8_t bit, bool state) { //temp = *(uint32_t *)((uint32_t)(reg) | ((3-2*!!state)<<27) | (bit<<21)); // LAS/LAC state ? setBit(reg, bit) : clearBit(reg, bit); } template __attribute__((always_inline)) inline void changeBitFlag(volatile T ®, T flag, T state) { // BFI, bitfield width set to __builtin_popcount(flag) // least significant bit set to __builtin_ctzl(flag) *(volatile T *)((uint32_t)(®) | (1 << 28) | (__builtin_ctzl(flag) << 23) | ((__builtin_popcount(flag) - 1) << 19)) = state; } template __attribute__((always_inline)) inline volatile bool getBit(volatile T ®, uint8_t bit) { return (volatile bool)*(volatile T *)((uint32_t)(®) | (1 << 28) | (bit << 23)); // UBFX } template __attribute__((always_inline)) inline volatile bool getBitFlag(volatile T ®, T flag) { return (volatile bool)*(volatile T *)((uint32_t)(®) | (1 << 28) | ((31 - __builtin_clzl(flag)) << 23)); // UBFX } #endif } // namespace atomic #endif // ADC_ATOMIC_H