I am working on a simple project that involves reading a 12-bit binary encoder signal and cycles a digital pin high and low (depending on the angular location of the encoder).
My problem is that when the digital output pin is told to go low, it only goes down to about 4V. I also noticed that it would occasionally dip down to ground, instead of just 4V. Much to my surprise, when I unplug the Arduino, the digital output pin is still reading 4V. When I turned off power to my encoder which is connected up the the Arduino through 12 digital input pins, the output pin dropped down to 0V. When I turn the encoder back on it goes back up to about 4V.
I have tried connecting the output pin to ground and obviously the voltage goes to 0, but as soon as I remove the connection to ground the voltage springs back up to around 4V. It appears as though somehow the voltage being fed to the digital input pins (in the form of a ~5V digital input signal) is preventing the digital output pin from going to ground. I have no idea why this is the case, I have searched all over and could not find any similar complaints. My code is posed below, any help is greatly appreciated!
unsigned long CamAngle = 0; // Variable to store encoder value
unsigned long PrevCamAngle = 0; // Variable to store previous encoder value
int i = 0; // Cycle index
int BDC[] = {683,2048,3413}; // BDC angle (12-bit encoder value)
int TDC[] = {0,1365,2731}; // TDC angle (12-bit encoder value)
boolean TDCycle = false; // TDC Cycle
boolean BDCycle = false; // BDC Cycle
boolean Cycle = true; // Encoder rollover cycle (replaces TDC cycle 0)
void setup()
{
pinMode(37, INPUT); // Encoder bit 0 (PORTC-0)
pinMode(36, INPUT); // Encoder bit 1 (PORTC-1)
pinMode(35, INPUT); // Encoder bit 2 (PORTC-2)
pinMode(34, INPUT); // Encoder bit 3 (PORTC-3)
pinMode(22, INPUT); // Encoder bit 4 (PORTA-0)
pinMode(23, INPUT); // Encoder bit 5 (PORTA-1)
pinMode(24, INPUT); // Encoder bit 6 (PORTA-2)
pinMode(25, INPUT); // Encoder bit 7 (PORTA-3)
pinMode(26, INPUT); // Encoder bit 8 (PORTA-4)
pinMode(27, INPUT); // Encoder bit 9 (PORTA-5)
pinMode(28, INPUT); // Encoder bit 10 (PORTA-6)
pinMode(29, INPUT); // Encoder bit 11 (PORTA-7)
pinMode(30, OUTPUT); // Set PORTC pin 8 to output (stop pin float)
pinMode(31, OUTPUT); // Set PORTC pin 7 to output (stop pin float)
pinMode(32, OUTPUT); // Set PORTC pin 6 to output (stop pin float)
pinMode(33, OUTPUT); // Set PORTC pin 5 to output (stop pin float)
digitalWrite(30, LOW); // Set PORTC pin 8 low (stop pin float)
digitalWrite(31, LOW); // Set PORTC pin 7 low (stop pin float)
digitalWrite(32, LOW); // Set PORTC pin 6 low (stop pin float)
digitalWrite(33, LOW); // Set PORTC pin 5 low (stop pin float)
pinMode(53, OUTPUT); // Set pin 53 (PORTB-0) as digital output
digitalWrite(53, LOW); // Set pin 53 (PORTB-0) LOW
}
void loop()
{
// Cam Angle Update
PrevCamAngle = CamAngle; // Set variable to previous encoder value
CamAngle = (PINA << 4) + PINC; // Read encoder, set variable to value
if (TDCycle == true && CamAngle >= TDC[i]) // Wait for encoder to reach angle if cycle active
{
PORTB = 0; // Set digital pin 53 LOW
TDCycle = false;
BDCycle = true;
}
if (BDCycle == true && CamAngle >= BDC[i]) // Wait for encoder to reach angle if cycle active
{
PORTB = 1; // Set digital pin 53 HIGH
TDCycle = false;
BDCycle = true;
i++;
if (i > 2) // Reset every 3 cycles (3 cycles per revolution)
{
i = 0;
BDCycle = false;
Cycle = true;
}
}
if (Cycle == true && CamAngle < (PrevCamAngle + 1)) // Wait for encoder to cycle back to 0
{
PORTB = 0; // Set digital pin 53 LOW
BDCycle = true;
Cycle = false;
}
}
As you can see I am utilizing direct port manipulation, however I have the same issues when I use the Arduino library commands as well.
As it turned out, the issue was the internal protection diodes in the Arduino. All the pins have internal protection diodes connected to ground and the 5V rail. So when power is disconnected, the 5V rail drops down to ground (or it should), however if you have power coming in from the IO pins, current will start to flow in through the protection diodes connected to the 5V rail and begin to power your Arduino.
This looks like it is a question better suited for https://arduino.stackexchange.com/ or https://electronics.stackexchange.com/. However, based on the info that you've given, my first thought is that this is not so much a coding issue as it is a problem with your connections - particularly when you can see the issue with no power applied to the Arduino.
Take a look at the datasheet for your encoder and make sure that you are not connecting an output to the output of the Arduino. There may also be a requirement for an external pull-down resistor. Finally, try a different pin - I have had Arduino pins that have died from exceeding the current ratings (whoops!).
Hope that helps!
Related
I am currently working on a low-power project using the Adafruit Feather M0 microprocessor. A requirement of my project is to be able to sleep the CPU and wake it again using an external interrupt triggered from the MPU6050 accelerometer.
I have tested the following code sample from GitHub - it works successfully! The question that I need answering is how to I alter this sample code to work on Pin 13 of the feather, rather than pin 6.
#define interruptPin 6
volatile bool SLEEP_FLAG;
void EIC_ISR(void) {
SLEEP_FLAG ^= true; // toggle SLEEP_FLAG by XORing it against true
//Serial.print("EIC_ISR SLEEP_FLAG = ");
//Serial.println(SLEEP_FLAG);
}
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
delay(3000); // wait for console opening
attachInterrupt(digitalPinToInterrupt(interruptPin), EIC_ISR, CHANGE); // Attach interrupt to pin 6 with an ISR and when the pin state CHANGEs
SYSCTRL->XOSC32K.reg |= (SYSCTRL_XOSC32K_RUNSTDBY | SYSCTRL_XOSC32K_ONDEMAND); // set external 32k oscillator to run when idle or sleep mode is chosen
REG_GCLK_CLKCTRL |= GCLK_CLKCTRL_ID(GCM_EIC) | // generic clock multiplexer id for the external interrupt controller
GCLK_CLKCTRL_GEN_GCLK1 | // generic clock 1 which is xosc32k
GCLK_CLKCTRL_CLKEN; // enable it
while (GCLK->STATUS.bit.SYNCBUSY); // write protected, wait for sync
EIC->WAKEUP.reg |= EIC_WAKEUP_WAKEUPEN4; // Set External Interrupt Controller to use channel 4 (pin 6)
PM->SLEEP.reg |= PM_SLEEP_IDLE_CPU; // Enable Idle0 mode - sleep CPU clock only
//PM->SLEEP.reg |= PM_SLEEP_IDLE_AHB; // Idle1 - sleep CPU and AHB clocks
//PM->SLEEP.reg |= PM_SLEEP_IDLE_APB; // Idle2 - sleep CPU, AHB, and APB clocks
// It is either Idle mode or Standby mode, not both.
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; // Enable Standby or "deep sleep" mode
SLEEP_FLAG = false; // begin awake
// Built-in LED set to output and high
PORT->Group[g_APinDescription[LED_BUILTIN].ulPort].DIRSET.reg = (uint32_t)(1<<g_APinDescription[LED_BUILTIN].ulPin); // set pin direction to output
PORT->Group[g_APinDescription[LED_BUILTIN].ulPort].OUTSET.reg = (uint32_t)(1<<g_APinDescription[LED_BUILTIN].ulPin); // set pin mode to high
Serial.println("Setup() Run!");
}
void loop() {
// put your main code here, to run repeatedly:
if (SLEEP_FLAG == true) {
PORT->Group[g_APinDescription[LED_BUILTIN].ulPort].OUTCLR.reg = (uint32_t)(1<<g_APinDescription[LED_BUILTIN].ulPin); // set pin mode to low
Serial.println("I'm going to sleep now.");
__WFI(); // wake from interrupt
SLEEP_FLAG = false;
Serial.println("Ok, I'm awake");
Serial.println();
}
//Serial.print("SLEEP_FLAG = ");
//Serial.println(SLEEP_FLAG);
PORT->Group[g_APinDescription[LED_BUILTIN].ulPort].OUTTGL.reg = (uint32_t)(1<<g_APinDescription[LED_BUILTIN].ulPin); // toggle output of built-in LED pin
delay(1000);
}
As per the pinout diagram and Atmel datasheet, I am struggling to work out which changes to make to allow pin 13 to operate in the same way as pin 6.
Atmel Datasheet
The obvious solution is to change the following lines...
#define interruptPin 13
EIC->WAKEUP.reg |= EIC_WAKEUP_WAKEUPEN1; // Set External Interrupt Controller to use channel 4 (pin 6)
I suspected channel 1 (WAKEUPEN1) due to the ENINT^1 next to pin 13 on the pinout diagram. But this didn't work, the code pin operation did not exhibit the same behaviour as the pin 6 setup.
I would be very grateful for any suggestion of how to implement this code working on Pin 13. Many thanks for your support.
I'm not an authority here, and your code looks correct to me.
Except, the pin out shows Pin 13 is the built-in LED line, and you manipulate LED_BUILTIN several places in your code. That's almost certainly conflicting with your attempts to use 13 as an interrupt line.
I am working on using Raspberry Pi to generate hardware PWM to control my stepper motors. When I tried hardware pwm, I found that I can not control the pulse in numbers such as I just wanted motor to move 8000 steps and then stop.
I don't want use time to turn off the PWM, I want specific steps.
#include <bcm2835.h>
#include <stdio.h>
// PWM output on RPi Plug P1 pin 12 (which is GPIO pin 18)
// in alt fun 5.
// Note that this is the _only_ PWM pin available on the RPi IO headers
#define PIN RPI_GPIO_P1_12
// and it is controlled by PWM channel 0
#define PWM_CHANNEL 0
// This controls the max range of the PWM signal
#define RANGE 1024
int main(int argc, char **argv)
{
if (!bcm2835_init())
return 1;
// Set the output pin to Alt Fun 5, to allow PWM channel 0 to be output there
bcm2835_gpio_fsel(PIN, BCM2835_GPIO_FSEL_ALT5);
// Clock divider is set to 16.
// With a divider of 16 and a RANGE of 1024, in MARKSPACE mode,
// the pulse repetition frequency will be
// 1.2MHz/1024 = 1171.875Hz, suitable for driving a DC motor with PWM
bcm2835_pwm_set_clock(BCM2835_PWM_CLOCK_DIVIDER_16);
bcm2835_pwm_set_mode(PWM_CHANNEL, 1, 1); //CTL reg
bcm2835_pwm_set_range(PWM_CHANNEL, RANGE); //RNG1/2 reg
// Vary the PWM m/s ratio between 1/RANGE and (RANGE-1)/RANGE
int direction = 1;
int data = 1;
while (1)
{
if (data == 1)
direction = 1;
else if (data == RANGE-1)
direction = -1;
data += direction;
bcm2835_pwm_set_data(PWM_CHANNEL, data);
bcm2835_delay(1);
}
bcm2835_close();
return 0;
}
Can this code achieve my thought (modifying some registers of pwm)? Which reg of PWM control the autofill(autopadding), I just want to know how to stop sending pulses after stopping the motor.
I have a simple question about how to read rotary encoder input.
If I understand this image correctly, then every turn triggers a rise on pin A. Then, you have to check pin B, which is high if the encoder is turning clockwise and low if the encoder is turning counter clockwise.
I've tried to write my own code and not using any libraries, because I thought this would be really simple, but it turned out it was not.
This is the code I've written:
#define rotary_A 2
#define rotary_B 3
void setup()
{
pinMode(rotary_A, INPUT);
pinMode(rotary_B, INPUT);
attachInterrupt(digitalPinToInterrupt(rotary_A), rotary_spin, RISING);
Serial.begin(9600);
}
void rotary_spin()
{
if (digitalRead(rotary_B) == HIGH)
Serial.println("+");
else
Serial.println("-");
}
I was expecting to get + when I turn it clockwise and - when I turn it counter clockwise. However, I'm getting several outputs for each turn, like there were several interrupts triggered in rapid succession. For example, when I turn the encoder clockwise:
-
-
+
+
and counter clockwise:
+
+
-
-
-
-
The outputs are different every time, but the last character is always the right one.
What am I getting wrong? Is it not that simple or are there different types of encoders?
The question implies that there should only be a single interrupt per revolution. But encoders typically generate more than a single cycle per revolution--some in the thousands. That is probably why the interrupts seem to occur more rapidly than expected.
In a zero-latency interrupt environment, the code should work. But if the phase B pin is sampled too long after the phase A pin goes high, it will fail. This might occur if the next phase A rising edge occurs while Serial.println is still executing in the previous interrupt.
A simple test to see if this is the case is to turn the encoder very, very slowly. If this results in correct data, the problem is probably interrupt latency. Serial.println can then be replaced with something much quicker, like illuminating LEDs, to see if that resolves latency issues.
For actual use, you would need to make sure that the worse-case latency between phase A going high and phase B being sampled is adequate for the maximum rate of encoder rotation.
Final note: The code should be able to adequately detect direction, but cannot be used to increment/decrement a counter to track position. That requires more than one interrupt per cycle.
check this repository of mine on Github.
https://github.com/KingZuluKing/Rotary-Encoder-Peripheral-System
It has to be something like this, more details inside the repo.
void encode_Rotor_func()
{
lcd.clear();
Vol_Knob_stat=digitalRead(Vol_Knob);
if(Vol_Knob_stat==0)
{
Vol_Dir_stat=digitalRead(Vol_Dir);
if(Vol_Dir_stat==0 && deciBells<=9)
{
deciBells++;
lcd.setCursor(0, 0);
lcd.print("VOLUME= .");
lcd.setCursor(7, 0);
lcd.print(deciBells);
lcd.setCursor(10, 0);
lcd.print("dB's");
}
else if(Vol_Dir_stat==1 && deciBells >=-79)
{
deciBells--;
lcd.setCursor(0, 0);
lcd.print("VOLUME= .");
lcd.setCursor(7, 0);
lcd.print(deciBells);
lcd.setCursor(10, 0);
lcd.print("dB's");
}
else{
do something else etc.. etc....
}
}
}
I made a small sketch that makes clear how to interpret the encoder accordingly to the image sequence in your post:
Try this code with the equivalent pins CLK and DT connected to A2 and A1 receptively.
// Rotary Encoder Inputs
#define CLK A2
#define DT A1
int counter = 0;
String currentDir ="";
unsigned char currentPairBit = 0b0; // 8 bits
unsigned char lastPairBit = 0b0; // 8 bits
void setup() {
Serial.begin(9600); // opens serial port, sets data rate to 9600 bps
DDRC = 0b00000000; // Set Analog(C) encoder pins as inputs
}
void loop() {
while(true) { // while cycle is faster than loop!
// reads the analog states!
currentPairBit = PINC >> 1 & 0b11; // Gets A2(PC2) and A1(PC1) bits on C (analog input pins) (>> 1 = Jumps pin 0)
if ((lastPairBit & 0b11) != currentPairBit) {
lastPairBit = lastPairBit << 2 | currentPairBit;
// Bit Pairs Cyclic Sequence:
// 1. 2. 3. 4. 5.
// 11 | 01 | 00 | 10 | 11 for CCW
// 11 | 10 | 00 | 01 | 11 for CW
if (lastPairBit == 0b01001011 || lastPairBit == 0b10000111) {
if (lastPairBit == 0b01001011) {
currentDir = "CCW";
counter--;
} else {
currentDir = "CW";
counter++;
}
Serial.print("Direction: ");
Serial.print(currentDir);
Serial.print(" | Counter: ");
Serial.println(counter);
}
}
}
}
Then check the Serial Monitor to see how fast it is. Note that you should avoid the delay() function in your code or any other that interrupts the cycle by too much time. Consider using a second auxiliary Arduino for anything else than counting.
Resulting Serial Output:
I recently got a HC-05 Bluetooth module for my arduino, but I cannot send or receive data from it. I used a code to turn on or off a led, but after I send a character from the Serial Monitor of my PC, I get ⸮. Also the module does not respond to any AT command. HC-05 ConnectionArduino connection I ran the Serial both in 9600 and 38400 baud but nothing changed. Also I have tried both no line ending and both NL and CR. But is wrong with this module? Here is my code:
/*
Arduino Turn LED On/Off using Serial Commands
Created April 22, 2015
Hammad Tariq, Incubator (Pakistan)
It's a simple sketch which waits for a character on serial
and in case of a desirable character, it turns an LED on/off.
Possible string values:
a (to turn the LED on)
b (tor turn the LED off)
*/
char junk;
String inputString="";
void setup() // run once, when the sketch starts
{
Serial.begin(9600); // set the baud rate to 9600, same should be of your Serial Monitor
pinMode(13, OUTPUT);
}
void loop()
{
if(Serial.available()){
while(Serial.available())
{
char inChar = (char)Serial.read(); //read the input
inputString += inChar; //make a string of the characters coming on serial
}
Serial.println(inputString);
while (Serial.available() > 0)
{ junk = Serial.read() ; } // clear the serial buffer
if(inputString == "a"){ //in case of 'a' turn the LED on
digitalWrite(13, HIGH);
}else if(inputString == "b"){ //incase of 'b' turn the LED off
digitalWrite(13, LOW);
}
inputString = "";
}
}
I will go step by step-
The connection
Arduino Pins Bluetooth Pins
RX (Pin 0) ———-> TX
TX (Pin 1) ———-> RX
5V ———-> VCC
GND ———-> GND
Connect a LED negative to GND of arduino and positive to pin 13 with a resistance valued between 220Ω – 1KΩ. And your done with the circuit.
Note : Don’t Connect RX to RX and TX to TX of Bluetooth to arduinoyou will receive no data , Here TX means Transmit and RX means Receive.
/*
* This program lets you to control a LED on pin 13 of arduino using a bluetooth module
*/
char data = 0; //Variable for storing received data
void setup()
{
Serial.begin(9600); //Sets the baud for serial data transmission
pinMode(13, OUTPUT); //Sets digital pin 13 as output pin
}
void loop()
{
if(Serial.available() > 0) // Send data only when you receive data:
{
data = Serial.read(); //Read the incoming data & store into data
Serial.print(data); //Print Value inside data in Serial monitor
Serial.print("\n");
if(data == '1') // Checks whether value of data is equal to 1
digitalWrite(13, HIGH); //If value is 1 then LED turns ON
else if(data == '0') // Checks whether value of data is equal to 0
digitalWrite(13, LOW); //If value is 0 then LED turns OFF
}
}
Link to Connection : https://halckemy.s3.amazonaws.com/uploads/image_file/file/153200/hc-05-LED%20blink%20Circuit.png
NOTE : While uploading the code , remove the TX and RX wires of Bluetooth Module from Arduino, once upload is completed, connect them.
#include <SoftwareSerial.h>
SoftwareSerial hc(2, 3); // RX | TX
void setup()
{
pinMode(4, OUTPUT);
digitalWrite(4, HIGH);
Serial.begin(9600);
Serial.println("Enter AT commands:");
hc.begin(38400); // HC-05 default speed in AT command more
}
void loop()
{
// Keep reading from HC-05 and send to Arduino Serial Monitor
if (hc.available())
Serial.write(hc.read());
// Keep reading from Arduino Serial Monitor and send to HC-05
if (Serial.available())
hc.write(Serial.read());
}
Use this code to test the bluetooth module in command mode.there are two modes in hc-05. one is command mode and other is data mode.
Press the button which is on bluetooth module for few sec. then the led Toggles slowly at this point the module is in command mode and in this you can test the AT commands.
Note: Open the serial monitor in 9600 baud rate
I am using Arduino UNO. The following code is working on PIN 10 but not on PIN 6. Can anyone pls suggest a reason. I know that each pair of PWM pins use different Timers but I havent done anything to any timers!!
Here's the code.
/*
Fade
This example shows how to fade an LED on pin 9
using the analogWrite() function.
This example code is in the public domain.
*/
int led = 10; // the pin that the LED is attached to
int brightness = 0; // how bright the LED is
int fadeAmount = 5; // how many points to fade the LED by
// the setup routine runs once when you press reset:
void setup() {
// declare pin 9 to be an output:
pinMode(led, OUTPUT);
// Serial.begin(9600); //for debugging
}
// the loop routine runs over and over again forever:
void loop() {
// set the brightness of pin 9:
analogWrite(led, 100);
// change the brightness for next time through the loop:
brightness = brightness + fadeAmount;
//Serial.println(brightness);//for testing purposes
// reverse the direction of the fading at the ends of the fade:
if (brightness == 0 || brightness == 255) {
fadeAmount = -fadeAmount ;
}
// wait for 30 milliseconds to see the dimming effect
delay(30);
}
Simply changing the led = 10 line to led = 6 causes the code to stop working, i.e. the output on PIN 6 becomes zero rather than the intended PWM wave.
Any suggestions would be appreciated.