As soon as I include the following line in my code, the ESP32 timer interrupts appear to stop working.
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire);
Without this line of code, the debug output on the serial port displays incrementing ticks as expected.
I have tried all the timers 0..3. with no success.
I have scratched around in the Adafruit code, but can't see anything directly related to timers.
Is there some limitation on using other peripherals like the SPI and timers together?
I noticed that the ESP on the board is: ESP32-D0WDQ6 (revision 0), and when I run the code on a newer Wemos D1 mini, the problem goes away; that ESP is a ESP32-D0WDQ6 (revision 1). This is not a big help unfortunately, because the D1 mini doesn't have a display :-(
#include <Arduino.h>
#include <Adafruit_SSD1306.h>
#define OLED_SDA 5
#define OLED_SCL 4
//#define OLED_RST 4
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
// >>>>>>> if I uncomment this line, then "timerTicks" no longer gets incremented
// Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire);
uint32_t next_debug_output = 0;
const uint32_t DEBUG_INTERVAL_MS = 2000;
hw_timer_t *timer = NULL;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
volatile uint32_t timerTicks = 0;
void IRAM_ATTR onTimer() {
portENTER_CRITICAL_ISR(&timerMux);
timerTicks++;
portEXIT_CRITICAL_ISR(&timerMux);
}
void setup() {
Serial.begin(115200);
Serial.printf("\nReady\n");
Serial.println("start timer ");
timer = timerBegin(3, 80, true); // timer 3, MWDT clock period = 12.5 ns * TIMGn_Tx_WDT_CLK_PRESCALE -> 12.5 ns * 80 -> 1000 ns = 1 us, countUp
timerAttachInterrupt(timer, &onTimer, true); // edge (not level) triggered
timerAlarmWrite(timer, 100, true); // 100 * 1 us = 100 us, autoreload true
timerAlarmEnable(timer); // enable
}
void loop() {
if (millis() > next_debug_output) {
Serial.printf("timerTicks [%d]\n", timerTicks);
next_debug_output = millis() + DEBUG_INTERVAL_MS;
}
delay(50);
}
I am using a (very old) Wemos D1 board that I had lying around because it has an integrated OLED.
https://www.ebay.com.au/itm/193866708945?chn=ps&_ul=AU&norover=1&mkevt=1&mkrid=705-139619-5960-0&mkcid=2&itemid=193866708945&targetid=&device=c&mktype=pla&googleloc=9071459&poi=&campaignid=15791083372&mkgroupid=&rlsatarget=&abcId=9300816&merchantid=494522737&gclid=Cj0KCQjwnNyUBhCZARIsAI9AYlF51FCigKYfOh1xJ1HMHhWjDTclRPK5JcYCXREn7HwzKaIMsiP-So0aAuSKEALw_wcB
I'm compiling the code using PlaformIO in Visual Studio Code, with this configuration, and as far as I can see, all my libraries and platforms are the latest:
[env:wemos_oled]
platform = espressif32
board = wemos_d1_mini32
framework = arduino
monitor_speed = 115200
upload_speed = 921600
lib_deps =
adafruit/Adafruit SSD1306#^2.5.1
adafruit/Adafruit GFX Library#^1.10.13
adafruit/Adafruit BusIO#^1.11.1
Related
I have an Adafruit Sharp Memory display connected via SPI to Arduino Nano Connect RP2040. I don't use Arduino IDE nor libraries. I write the code in C++ like I would write for RP2040 on Pi Pico, so I'm using Pico/RP2040 libraries. I'm building it and then just copying the .uf2 file to Arduino.
I'm trying to control the display but without success and I have no idea what I'm doing wrong. Here is my code, where I'm sending a command which should clear the display but it does not:
#include <stdio.h>
#include <stdlib.h>
#include "pico/stdlib.h"
#include "hardware/spi.h"
#include "hardware/gpio.h"
// ---SPI
// Pins for Arduino
#define SCK_PIN 13 // SCLK / SCK
#define MOSI_PIN 11 // MOSI / COPI
#define SS_PIN 10 // SS / CS
// ---Sharp display
#define WIDTH 144
#define HEIGHT 168
int main() {
stdio_init_all();
printf("START \n");
bool vcom_bool_{false};
spi_init(spi0, 2000000);
spi_set_format( spi0, 8, SPI_CPOL_0, SPI_CPHA_0, SPI_MSB_FIRST);
gpio_set_function(MOSI_PIN, GPIO_FUNC_SPI);
gpio_set_function(SCK_PIN, GPIO_FUNC_SPI);
gpio_init(SS_PIN);
gpio_set_dir(SS_PIN, GPIO_OUT);
gpio_put(SS_PIN, 0); // this display is low on inactive
while(true)
{
printf("VCOM = %b \n", vcom_bool_);
gpio_put(SS_PIN, 1);
// Clear the display
uint8_t buf[2];
if(vcom_bool_)
{
buf[0] = 0b01100000;
vcom_bool_ = false;
}
else
{
buf[0] = 0b00100000;
vcom_bool_ = true;
}
buf[1] = 0b00000000;
spi_write_blocking(spi0, buf, 2);
gpio_put(SS_PIN, 0);
sleep_ms(10);
sleep_ms(500);
}
}
Display and wiring are ok, because when I run example code from Adafruit (it is using Arduino's libraries) it works.
I follow this documentation from Sharp: https://www.sharpsde.com/fileadmin/products/Displays/2016_SDE_App_Note_for_Memory_LCD_programming_V1.3.pdf
I also reviewed the code from Adafruit and the SPI communication seems to be done in the same way.
What am I doing wrong?
Is there some issue with using Pico libraries for Arduino Nano RP2040? I did I2C communication in the same way (Pico/RP2040 libraries, .uf2 copied to Arduino Nano RP2040) and it worked.
My TI Tiva ARM program is not working on TM4C123G. Using board EK TM4C123GXL. Program doesn't blink on board RGB LEDs. I am able to run other example program to check the LEDs, this program is from textbook TI TIVA ARM PROGRAMMING FOR EMBEDDED SYSTEMS. Need help to debug this program. Thanks
Code:
/* p2.4.c: Toggling a single LED
/* This program turns on the red LED and toggles the blue LED 0.5 sec on and 0.5 sec off. */
#include "TM4C123GH6PM.h"
void delayMs(int n);
int main(void)
{
/* enable clock to GPIOF at clock gating control register */
SYSCTL->RCGCGPIO |= 0x20;
/* enable the GPIO pins for the LED (PF3, 2 1) as output */
GPIOF->DIR = 0x0E;
/* enable the GPIO pins for digital function */
GPIOF->DEN = 0x0E;
/* turn on red LED only and leave it on */
GPIOF->DATA = 0x02;
while(1)
{
GPIOF->DATA |= 4; /* turn on blue LED */
delayMs(500);
GPIOF->DATA &= ~4; /* turn off blue LED */
delayMs(500);
}
}
/* delay n milliseconds (16 MHz CPU clock) */
void delayMs(int n)
{
int i, j;
for(i = 0 ; i < n; i++)
for(j = 0; j < 3180; j++)
{} /* do nothing for 1 ms */
}
I am using KEIL IDE-Version: µVision V5.36.0.0
Tool Version Numbers:
Toolchain: MDK-Lite Version: 5.36.0.0
Toolchain Path: G:\Keil_v5\ARM\ARMCLANG\Bin
C Compiler: ArmClang.exe V6.16
Assembler: Armasm.exe V6.16
Linker/Locator: ArmLink.exe V6.16
Library Manager: ArmAr.exe V6.16
Hex Converter: FromElf.exe V6.16
CPU DLL: SARMCM3.DLL V5.36.0.0
Dialog DLL: TCM.DLL V1.53.0.0
Target DLL: lmidk-agdi.dll V???
Dialog DLL: TCM.DLL V1.53.0.0
If you must use a counting-loop delay, you must at least declare the control variables volatile to be sure they will not be optimised away:
volatile int i, j;
However it would be far better to avoid implementing delays that rely on instruction cycles and the compiler's code generation. The Cortex-M core has a SYSTICK clock that provides accurate timing that does not rely on the compiler's code generation, changes to the system clock, or porting to different Cortex-M devices.
For example:
volatile uint32_t msTicks = 0 ;
void SysTick_Handler(void)
{
msTicks++;
}
void delayMs( uint32_t n )
{
uint32_t start = msTicks ;
while( msTicks - start < n )
{
// wait
}
}
int main (void)
{
// Init SYSTICK interrupt interval to 1ms
SysTick_Config( SystemCoreClock / 1000 ) ;
...
}
A busy-wait delay has limitations that make it unsuitable for all but the most trivial programs. During the delay the processor is tied up-doing nothing useful. Using the SYSTICK and msTicks defined as above you a better solution might be:
uint32_t blink_interval = 1000u ;
uint32_t next_toggle = msTicks + blink_interval ;
for(;;)
{
// If time to toggle LED ...
if( (int32_t)(msTicks - next_toggle) <= 0 )
{
// Toggle LED and advance toggle time
GPIOF->DATA ^= 4 ;
next_toggle += blink_interval ;
}
// Do other work while blinking LED
...
}
Note also the use of the bitwise-XOR operator to toggle the LED. That could be used to simplify you original loop:
while(1)
{
GPIOF->DATA ^= 4 ;
delayMs( 500 ) ;
}
It looks like some issue with the compiler version optimization. Reverted back to v5.
#include "project.h"
#include "led.h"
#include "timer.h"
#define DIVIDER 8
#define TMRC (DCO_FREQ / DIVIDER / 1000 * TIMER_INTERVAL)
static tWord tickCount=0;
void Timer_Init() {
BCSCTL1 = CALBC1_16MHZ;
DCOCTL = CALDCO_16MHZ;
BCSCTL2 = 0x0 ; // MCLK clock source DCOCLK, MCLK divider 1, SMCLK source DCOCLK, SMCLK divider 1
// Set up interrupts and timer 0
// Enable interrupts on timer
CCTL0 = CCIE;
// Use clock SMCLK, UP counting, divided of 8
TACTL = TASSEL_2 + MC_1 + ID_3;
// Set compare value
CCR0 = TMRC;
}
__attribute__((interrupt(TIMER0_A0_VECTOR))) void Timer_A(void) {
timer_run();
}
tWord getTick() {return tickCount;}
timer_run() {
tickCount++;
Led_Update();
}
This code was given for our instructor, I would not like to use it to set up this TimerA to blink the LED at intervals, changeable through the constructor of LED_Init() which is ran before this Timer_Init(). TIMER_INTERVAL will be set before the execution of the Timer_Init through Led_Init().
DCO_FREQ is not set anywhere so I am not quite sure what I am suppose to set it to.
I also don't understand the purpose of the tick counter.
It is also not impossible that the TMRC calculation formula is wrong because logically the TMRC decreases if the preset TIMER_INTERVAL is higher, which makes no sense, or does it?
Anyway, somehow I would like it to be able to run at slower intervals, ex 1s or slower, but no Idea how.
Unit: MSP430G2553
BCSCTL1 = CALBC1_16MHZ;
DCOCTL = CALDCO_16MHZ;
The DCO runs at 16 MHz.
#define TMRC (... * TIMER_INTERVAL)
the TMRC decreases if the preset TIMER_INTERVAL is higher
TMRC increases proportionally with TIMER_INTERVAL.
Im trying to create an embedded c code to control a dc motor with the PIC32MX460F512L microcontroller. Ive Configured the system clock at 80MHz, and the peripheral clock at 10MHz, Am using Timer 1 for pulsing the PWM with a given duty cycle, and Timer 2 for measuring the motor run time. I have a header file(includes.h) that contains system configuration information eg clock. Ive created most of the functions but some are a bit challenging. For example, initializing the LEDS and the functions for forward, backward movements and stop, I wanted the dc motor to run in forward direction for 4 sec at 70% duty cycle, then stop for 1 sec then reverse for 3 sec at 50% duty cycle and then stop for 1 sec and then forward again for 3 sec at 40% duty cycle, stop for 1 sec and finally forward for 5 sec at 20% duty cycle. Any suggestions for the forward, stop, and reverse functions
#include <stdio.h>
#include <stdlib.h>
#include <includes.h>
void main()
{
// Setting up PIC modules such as Timers, IOs OCs,Interrupts, ...
InitializeIO();
InitializeLEDs();
InitializeTimers();
while(1) {
WaitOnBtn1();
Forward(4.0,70);
Stop(1.0);
Backward(3.0,50);
Stop(2);
Forward(3.0,40);
Stop(1.0);
Backward(2.0,20);
LEDsOFF();
}
return;
}
void InitializeIO(){
TRISAbits.TRISA6 = 1;
TRISAbits.TRISA7 = 1;
TRISGbits.TRISG12 = 0;
TRISGbits.TRISB13 = 0;
LATGbits.LATB12 = 0;
LATGbits.LATB13 = 0;
return;
}
void InitializeLEDs(){
//code to initialize LEDS
}
void InitializeTimers(){
// Initialize Timer1
T1CON = 0x0000; // Set Timer1 Control to zeros
T1CONbits.TCKPS=3; // prescale by 256
T1CONbits.ON = 1; // Turn on Timer
PR1= 0xFFFF; // Period of Timer1 to be full
TMR1 = 0; // Initialize Timer1 to zero
// Initialize Timer2
T2CON = 0;
T2CONbits.TCKPS = 7; // prescale by 256
T2CONbits.T32 = 1; // use 32 bits timer
T2CONbits.ON = 1;
PR2 = 0xFFFFFFFF; // Period is set for 32 bits
TMR2 = 0;
}
void WaitOnBtn1(){
// wait on Btn1 indefinitely
while(PORTAbits.RA6 == 0);
// Turn On LED1 indicating it is Btn1 is Pushed
LATBbits.LATB10 = 1;
return;
}
void Forward(float Sec, int D){
int RunTime = (int)(Sec*39000); // convert the total
time to number of Tics
TMR2 = 0;
//LEDs
LATGbits.LATG12 = 1; // forward Direction
LATBbits.LATB12 = 0;
LATBbits.LATB13 = 0;
LATBbits.LATB11 = 1;
// Keep on firing the PWM as long as Run time is not
elapsed
while (TMR2 < RunTime){
PWM(D);
}
return;
}
void PWM(int D){
TMR1 = 0;
int Period = 400;
while (TMR1< Period) {
if (TMR1 < Period*D/100){
LATGbits.LATG13 = 1;
}
else{
LATGbits.LATG13 = 0;
}
}
Functions, not methods, to be precise.
So what is exactly the question?
What I can say from a quick look on a source code:
LEDs initialisation should be done as you did in InitializeIO() function. Simply set proper TRISx bits to 0 to configure LED pins as output.
For the PWM and motor control functions you should take some time and try to understand how builtin PWM peripheral works. It is a part of OC (Output Compare) and it is very easy to use. Please, take look on following link http://ww1.microchip.com/downloads/en/DeviceDoc/61111E.pdf
and this one for the minimal implementation using builtin peripheral libraries https://electronics.stackexchange.com/questions/69232/pic32-pwm-minimal-example
Basically you need to set up OC registers to "make" OC module acts like PWM. You need to allocate one of the timers to work with OC module (it will be used for base PWM frequency) and that's it.
All you need after that is to set PWM duty cycle value by setting timer PRx register, you don't need to swap bits like in your PWM routine.
To stop it simple stop it simply disable the timer.
To run it again run the timer.
To change direction (it depends of your driver for the motor) I guess you need just to toggle direction pin.
I hope it helps...
I'm probably just having a can't-see-the-forest-for-the-trees moment with one of these registers, but I can't get the PWM working on the dsPIC33FJ32MC102 microcontroller (warning: big PDF) I'm playing around with. I've followed both the datasheet and further application note (warning: another PDF) and even code samples and I can't see what I'm doing wrong, though on my testbench I'm getting Vcc on the high output and Ground on the low output. I've tied the fault pins both to Vcc and disabled the register keycode so my changes should see some effect. What am I doing wrong?
#define FOSC (3686400ULL)
#define FCY (FOSC/2)
#include <xc.h>
#include <libpic30.h>
...
#pragma config PWMPIN = ON // Motor Control PWM Module Pin Mode bit (PWM module pins controlled by PORT register at device Reset)
#pragma config PWMLOCK = OFF
...
void main(void){
...
//setup PWM
//Clear faults
IFS3bits.PWM1IF = 0;
IFS3bits.FLTA1IF = 0;
IFS4bits.FLTB1IF = 0;
//Setup dead times
P1DTCON1bits.DTAPS = 0b00; //Dead time tick is 1 TCY
P1DTCON1bits.DTBPS = 0b00;
P1DTCON1bits.DTA = 10; //Dead time is 10TCY ~= 3uS
P1DTCON1bits.DTB = 10;
P1DTCON2bits.DTS1A = 0; //Active and Inactive transition dead times
P1DTCON2bits.DTS2A = 0; //0 takes A dead time
P1DTCON2bits.DTS3A = 0; //1 takes B dead time
P1DTCON2bits.DTS1I = 1;
P1DTCON2bits.DTS2I = 1;
P1DTCON2bits.DTS3I = 1;
P1TCONbits.PTOPS = 0b0000; //1 CPU tick = 1 PWM tick
P1TCONbits.PTCKPS = 0b00;
P1TCONbits.PTMOD = 0b00;
P1TCONbits.PTSIDL = 0; //Run when CPU idles
// no longer necessary since I disabled register write lock:
// __builtin_write_PWMSFR(&P1FLTACON,0x0000,&PWM1KEY);
// __builtin_write_PWMSFR(&P1FLTBCON,0x0000,&PWM1KEY);
// __builtin_write_PWMSFR(&PWM1CON1 ,0x0077,&PWM1KEY);
PWM1CON1 = 0x0077; //Enable all 3 channels
P1FLTACON = 0x0000; //Disable faults
P1FLTBCON = 0x0000;
//Setup Wave freq/duty
//Fosc = 7.3728 MHz -> Fcy = 3.6864MHz
//Desire a PWM of 20250Hz (smaller scalar error than 20kHz)
//P1TPER = [Fcy/(Fpwm*Scalar)] - 1
//Therefore P1TPER = [3.6864M/(20250*1)] - 1 = 181;
P1TPER = 181;
P1DC1 = 0x7FFF; // 0x7FFF for 50%
P1DC2 = 0x7FFF;
P1DC3 = 0x7FFF;
P1OVDCON = 0x3F00; //Disable override; override disables PWM
PWM1CON2 = 0x0000;
P1TCONbits.PTEN = 1; //Turn on
...
while(1);
}
I believe you have set up the timer to count from 0 to 181 (P1TPER) and then reset and repeat. But you have set the duty cycle registers to 0x7FFF, which is greater than 181. So I believe the duty cycle value will never be less than the timer value and therefore the output will never change. Try setting the duty cycle registers to 181/2 = 90 to get a duty cycle of 50%.