#include <xc.h>
#pragma config FOSC = HS
void main()
{
OSCCONbits.IRCF = 0b111;
OSCTUNE = 0b01111;
OSCCONbits.SCS = 0;
OSCCONbits.OSTS = 1;
OSCCONbits.HTS = 1;
OSCCONbits.LTS = 1;
TRISBbits.TRISA2 = 0;
ANSELbits.ANS2 = 1;
ADCON0 = 0x89;
ADCON1 = 0x00;
while(1)
{
ADCON0bits.GO = 1;
while(ADCON0bits.GO == 1)
{
PORTBbits.RB6 = ~(PORTBbits.RB6);
}
}
}
I am struggling to change the adc conversion.
When i am checking in the oscilloscope. i am getting the conversion time as 10 micro seconds. but it needs to take like 200 nsec.
MPLAB version 5.40
Pickit 3 programmer
XC8 compiler
The time to complete one bit conversion is defined as Tad. One full 10 bit conversion requires 11 Tad periods. The minimum Tad time is 1.6 us at 20 Mhz, check datasheet TABLE 17-11. If you are using 16 Mhz oscillator than the minimum Tad time is 2 us. So for full 10 bit conversion your ADC need 22 us. But ADC need also some time to charge holding capacitor, check datasheet section 9.2, which is about 2 us or more. As you can calculate the minimum ADC conversion time for PIC16F88x is about 25us at 16 Mhz microcontroller oscillator.
Related
I am interfacing a M90E32AS energy meter IC with dsPIC33F series processor. I successfully read voltage and current values. I try to read power values also, as per the datasheet the power registers are 32-bits wide. I tried the following code to read the 32 bit value but I am unsuccessful. Help me to rectify the error.
int PmB_read()
{
CS=0;
SPI2BUF=SBUF=0x80B2;
while(SPI2STATbits.SPITBF==1){}
SPI2BUF=0x0;
while(SPI2STATbits.SPITBF==1){}
delay();
CS=1;
HiByte = SPI2BUF;
return HiByte;
}
int PmBLSB_read()
{
CS=0;
SPI2BUF=SBUF=0x80C2;
while(SPI2STATbits.SPITBF==1){}
SPI2BUF=0x0;
while(SPI2STATbits.SPITBF==1){}
delay();
CS=1;
LoByte = SPI2BUF;
TPmB = (HiByte << 16)| LoByte;
Total = TPmB * 0.00032;
return Total;
}
Here is the data sheet screen shot for power register
I am working on a weird problem: As a part of my project, I migrated a firmware from CooCox to TrueStudio. Both, CooCox and TrueStudio automatically create some standard files while creating a project for a specific Microcontroller. The Microcontroller used here is the STM32F407VGT6. I am using ms - delay and s - delay which are derived from the µs - Delay function I will show you.
*edit2: I should mention, that the original project is a pure C project. I am trying to make the Project a C++/C project in TrueStudio.
What I will try now is to migrate the firmware into a TrueStudio pure C project and see if the problem still exists.
I will inform you about the results
**Results: The problem is actually gone now in the pure C Project, but I would really like to implement classes etc using C++. Any ideas how to solve this?
**
*
The initializing systick code is (HCLK Frequency = 168MHz).
*edit1: the HCLK Frequency equals the SYSCLK *
void systick_init(void){
RCC_ClocksTypeDef RCC_Clocks;
Systick_Delay=0;
RCC_GetClocksFreq(&RCC_Clocks);
SysTick_Config((RCC_Clocks.HCLK_Frequency / 1000000) - 1);
}
The function for the 1µs Delay looks like this:
void delay_us(volatile uint32_t delay)
{
Systick_Delay = delay;
while(Systick_Delay != 0);
}
The Systick Handler contains the following Code:
void SysTick_Handler(void)
{
// Tick für Delay
if(Systick_Delay != 0x00)
{
Systick_Delay--;
}
}
When I create a .hex file to flash the µC with using Coocox, the timing function works (with some minor accuracy mistakes that don't bother me).
When I create the .hex file with TrueStudio, the delays have massive inaccuracys. For example, a delay of 500ms becomes a delay of roughly 2s.
Since the Code is written dependant on the actual HCLK_Frequency, I can't understand the mistake and in my understanding, even if the HCLK should differ, the 1µs Delay should still take about 1µs.
My next step will be comparing the automatically created system files, but maybe anyone has a different approach / another idea?
*edit 3: I normally include my systick - header with the command ' extern "C" '. So my systick source file is a .c file. When I rename the file to systick.cpp, and I include the header without 'extern "C"', the delay function does not work at all. Maybe, that helps with the solution?
*
You are either running off a different clock or have different PLL settings. Looks like your clock speed is 1/4 of what you had before.
The basic startup code provided does not always set the maximum speed for the board. Have a look in some of the examples in the stm32cube.zip code. You will find some System Clock Configuration code for your board which will select the correct clock and pll settings. (this will be in your code somewhere as well).
Look in main.c under stm32cubef4/projects/STM32F4-Discovery\Demonstrations\src.
You will find the following code which sets up the clock:
/**
* #brief System Clock Configuration
* The system Clock is configured as follow :
* System Clock source = PLL (HSE)
* SYSCLK(Hz) = 168000000
* HCLK(Hz) = 168000000
* AHB Prescaler = 1
* APB1 Prescaler = 4
* APB2 Prescaler = 2
* HSE Frequency(Hz) = 8000000
* PLL_M = 8
* PLL_N = 336
* PLL_P = 2
* PLL_Q = 7
* VDD(V) = 3.3
* Main regulator output voltage = Scale1 mode
* Flash Latency(WS) = 5
* #param None
* #retval None
*/
static void SystemClock_Config(void)
{
RCC_ClkInitTypeDef RCC_ClkInitStruct;
RCC_OscInitTypeDef RCC_OscInitStruct;
/* Enable Power Control clock */
__HAL_RCC_PWR_CLK_ENABLE();
/* The voltage scaling allows optimizing the power consumption when the device is
clocked below the maximum system frequency, to update the voltage scaling value
regarding system frequency refer to product datasheet. */
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/* Enable HSE Oscillator and activate PLL with HSE as source */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 8;
RCC_OscInitStruct.PLL.PLLN = 336;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 7;
HAL_RCC_OscConfig(&RCC_OscInitStruct);
/* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2
clocks dividers */
RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5);
/* STM32F405x/407x/415x/417x Revision Z devices: prefetch is supported */
if (HAL_GetREVID() == 0x1001)
{
/* Enable the Flash prefetch */
__HAL_FLASH_PREFETCH_BUFFER_ENABLE();
}
}
I found the solution now!
For anyone having similar problems:
You need to declare the SysTick_Handler function as
extern "C" void SysTick_Handler(void)
{
//Tick für Delay
if(Systick_Delay != 0x00)
{
Systick_Delay--;
}
}
Now it is working like it is supposed to do.
I am working with the explorer16/32 evb and trying to send data to UART.
I tried UART1, UART2 with and without interrupts and got the same problem all the time.
It appeared that in order to send 1 byte I need to split it to two 4 bits with shift
code:
#define FCY 16000000
#define BAUDRATE 9600
#define BRGVAL ((FCY/BAUDRATE)/16)-1
U2MODE = 0;
U2STA = 0;
U2MODEbits.STSEL = 0; // 1-Stop bit
U2MODEbits.PDSEL = 0 ; // No Parity 8 bit data
U2MODEbits.ABAUD = 0; // Auto-Baud Disabled
U2MODEbits.BRGH = 1; // High Speed Mode
U2MODEbits.URXINV = 0;
U2STAbits.UTXINV = 0;
U2BRG = BRGVAL; // Baud Rate Setting for 9600
U2MODEbits.UARTEN = 1; //Enable UART module
U2STAbits.UTXEN = 1; //Enable UART TX
unsigned char putU2(unsigned char c)
{
while (U2STAbits.TRMT == 0)
{
}
while ( CTS);
while ( U2STAbits.UTXBF);
U2TXREG = c & 0xF;
U2TXREG = (c >> 4) & 0xF;
return c;
}
If I am sedning that data splited to 4 bits I can see the data correct on the PC( c# serial port application)
But it should not be split into two writes to U2TXREG.
I am not 100% sure that the FCY is 16000000 but this is the only value that give me reasonable result.
What could be the problem?
For PIC24H, in UART high speed mode ( BRGH = 1 ) to get correct value of BRGVAL you need to divide by 4, not 16. Change the formula on line 3. Also, it is helpful to be 100% sure about clock rate; if you didn't change configuration bits it is likely 8 MHz, not 16.
i'm trying to control my led in 256 (0-255) different levels of brightness. my controller is set to 80mhz and running on rtos. i'm setting the clock module to interrupt every 5 microseconds and the brightness e.g. to 150. the led is dimming, but i'm not sure if it is done right to really have 256 different levels
int counter = 1;
int brightness = 0;
void SetUp(void)
{
SysCtlClockSet(SYSCTL_SYSDIV_2_5|SYSCTL_USE_PLL|SYSCTL_OSC_MAIN|SYSCTL_XTAL_16MHZ);
GPIOPinTypeGPIOOutput(PORT_4, PIN_1);
Clock_Params clockParams;
Clock_Handle myClock;
Error_Block eb;
Error_init(&eb);
Clock_Params_init(&clockParams);
clockParams.period = 400; // every 5 microseconds
clockParams.startFlag = TRUE;
myClock = Clock_create(myHandler1, 400, &clockParams, &eb);
if (myClock == NULL) {
System_abort("Clock create failed");
}
}
void myHandler1 (){
brightness = 150;
while(1){
counter = (++counter) % 256;
if (counter < brightness){
GPIOPinWrite(PORT_4, PIN_1, PIN_1);
}else{
GPIOPinWrite(PORT_4, PIN_1, 0);
}
}
}
A 5 microsecond interrupt is a tall ask for an 80 MHz processor, and will leave little time for other work, and if you are not doing other work, you need not use interrupts at all - you could simply poll the clock counter; then it would still be a lot of processor to throw at a rather trivial task - and the RTOS is overkill too.
A better way to perform your task is to use the timer's PWM (Pulse Width Modulation) feature. You will then be able to accurately control the brightness with zero software overhead; leaving your processor to do more interesting things.
Using a PWM you could manage with a far lower performance processor if LED control is all it will do.
If you must use an interrupt/GPIO (for example your timer does not support PWM generation or the LED is not connected to a PWM capable pin), then it would be more efficient to set the timer incrementally. So for example for a mark:space of 150:105, you would set the timer for 150*5us (9.6ms), on the interrupt toggle the GPIO, then set the timer to 105*5us (6.72ms).
A major problem with your solution is the interrupt handler does not return - interrupts must run to completion and be as short as possible and preferably deterministic in execution time.
Without using hardware PWM, the following based on your code fragment is probably closer to what you need:
#define PWM_QUANTA = 400 ; // 5us
static volatile uint8_t brightness = 150 ;
static Clock_Handle myClock ;
void setBrightness( uint8_t br )
{
brightness = br ;
}
void SetUp(void)
{
SysCtlClockSet(SYSCTL_SYSDIV_2_5|SYSCTL_USE_PLL|SYSCTL_OSC_MAIN|SYSCTL_XTAL_16MHZ);
GPIOPinTypeGPIOOutput(PORT_4, PIN_1);
Clock_Params clockParams;
Error_Block eb;
Error_init(&eb);
Clock_Params_init(&clockParams);
clockParams.period = brightness * PWM_QUANTA ;
clockParams.startFlag = TRUE;
myClock = Clock_create(myHandler1, 400, &clockParams, &eb);
if (myClock == NULL)
{
System_abort("Clock create failed");
}
}
void myHandler1(void)
{
static int pin_state = 1 ;
// Toggle pin state and timer period
if( pin_state == 0 )
{
pin_sate = 1 ;
Clock_setPeriod( myClock, brightness * PWM_QUANTA ) ;
}
else
{
pin_sate = 0 ;
Clock_setPeriod( myClock, (255 - brightness) * PWM_QUANTA ) ;
}
// Set pin state
GPIOPinWrite(PORT_4, PIN_1, pin_state) ;
}
On the urging of Clifford I am elaborating on an alternate strategy for reducing the load of software dimming as as servicing interrupts every 400 clock cycles may prove difficult. The preferred solution should of course be to use hardware pulse-width modulation whenever available.
One option is to set interrupts only at the PWM flanks. Unfortunately this strategy tends to introduce races and drift as time elapses while adjustments are taking place and scales poorly to multiple channels.
Alternative we may switch from pulse-width to delta-sigma modulation. There is a fair bit of theory behind the concept but in this context it boils down to toggling the pin on and off as quickly as possible while maintaining an average on-time proportional to the dimming level. As a consequence the interrupt frequency may be reduced without bringing the overall switching frequency down to visible levels.
Below is an example implementation:
// Brightness to display. More than 8-bits are required to handle full 257-step range.
// The resolution also course be increased if desired.
volatile unsigned int brightness = 150;
void Interrupt(void) {
// Increment the accumulator with the desired brightness
static uint8_t accum;
unsigned int addend = brightness;
accum += addend;
// Light the LED pin on overflow, relying on native integer wraparound.
// Consequently higher brightness values translate to keeping the LED lit more often
GPIOPinWrite(PORT_4, PIN_1, accum < addend);
}
A limitation is that the switching frequency decreases with the distance from 50% brightness. Thus the final N steps may need to be clamped to 0 or 256 to prevent visible flicker.
Oh, and take care if switching losses are a concern in your application.
I am trying to read several PWM signals from an RC receiver into an ATMega 2560. I am having trouble understanding how the ICRn pin functions as it appears to be used for all three compare registers.
The RC PWM signal has a period of 20ms with a HIGH pulse of 2ms being a valid upper value and 1ms being a valid lower value. So the value will sweep from 1000us to 2000us. The period should begin at the rising edge of the pulse.
I have prescaled the 16MHz clock by 8 to have a 2MHz timer an thus should be able to measure the signal to 0.5us accuracy which is sufficient for my requirements.
Please note that I am having not problems with PWM output and this question is specifically about PWM input.
My code thus far is attached below. I know that I will have to use ICR3 and an ISR to measure the PWM values but I am unsure as to the best procedure for doing this. I also do not know how to check if the value measured is from PE3, PE4, or PE5. Is this code right and how do I get the value that I am looking for?
Any help would be greatly appreciated.
// Set pins as inputs
DDRE |= ( 0 << PE3 ) | ( 0 << PE4 ) | ( 0 << PE5 );
// Configure Timers for CTC mode
TCCR3A |= ( 1 << WGM31 ) | ( 1 << WGM30 ); // Set on compare match
TCCR3B |= ( 1 << WGM33 ) | ( 1 << WGM32 ) | ( 1 << CS31); // Set on compare match, prescale_clk/8
TCCR3B |= ( 1 << ICES5 ) // Use rising edge as trigger
// 16 bit register - set TOP value
OCR3A = 40000 - 1;
OCR3B = 40000 - 1;
OCR3C = 40000 - 1;
TIMSK3 |= ( 1 << ICIE3 );
I had forgotten to post my solution a few months ago so here it is...
I used a PPM receiver in the end so this code can easily edited to read a simple PWM.
In my header file I made a structure for a 6 channel receiver that I was using for my project. This can be changed as required for receivers with more or less channels.
#ifndef _PPM_H_
#define _PPM_H_
// Libraries included
#include <stdint.h>
#include <avr/interrupt.h>
struct orangeRX_ppm {
uint16_t ch[6];
};
volatile unsigned char ch_index;
struct orangeRX_ppm ppm;
/* Functions */
void ppm_input_init(void); // Initialise the PPM Input to CTC mode
ISR( TIMER5_CAPT_vect ); // Use ISR to handle CTC interrupt and decode PPM
#endif /* _PPM_H_ */
I then had the following in my .c file.
// Libraries included
#include <avr/io.h>
#include <stdint.h>
#include "ppm.h"
/* PPM INPUT
* ---
* ICP5 Pin48 on Arduino Mega
*/
void ppm_input_init(void)
{
DDRL |= ( 0 << PL1 ); // set ICP5 as an input
TCCR5A = 0x00; // none
TCCR5B = ( 1 << ICES5 ) | ( 1 << CS51); // use rising edge as trigger, prescale_clk/8
TIMSK5 = ( 1 << ICIE5 ); // allow input capture interrupts
// Clear timer 5
TCNT5H = 0x00;
TCNT5L = 0x00;
}
// Interrupt service routine for reading PPM values from the radio receiver.
ISR( TIMER5_CAPT_vect )
{
// Count duration of the high pulse
uint16_t high_cnt;
high_cnt = (unsigned int)ICR5L;
high_cnt += (unsigned int)ICR5H * 256;
/* If the duration is greater than 5000 counts then this is the end of the PPM signal
* and the next signal being addressed will be Ch0
*/
if ( high_cnt < 5000 )
{
// Added for security of the array
if ( ch_index > 5 )
{
ch_index = 5;
}
ppm.ch[ch_index] = high_cnt; // Write channel value to array
ch_index++; // increment channel index
}
else
{
ch_index = 0; // reset channel index
}
// Reset counter
TCNT5H = 0;
TCNT5L = 0;
TIFR5 = ( 1 << ICF5 ); // clear input capture flag
}
This code will use an trigger an ISR every time ICP5 goes from low to high. In this ISR the 16bit ICR5 register "ICR5H<<8|ICR5L" holds the number of pre-scaled clock pulses that have elapsed since the last change from low to high. This count is typically less than 2000 us. I have said that if the count is greater than 2500us (5000 counts) then the input is invalid and the next input should be ppm.ch[0].
I have attached an image of PPM as seen on my oscilloscope.
This method of reading PPM is quite efficient as we do not need to keep polling pins to check their logic level.
Don't forget to enable interrupts using the sei() command. Otherwise the ISR will never run.
Let's say you want to do the following (I'm not saying this will allow you to accurately measure the PWM signals but it might serve as example on how to set the registers)
Three timers running, which reset every 20 ms. This can be done by setting them in CTC mode for OCRnA: wgm3..0 = 0b0100.
//timer 1
TCCR4A = 0;
TCCR1B = (1<<CS11) | (1<<WGM12);
OCR1A = 40000 - 1;
//timer 3 (there's no ICP2)
TCCR3A = 0;
TCCR3B = (1<<CS31) | (1<<WGM32);
OCR3A = 40000 - 1;
//timer 4
TCCR4A = 0;
TCCR4B = (1<<CS41) | (1<<WGM42);
OCR4A = 40000 - 1;
Now connect each of the three pwm signals to their own ICPn pin (where n = timer). Check the datasheet for the locations of the different ICPn pins (i'm pretty sure it's not PE3, 4, 5)
Assuming the pwm signals start high at t=0 and go low after their high-time for the remainder of the period. You want to measure the high-time so we trigger an interrupt for each when a falling edge occurs on the ICPn pin.
bit ICESn in the TCCRnB register set to 0 will select the falling edge (this is already done in the previous code block).
To trigger the interrupts, set the corresponding interrupt enable bits:
TIMSK1 |= (1<<ICIE1);
TIMSK3 |= (1<<ICIE3);
TIMSK4 |= (1<<ICIE4);
sei();
Now each time an interrupt is triggered for ICn you can grab the ICRn register to see the time (in clockperiods/8) at which the falling edge occurred.