Servo Motor not moving properly in Proteus - c

I'm trying to control a Servo Motor with a PIC18f4550, but before buying one, I'm trying to simulate it on Proteus ISIS, but I'm getting some inconsistencies when setting the angle.
I've tried using a 20ms period and 1ms, 1.167ms, 1.333ms, 1.5ms, 1.667ms or 1.833ms duty cycle and it results in a -69.8° Angle on Proteus MOTOR-PWMSERVO, but using a 2ms duty cycle results in a full 90.0° Angle
#define CONTROL PORTCbits.RC0
#define BUTTON PORTCbits.RC1
const Ang_Neg90 = 1.0;
const Ang_Neg60 = 1.167;
const Ang_Neg30 = 1.333;
const Ang_0 = 1.5;
const Ang_30 = 1.667;
const Ang_60 = 1.833;
const Ang_90 = 2.00;
// ------------------------------------------------- //
void ServoPosition (unsigned float Angle) {
CONTROL = 1;
Delay_ms(Angle);
CONTROL = 0;
Delay_ms(20 - Angle);
}
void main(){
while(1){
ServoPosition(Ang_0);
if (BUTTON == 1){
break;
while(1){
ServoPosition(Ang_90);
}
}
}
}
I want my program to be able to go from 0 degrees to 90 degrees, but all I'm getting is -69.8 to 90. Any ideas of what I'm getting wrong?

It's hard to tell without seeing your entire setup, so here's a list of things it might be.
Check Simulated Servo Settings
Make sure it's set to the default values.
Measure your PWM signal with the Oscilloscope Tool
The Hobby Servo Motor sample shows how to use the oscilloscope tool. Hook the oscilloscope up to your pic18f4550 PWM output and measure it to make sure the duty cycle is correct. You're having problems at the low end (trying to reach 1ms) so I suspect the oscilloscope will show your duty cycle is too long.
If you find that your duty cycles are too long, you can try:
Subtracting a fudge value to deal with whatever falloff time is present in the simulation. You might have to use different values when you run on real hardware.
Using a dedicated PWM module on the pic18f4550. This is probably the best solution since you can just set the PWM registers (assuming this is how it works... I haven't looked closely at the manual) and then let it control the duty cycle for you.
Check Voltages
The battery in the Hobby Server was 6V, but 5V also seemed to work. Lower than that and it began to act strange.

The type of your constant is wrong:
const Ang_Neg90 = 1.0;
This is a constant of type int with the value 1.
I guess you mean:
const float Ang_Neg90 = 1.0;
But anyway:
If you use the Microchip built in delay function the argument should be of the type unsigned long and you are working with unsigned float.
Try to define your delay times in us (integer not float values!) and use the function __delay_us(...). And be sure to define _XTAL_FREQ correctly.
And as already mentioned: Please remember there don't exist a type unsigned float. float is always unsigned.

Related

Control a GPIO pin within 150ns of tolerance

My problem is that turning on and off my GPIO pin takes way too long, despite using good timekeeping functionality, including both ndelay from linux/delay.h and my own accurate_ndelay which (shown below) uses ktime_get_ns() from linux/ktime.h.
My kernel version is 4.19.38 with Armbian, running on an OrangePi Zero.
static inline void accurate_ndelay(uint16_t ns){
uint64_t s = ktime_get_ns();
uint64_t e = s + ns;
while(ktime_get_ns() < e);
}
static inline void unsafe_bit2812(struct WS2812* ws2812, uint8_t b){
if(b){
gpio_set_value(ws2812->pin, 1);
accurate_ndelay(ws2812->t0h);
gpio_set_value(ws2812->pin, 0);
accurate_ndelay(ws2812->t0l);
} else {
gpio_set_value(ws2812->pin, 1);
accurate_ndelay(ws2812->t1h);
gpio_set_value(ws2812->pin, 0);
accurate_ndelay(ws2812->t1l);
}
}
When I measure the real-world delay (as shown by my oscilloscope, not bad software). The delay is not the expected 350ns, but 920ns. Which for the WS2812 is 770ns too much!
That's some pretty tight timing. The OrangePi Zero run at 1.2 GHz, so 150 ns is 180 clock cycles. That doesn't give you time to do much.
The first thing to do is use ktime_get_ns() to just measure how long the gpio_set_value() call takes. Or remove the delay and measure it with the scope. You might know the answer already, if you delayed for 350ns and measured 920ns, then it takes about 600ns.
You're calling gpio_set_value(), which pulls in the safe, portable Linux gpio library. The maximum possible performance would be to write your own driver for the GPIO that goes right to the HW registers and sets the two states, with the delay, as a single action.
Even with a custom driver, you'll have delays introduced by the clock driving the gpio and the rise and fall times of the device.

Average from error prone measurement samples without buffering

I got a µC which measures temperature with of a sensor with an ADC. Due to various circumstances it can happen, that the reading is 0 (-30°C) or a impossible large Value (500-1500°C). I can't fix the reasons why these readings are so bad (time critical ISRs and sometimes a bad wiring) so I have to fix it with a clever piece of code.
I've come up with this (code gets called OVERSAMPLENR-times in a ISR):
#define OVERSAMPLENR 16 //read value 16 times
#define TEMP_VALID_CHANGE 0.15 //15% change in reading is possible
//float raw_tem_bed_value = <sum of all readings>;
//ADC = <AVR ADC reading macro>;
if(temp_count > 1) { //temp_count = amount of samples read, gets increased elsewhere
float avgRaw = raw_temp_bed_value / temp_count;
float diff = (avgRaw > ADC ? avgRaw - ADC : ADC - avgRaw) / (avgRaw == 0 ? 1 : avgRaw); //pulled out to shorten the line for SO
if (diff > TEMP_VALID_CHANGE * ((OVERSAMPLENR - temp_count) / OVERSAMPLENR)) //subsequent readings have a smaller tollerance
raw_temp_bed_value += avgRaw;
else
raw_temp_bed_value += ADC;
} else {
raw_temp_bed_value = ADC;
}
Where raw_temp_bed_value is a static global and gets read and processed later, when the ISR got fired 16 times.
As you can see, I check if the difference between the current average and the new reading is less then 15%. If so I accept the reading, if not, I reject it and add the current average instead.
But this breaks horribly if the first reading is something impossible.
One solution I though of is:
In the last line the raw_temp_bed_value is reset to the first ADC reading. It would be better to reset this to raw_temp_bed_value/OVERSAMPLENR. So I don't run in a "first reading error".
Do you have any better solutions? I though of some solutions featuring a moving average and use the average of the moving average but this would require additional arrays/RAM/cycles which we want to prevent.
I've often used something what I call rate of change to the sampling. Use a variable that represents how many samples it takes to reach a certain value, like 20. Then keep adding your sample difference to a variable divided by the rate of change. You can still use a threshold to filter out unlikely values.
float RateOfChange = 20;
float PreviousAdcValue = 0;
float filtered = FILTER_PRESET;
while(1)
{
//isr gets adc value here
filtered = filtered + ((AdcValue - PreviousAdcValue)/RateOfChange);
PreviousAdcValue = AdcValue;
sleep();
}
Please note that this isn't exactly like a low pass filter, it responds quicker and the last value added has the most significance. But it will not change much if a single value shoots out too much, depending on the rate of change.
You can also preset the filtered value to something sensible. This prevents wild startup behavior.
It takes up to RateOfChange samples to reach a stable value. You may want to make sure the filtered value isn't used before that by using a counter to count the number of samples taken for example. If the counter is lower than RateOfChange, skip processing temperature control.
For a more advanced (temperature) control routine, I highly recommend looking into PID control loops. These add a plethora of functionality to get a fast, stable response and keep something at a certain temperature efficiently and keep oscillations to a minimum. I've used the one used in the Marlin firmware in my own projects and works quite well.

CodeWarrior Get variable value from a event.c

to enter context I'm doing this:
Drive a stepper motor through the variation of pulse frequency input to a Driver (A4988) (It is not necessary to know the functioning of this for this question). Now and got varying the frequency of the pulses (They change the engine speed). You need to know that for the motor shaft 1 full turn have to get 200 pulses (The engine is 1.8 ° degrees per step).
I got the engine and make him a full turn in 1 second.
Period = 0.005s
To program this I am using the component: TimerUnit_LDD.
With a frequency of 163840 Hz count
In the case of the whole turn 1 to get that frequently use this function.
---- main.c
TU1_Enable (TU1_DeviceData);
 TU1_SetPeriodTicks (TU1_DeviceData, 410);
  
The parameter 410 is compared to the period I want, as is sending pulses programmed by changing the value of a pin taken into account both the high and the low pulse, like this:
----- Events.c
TU1_OnCounterRestart void (* UserDataPtr LDD_TUserData)
{
 Step1_NegVal ();
 }
The period for serious formulates 819.2, having in mind the above serious approximates 409.6 and 410 (seen in a oscilloscope frequency is 200 Hz (ok).
Already entered in context the problem is this:
---- main.c
TU1_Enable (TU1_DeviceData); // Enable the counter
TU1_SetPeriodTicks (TU1_DeviceData, 410); // Setting the desired period
for (;;) {
           TU1_Enable (TU1_DeviceData);
           WAIT1_Waitms (1000); // Rotation time
 TU1_Enable (TU1_DeviceData); // Disable the counter
}
With this code is what I try to check that the frequency calculation was correct and that in one second would 1 turn. But what happens is that it gives the rotation but is offset a little more. I guess this goes through the runtime required for each line of code.
What I want to know is, how could obtain the numerical value of a variable in an event? how could I do something like this.
---- main.c
TU1_Enable (TU1_DeviceData); // Initialize the counter
TU1_SetPeriodTicks (TU1_DeviceData, 410); // Setting the desired period
for (;;) {
for (;;) {
      if (GetValue (x) == 200) break; // GetValue (x) This function is what I want to achieve
}
WAIT1_Waitms (1000);
}
----- Events.c
TU1_OnCounterRestart void (* UserDataPtr LDD_TUserData)
{
 Step1_NegVal ();
x = x + 1;
 }
GetValue (x) this function would obtain the value of x which is in Events.c and define a number of pulses to control espefico.
Take a variable and is affected by the counter, and that this gets to 200 (for 1 turn in 1 second).
This would have the certainty that menera be sent alone and lonely, neither more nor less, only 200 pulses.
I require this as specific as I am desarrolando the program for a CNC machine and is too importanto precision is the highest.
I hope you understand and I speak Spanish and this was translated by Chrome
Programmed in C language,
Freescale KL25Z,
CodeWarrior,
OPEN_SDA,
I managed to implement something but I think it may be easier to get
-----(main.c)
extern int count;//called external variable
int main(void){
PE_low_level_init();
TU1_Enable(TU1_DeviceData);
TU1_SetPeriodTicks(TU1_DeviceData,410);//T=0.005 sec
for(;;){
Term1_Cls();// Clear Console
WAIT1_Waitms(1000);
Term1_MoveTo(0,0);// Set 0,0 in Console
for(;;){
TU1_Enable(TU1_DeviceData);
Term1_SendNum(count);
Term1_CRLF();
if (count>400){//amount of high and low pulse counting
count=0;
TU1_Disable(TU1_DeviceData);
break;
}
}
WAIT1_Waitms(1000);
Dir1_NegVal();
}
----(Events.c)
int count;
void TU1_OnCounterRestart(LDD_TUserData *UserDataPtr)
{
Step1_NegVal();
count=count+1; //counter
}

Audio tone for Microcontroller p18f4520 using a for loop

I'm using C Programming to program an audio tone for the microcontroller P18F4520.
I am using a for loop and delays to do this. I have not learned any other ways to do so and moreover it is a must for me to use a for loop and delay to generate an audio tone for the target board. The port for the speaker is at RA4. This is what I have done so far.
#include <p18f4520.h>
#include <delays.h>
void tone (float, int);
void main()
{
ADCON1 = 0x0F;
TRISA = 0b11101111;
/*tone(38.17, 262); //C (1)
tone(34.01, 294); //D (2)
tone(30.3, 330); //E (3)
tone(28.57, 350); //F (4)
tone(25.51, 392); //G (5)
tone(24.04, 416); //G^(6)
tone(20.41, 490); //B (7)
tone(11.36, 880); //A (8)*/
tone(11.36, 880); //A (8)
}
void tone(float n, int cycles)
{
unsigned int i;
for(i=0; i<cycles; i++)
{
PORTAbits.RA4 = 0;
Delay10TCYx(n);
PORTAbits.RA4 = 1;
Delay10TCYx(n);
}
}
So as you can see what I have done is that I have created a tone function whereby n is for the delay and cycles is for the number of cycles in the for loop. I am not that sure whether my calculations are correct but so far it is what I have done and it does produce a tone. I'm just not sure whether it is really a A tone or G tone etc. How I calculate is that firstly I will find out the frequency tone for example A tone has a frequency of 440Hz. Then I will find the period for it whereby it will be 1/440Hz. Then for a duty cycle, I would like the tone to beep only for half of it which is 50% so I will divide the period by 2 which is (1/440Hz)/2 = 0.001136s or 1.136ms. Then I will calculate delay for 1 cycle for the microcontroller 4*(1/2MHz) which is 2µs. So this means that for 1 cycle it will delay for 2µs, the ratio would be 2µs:1cyc. So in order to get the number of cycles for 1.136ms it will be 1.136ms:1.136ms/2µs which is 568 cycles. Currently at this part I have asked around what should be in n where n is in Delay10TCYx(n). What I have gotten is that just multiply 10 for 11.36 and for a tone A it will be Delay10TCYx(11.36). As for the cycles I would like to delay for 1 second so 1/1.136ms which is 880. That's why in my method for tone A it is tone(11.36, 880). It generates a tone and I have found out the range of tones but I'm not really sure if they are really tones C D E F G G^ B A.
So my questions are
1. How do I really calculate the delay and frequency for tone A?
2. for the state of the for loop for the 'cycles' is the number cycles but from the answer that I will get from question 1, how many cycles should I use in order to vary the period of time for tone A? More number of cycles will be longer periods of tone A? If so, how do I find out how long?
3. When I use a function to play the tone, it somehow generates a different tone compared to when I used the for loop directly in the main method. Why is this so?
4. Lastly, if I want to stop the code, how do I do it? I've tried using a for loop but it doesn't work.
A simple explanation would be great as I am just a student working on a project to produce tones using a for loop and delays. I've searched else where whereby people use different stuff like WAV or things like that but I would just simply like to know how to use a for loop and delay for audio tones.
Your help would be greatly appreciated.
First, you need to understand the general approach to how you generate an interrupt at an arbitrary time interval. This lets you know you are able to have a specific action occur every x microseconds, [1-2].
There are already existing projects that play a specific tone (in Hz) on a PIC like what you are trying to do, [3-4].
Next, you'll want to take an alternate approach to generating the tone. In the case of your delay functions, you are effectively using up the CPU for nothing, when it could be done something else. You would be better off using timer interrupts directly so you're not "burning the CPU by idling".
Once you have this implemented, you just need to know the corresponding frequency for the note you are trying to generate, either by using a formula to generate a frequency from a specific musical note [5], or using a look-up table [6].
What is the procedure for PIC timer0 to generate a 1 ms or 1 sec interrupt?, Accessed 2014-07-05, <http://www.edaboard.com/thread52636.html>
Introduction to PIC18′s Timers – PIC Microcontroller Tutorial, Accessed 2014-07-05, <http://extremeelectronics.co.in/microchip-pic-tutorials/introduction-to-pic18s-timers-pic-microcontroller-tutorial/>
Generate Ring Tones on your PIC16F87x Microcontroller, Accessed 2014-07-05, <http://retired.beyondlogic.org/pic/ringtones.htm>http://retired.beyondlogic.org/pic/ringtones.htm
AN655 - D/A Conversion Using PWM and R-2R Ladders to Generate Sine and DTMF Waveforms, Accessed 2014-07-05, <http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=1824&appnote=en011071>
Equations for the Frequency Table, Accessed 2014-07-05, <http://www.phy.mtu.edu/~suits/NoteFreqCalcs.html>
Frequencies for equal-tempered scale, A4 = 440 Hz, Accessed 2014-07-05, <http://www.phy.mtu.edu/~suits/notefreqs.html>
How to compute the number of cycles for delays to get a tone of 440Hz ? I will assume that your clock speed is 1/2MHz or 500kHz, as written in your question.
1) A 500kHz clock speed corresponds to a tic every 2us. Since a cycle is 4 clock tics, a cycle lasts 8 us.
2) A frequency of 440Hz corresponds to a period of 2.27ms, or 2270us, or 283 cycles.
3) The delay is called twice per period, so the delays should be about 141 cycles for A.
About your tone function...As you compile your code, you must face some kind of warning, something like warning: (42) implicit conversion of float to integer... The prototype of the delay function is void Delay10TCYx(unsigned char); : it expects an unsigned char, not a float. You will not get any floating point precision. You may try something like :
void tone(unsigned char n, unsigned int cycles)
{
unsigned int i;
for(i=0; i<cycles; i++)
{
PORTAbits.RA4 = 0;
Delay1TCYx(n);
PORTAbits.RA4 = 1;
Delay1TCYx(n);
}
}
I changed for Delay1TCYx() for accuracy. Now, A 1 second A-tone would be tone(141,440). A 1 second 880Hz-tone would be tone(70,880).
There is always a while(1) is all examples about PIC...if you just need one beep at start, do something like :
void main()
{
ADCON1 = 0x0F;
TRISA = 0b11101111;
tone(141,440);
tone(70,880);
tone(141,440);
while(1){
}
}
Regarding the change of tone when embedded in a function, keep in mind that every operation takes at least one cycle. Calling a function may take a few cycles. Maybe declaring static inline void tone (unsigned char, int) would be a good thing...
However, as signaled by #Dogbert , using delays.h is a good start for beginners, but do not get used to it ! Microcontrollers have lots of features to avoid counting and to save some time for useful computations.
timers : think of it as an alarm clock. 18f4520 has 4 of them.
interruption : the PIC stops the current operation, performs the code specified for this interruption, erases the flag and comes back to its previous task. A timer can trigger an interruption.
PWM pulse wave modulation. 18f4520 has 2 of them. Basically, it generates your signal !

PIC24H POT input to control LED blink delay not linear

Hello I'm using the PIC24H microprocessor and I wrote a simple program that takes input from a POT using analog input which is then set to a modulus value of delay. It does seem to set the delay, but progression from left to right is seemingly random and inconsistent. Help would be awesome! Thank you!
int main (void){
AD1CON1bits.ADON=0;
AD1CON1=0x00E0;
AD1CON1bits.AD12B=1;
AD1CON3=0x8000;
AD1CON2=0x8000;
AD1CHS0=0x0000;
AD1CON1bits.ADON=1;
int wtdState;
int delay;
int temp;
// Set Analog Input Pin
_CN2PUE=0;
_TRISA0=1;
_PCFG0=0;
//Set Digital Output Pins
_ODCB15=0;
_TRISB15=0;
_LATB15=0;
_ODCB14=0;
_TRISB14=0;
_LATB14=1;
while(1){
wtdState = _SWDTEN;
_SWDTEN=1;
AD1CON1bits.SAMP=1;
Nop();
while(!AD1CON1bits.DONE){}
_SWDTEN = wtdState;
temp = ADC1BUF0;
delay = temp%225+25;
__delay_ms(delay);
_LATB15=1;
__delay_ms(delay);
_LATB15=0;
}
}
it seems that by taking the mod you are making the noise significant, perhaps you should divide instead.

Resources