123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136 |
- #include <avr/io.h>
- #include <util/delay.h>
- static inline void errBlinky(uint16_t repeat);
- /* Qualcomm AR9271 802.11n chip needs firmware from the manufacturer, but they
- * are not capable of producing reliable software. During normal use they
- * require their chip to be reset for continued operation.
- *
- * There is no documentation on when one might need to perform this reset.
- * Based on some observation I found that the both chip and firmware support
- * controlling a LED from the USB host. When the manufacturer's firmware is
- * requiring the host to perform the reset controlling the LEDs does not work
- * as expected, indicating the request for reset.
- *
- * Yes, I understand how dumb this is, but one must understand the situation
- * that Qualcomm is in. If they knew what they were doing things would be
- * different, and that, might cause them some concern.
- *
- * --
- *
- * Monitor the LED output, the host should blink the LED once per second. We
- * will blink our LED in sync with their LED and blink rapidly if we are
- * resetting the AR9271.
- */
- int main(void) {
- // Set our led to output
- DDRB |= _BV(PB5);
- // Set input (pin labeled 12), connect this to the AR9271 LED output
- DDRB &= ~_BV(PB4);
-
- uint8_t last = 0;
- uint8_t count = 0;
- for(;;) {
- uint8_t status = 0;
- // host must keep the LED on for a minimum of one second per Nyquist-Shannon Sampling Theorem
- for(uint8_t i = 10; i--;) {
- // mirror our led to the AR9271 led, and remember it's status
- if(status = (PINB & _BV(PB4))) PORTB |= _BV(PB5); else PORTB &= ~_BV(PB5);
- // do this over about 500ms (one half second, we are in a fixed length loop)
- _delay_ms(50);
- }
- // compare the recorded state to the last state, it should alternate
- if(++count && last != status) count = 0;
- // stash status to compare next time...
- last = status;
- // if the counter is small we can skip reset
- if(3 >= count) continue; else count = 0;
- // power off USB device
- { // switch outputs to high impedance rather than "turning off", same effect
- DDRB &= ~ (_BV(PB2) | _BV(PB3));
- // wait three seconds (50 thousandths of a minute)
- errBlinky(50);
- }
- // power on USB device, computer will load firmware into it
- { // Set relay (pins labeled 10 and 11) to output.
- DDRB |= (_BV(PB2) | _BV(PB3));
- // [!] Use digital pins as power source, relay coil should be >200 Ohms
- PORTB |= _BV(PB2);
- PORTB &= ~_BV(PB3);
- // let the relay pull in, click, clack, and whatever
- _delay_ms(100);
- // drop power consumption (switch pin 11 to high impedance)
- DDRB &= ~_BV(PB3);
- // failure after power-up is unlikely, should consider computer boot time
- errBlinky(3000 /* thousandths of a minute is three minutes */);
- }
- }
- return 0;
- }
- static inline void errBlinky(uint16_t repeat) {
- // limit repeat to a maximum of about three minutes
- repeat = (3000 < repeat) ?3000 :repeat;
- // alternate our led every one thousandths of a minute to show error state
- for(;repeat--;) { PORTB ^= _BV(PB5); _delay_ms(60); }
- }
- /* some numbers regarding the relay and our arduino pro mini
- * we are using a 3V3 Pro Mini connected to a Omron G6DS-1A-H5
- * the relay says it must switch at 70% of rated 5V, which is 3V5 so we
- * are under powering the relay, cross your fingers
- *
- * the relay datasheet claims an operate time of 10ms max, so when we switch
- * it 100ms is plenty for debounce. We will also pull back the current using
- * a current limit resistor, the datasheet claims 5% of rated voltage for must
- * release. We use a 200 Ohm resistor and the coil is 208 Ohm. We might push
- * about 8mA current with our 3V3 digital pin, so that'll be interesting if
- * that works.
- *
- * [?] attempting to power the relay with a diode on PB3 and a resistor on
- * PB2 was not enough, using a test PSU 3V0 is enough to energize relay, but
- * it was right on the edge. I ended up putting a PNP transistor on PB3 with
- * it's emitter on the 5V rail, this is more than enough. Code obviously needs
- * to be adjusted because we want PB3 and PB2 to oppose each other. The Zener
- * is used to make sure that when PB3 floats it isn't pulled to 3V3 by the
- * 328p digital input pin proection diodes. 2V1 seemed to be enough, more is
- * fine, but when PB3 is driven to ground the base of the PNP does need to come
- * down a bit less than the 5V so 4V2 might be the limit on the Zener or the relay
- * may not energize. I did test with two series silicon diodes with the opposite
- * polarity hoping the 1V2 forward voltage was enough to let the base float to 5V
- * but it did not work.
- *
- *
- * Arduino 3V3 Pro Mini
- * +---------------+
- * | o o o o o o | ___
- * |o o| <raw> -------|___> 5V
- * |o _ _ o| <gnd> -----+
- * |o | | | | o| <rst> |
- * |o '-' '-' o| <3v3> |
- * |o . o| V
- * |o .` `. o|
- * |o < > o|
- * |o '. .' o|
- * |o ' o| <led> (PB5) error indicator
- * |o ___ o| <input> (PB4) ----------------+--------+
- * |o -/ _ \- o| <output> (PB3) ------+ | |
- * |o -\___/- o| <output> (PB2) --+ | .-. .-.
- * +---------------+ | .-. |=| |R| pulldown
- * | |R| |D| '-'
- * .-. '-' '-' |
- * |R| | | |
- * +-------------+ +---+---+ '-' .-. | V
- * | G6DS c | | | | | |Z| 2V1 |___
- * | relay o +----+ .-. +----+ |=| Zener |___> AR9271_LED
- * | i | |=|f | '-'
- * | sw l+----+ |D|l | | ___
- * +--+---+------+ | '-'y | | E--|___> 5V
- * | | | | b d | +--B
- * | |___ | | a i | C--+
- * | |___> 5V +---+ c o | |
- * |___ | k d +---------+
- * |___> AR9271_POWER | e
- * V
- */
|