I am working on a project that will require timer interrupts.
Using STM32cubeIDE, I generated code that should work with timer-interrupts. Here is a truncation of what my main.cpp looks like: (htim1 is a global handle)
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM1_Init();
HAL_TIM_Base_Start_IT(&htim1);
while(1);
Here is what MX_TIM1_INIT() looks like:
static void MX_TIM1_Init(void)
{
/* USER CODE BEGIN TIM1_Init 0 */
/* USER CODE END TIM1_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};
/* USER CODE BEGIN TIM1_Init 1 */
/* USER CODE END TIM1_Init 1 */
htim1.Instance = TIM1;
htim1.Init.Prescaler = 84-1;
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 0xFFFF-1;
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 0;
htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_PWM_Init(&htim1) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
sBreakDeadTimeConfig.DeadTime = 0;
sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM1_Init 2 */
/* USER CODE END TIM1_Init 2 */
HAL_TIM_MspPostInit(&htim1);
}
Here is what my interrupt handler looks like:
void TIM1_UP_TIM10_IRQHandler(void)
{
/* USER CODE BEGIN TIM1_UP_TIM10_IRQn 0 */
/* USER CODE END TIM1_UP_TIM10_IRQn 0 */
// HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_12); //O-SCOPE DEBUG on PA6
// if (timer1 == nullptr) return;
//timer1->TimerISR();
HAL_TIM_IRQHandler(&htim1);
/* USER CODE BEGIN TIM1_UP_TIM10_IRQn 1 */
/* USER CODE END TIM1_UP_TIM10_IRQn 1 */
}
And here is my callback function:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_12); //O-SCOPE DEBUG on PA6
}
No matter what I do I can't get the debugger to even enter the handler.... Could anyone enlighten me as to what I am doing wrong? Thanks!
I would never use HAL library to set up the timers. It makes no sense for me. In the example below I will omit clock (in my case 168MHz) & GPIO settings. As you did not state what model of STM32F4 you use, this code was tested using STM32F446RET uC. Other STM32F4 have identical timers.
Setting the timer:
__HAL_RCC_TIM1_CLK_ENABLE();
TIM1 -> PSC = (20000 - 1);
TIM1 -> ARR = (4200 - 1);
//168e6 / (20000 * 4200) = 2 - two interrupts per second
TIM1 -> EGR |= TIM_EGR_UG; // reinitialize the counter and reload registers
TIM1 -> DIER |= TIM_DIER_UIE;
NVIC_EnableIRQ(TIM1_UP_TIM10_IRQn);
TIM1 -> CR1 = TIM_CR1_CEN;
The interrupt handler. If you program in C++ handlers have to be declared as extern "C" !!!
//if you compile as C++ you need to declare handlers as "normal" C functions
//#ifdef`s are not needed if this code will never be compiled as C progream
#ifdef __cplusplus
extern "C" {
#endif
void TIM1_UP_TIM10_IRQHandler(void)
{
if(TIM1 -> SR & TIM_SR_UIF)
{
TIM1 -> SR = ~(TIM_SR_UIF); // clear UIF flag
GPIOA -> ODR ^= 1 << 5; // toggle PA5
}
}
#ifdef __cplusplus
}
#endif
And my LED connected to PA5 changes the state every 500ms.
Job done - isn't it easier than HAL?
Related
I have a simple Xcode project that contains a single C++ class. However, when I attempt to build that project in Xcode I'm getting the following error message:
error: unable to spawn process '/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang' (No such file or directory) (in target 'iOSStaticLibrary' from project 'iOSStaticLibrary')
In addition, the following warning is emitted:
warning: Could not read serialized diagnostics file: error("Failed to open diagnostics file") (in target 'iOSStaticLibrary' from project 'iOSStaticLibrary')
I couldn't figure out how to attach the actual project to my post. It only consists of 3 files, so, I'm posting the relative filenames and contents below.
In order to recreate the problem, create the 3 files in the specified folder and add the corresponding content.
File ARM64/Debug/iOSStaticLibrary.xcodeproj/project.pbxproj:
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
3b051b2877334f3891e05fbf /* iOSStaticLibrary.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 810f8ddb516f4fd9aae95391 /* iOSStaticLibrary.cpp */; settings = {COMPILER_FLAGS = "-c -Wall -Wswitch -W\"no-deprecated-declarations\" -W\"empty-body\" -W\"bool-conversion\" -W\"constant-conversion\" -W\"shorten-64-to-32\" -W\"enum-conversion\" -W\"int-conversion\" -W\"return-type\" -Wparentheses -W\"no-pointer-sign\" -W\"no-format\" -Wuninitialized -W\"unreachable-code\" -W\"unused-function\" -W\"unused-value\" -W\"unused-variable\" -O0 -fno-strict-aliasing -fwritable-strings -fno-threadsafe-statics -fno-exceptions -std=gnu++11 -x c++"; }; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
0e5228c6fe3a40ef9446f1d3 /* libiOSStaticLibrary.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libiOSStaticLibrary.a; sourceTree = BUILT_PRODUCTS_DIR; };
810f8ddb516f4fd9aae95391 /* iOSStaticLibrary.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = iOSStaticLibrary.cpp; path = ../../../iOSStaticLibrary.cpp; sourceTree = SOURCE_ROOT; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
c8f1fdfa1a1a4cf2af0a7098 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
20adea7cddb14f79ab5df50b /* Products */ = {
isa = PBXGroup;
children = (
0e5228c6fe3a40ef9446f1d3 /* libiOSStaticLibrary.a */,
);
name = Products;
sourceTree = SOURCE_ROOT;
};
cb87a76375d04d85a8368aa7 = {
isa = PBXGroup;
children = (
810f8ddb516f4fd9aae95391 /* iOSStaticLibrary.cpp */,
20adea7cddb14f79ab5df50b /* Products */,
);
sourceTree = "\"<group>\"";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
3488e49fcf514802b7be61b6 /* iOSStaticLibrary */ = {
isa = PBXNativeTarget;
buildConfigurationList = 67256f7dc92b42baa13ec8d3 /* Build configuration list for PBXNativeTarget "iOSStaticLibrary" */;
buildPhases = (
7baf7bfde3004dd493908ddd /* Sources */,
c8f1fdfa1a1a4cf2af0a7098 /* Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = iOSStaticLibrary;
productName = iOSStaticLibrary;
productReference = 0e5228c6fe3a40ef9446f1d3 /* libiOSStaticLibrary.a */;
productType = "com.apple.product-type.library.static";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
ee73158d3505468a8fdc43b9 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0610;
ORGANIZATIONNAME = MyOrganizationName;
TargetAttributes = {
3488e49fcf514802b7be61b6 = {
CreatedOnToolsVersion = 6.1;
};
};
};
buildConfigurationList = 33fd7959eb644d189bbaa7f7 /* Build configuration list for PBXProject "iOSStaticLibrary" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
English,
en,
);
mainGroup = cb87a76375d04d85a8368aa7;
productRefGroup = 20adea7cddb14f79ab5df50b /* Products */;
projectDirPath = "\n\n ";
projectRoot = "\n\n ";
targets = (
3488e49fcf514802b7be61b6 /* iOSStaticLibrary */,
);
};
/* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */
7baf7bfde3004dd493908ddd /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
3b051b2877334f3891e05fbf /* iOSStaticLibrary.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
1cbb1a8c7cbf4c2699369b65 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
};
name = Debug;
};
fda96981947c4c2cb659f3e3 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_CXX_LIBRARY = "libc++";
IPHONEOS_DEPLOYMENT_TARGET = 8.1;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
};
name = Debug;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
33fd7959eb644d189bbaa7f7 /* Build configuration list for PBXProject "iOSStaticLibrary" */ = {
isa = XCConfigurationList;
buildConfigurations = (
1cbb1a8c7cbf4c2699369b65 /* Debug */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Debug;
};
67256f7dc92b42baa13ec8d3 /* Build configuration list for PBXNativeTarget "iOSStaticLibrary" */ = {
isa = XCConfigurationList;
buildConfigurations = (
fda96981947c4c2cb659f3e3 /* Debug */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Debug;
};
/* End XCConfigurationList section */
};
rootObject = ee73158d3505468a8fdc43b9 /* Project object */;
}
File iOSStaticLibrary.h
class iOSStaticLibrary
{
public:
iOSStaticLibrary();
~iOSStaticLibrary();
};
File iOSStaticLibrary.cpp:
#include "iOSStaticLibrary.h"
iOSStaticLibrary::iOSStaticLibrary()
{
}
iOSStaticLibrary::~iOSStaticLibrary()
{
}
My assumption is that something in the project.pbxproj is not as it should be. This file is generated by a code generator, not by Xcode itself so that might well be the case.
However, for the life of me I cannot figure out what's wrong.
Alright, I found the issue. The code generator creates the Xcode project as an XML file. If I open it in Xcode and apply a change to it, Xcode automatically converts the file to the other non-XML file format (i.e. as shown in the question)
Now, the code generator emits the following problematic code:
...
<key>projectDirPath</key>
<string>
</string>
...
<key>projectRoot</key>
<string>
</string>
..
Both keys (projectDirPath / projectRoot) should have an empty string as value and it actually does look like it is empty in the above XML.
However, Xcode interprets it differently. If the XML file is converted to the other format it looks as follows:
...
projectDirPath = "\n\n ";
projectRoot = "\n\n ";
...
So, Xcode interprets the whitespace between <string> and </string> as actual value. To fix it, the code generator must be adjusted to emit
...
<key>projectDirPath</key>
<string></string>
...
<key>projectRoot</key>
<string></string>
...
or <string /> or any other valid way instead of the above approach. That actually fixes the problem.
I have written a debounce class to debounce inputs.
The idea was that a state of a certain input may be ON, OFF, FALLING or RISING.
the object.debounceInputs() is to be called with a fixed interval
With the the function object.readInput() the correct state of the object could be read in. A FALLING or RISING state only lasts for 1 interval time (usually set at 20ms) and these states can only be read once.
Ofcourse I tested the software and it worked without flaw, now I started using the software in other projects and a peculiar bug came to light.
The software works perfectly fine... if you have just one input object. If you debounce more than 1 object, the inputs are affecting each other which should not be possible as every object uses private variables.
The source code:
#include "debounceClass.h"
Debounce::Debounce(unsigned char _pin) {
pinMode(_pin, INPUT_PULLUP); // take note I use a pull-up resistor by default
pin = _pin;
}
unsigned char Debounce::readInput() {
byte retValue = state;
if(state == RISING) state = ON; // take note I use a pull-up resistor
if(state == FALLING) state = OFF; // rising or falling may be returned only once
return retValue;
}
void Debounce::debounceInputs() {
static bool oldSample = false, statePrev = false;
bool newSample = digitalRead(pin);
if(newSample == oldSample) { // if the same state is detected atleast twice in 20ms...
if(newSample != statePrev) { // if a flank change occured return RISING or FALLING
statePrev = newSample ;
if(newSample) state = RISING;
else state = FALLING;
}
else { // or if there is no flank change return PRESSED or RELEASED
if(newSample) state = ON;
else state = OFF;
}
}
oldSample = newSample;
return 255;
}
The corresponding header file:
#include <Arduino.h>
#ifndef button_h
#define button_h
//#define
#define ON 9 // random numbers, RISING and FALLING are already defined in Arduino.h
#define OFF 10
class Debounce {
public:
Debounce(unsigned char _pin);
unsigned char readInput();
void debounceInputs();
private:
unsigned char state;
unsigned char pin;
};
#endif
I have had this bug with 2 separate projects, so the fault definitely lies in my Debounce class.
An example program to illustrate the program:
#include "debounceClass.h"
const int pin3 = 3 ;
const int pin4 = 4 ;
Debounce obj1( pin3 ) ;
Debounce obj2( pin4 ) ;
byte previousState1, previousState2;
unsigned long prevTime = 0, prevTime1 = 0, prevTime2 = 0;
void setup()
{
Serial.begin( 115200 ) ;
// CONSTRUCTOR OF OBJECTS SETS THE PINMODE TO INPUT_PULLUP
pinMode( pin3, OUTPUT ) ;
pinMode( pin4, OUTPUT ) ;
}
const int interval = 20, interval1 = 1000, interval2 = 2000;
void loop() {
unsigned long currTime = millis() ;
if( currTime > prevTime + interval ) {
prevTime = currTime ;
obj1.debounceInputs(); // comment one of these 2 out, and the other debounces perfectly
obj2.debounceInputs();
#define printState(x) case x: Serial.print(#x); break
byte state = obj1.readInput() ;
if( state != previousState1 ) {
previousState1 = state ;
Serial.print("state of obj1 = ") ;
switch ( state ) {
printState( ON ) ;
printState( OFF ) ;
printState( RISING ) ;
printState( FALLING ) ;
}
Serial.println();
}
state = obj2.readInput() ;
if( state != previousState2 ) {
previousState2 = state ;
Serial.print("state of obj2 = ") ;
switch ( state ) {
printState( ON ) ;
printState( OFF ) ;
printState( RISING ) ;
printState( FALLING ) ;
}
Serial.println();
}
}
if( currTime > prevTime1 + interval1 ) {
prevTime1 = currTime ;
digitalWrite( pin3, !digitalRead( pin3 ) );
}
if( currTime > prevTime2 + interval2 ) {
prevTime2 = currTime ;
digitalWrite( pin4, !digitalRead( pin4 ) );
}
}
This program automatically toggles both pins so you do not need physical inputs. If you comment out one of the indicated lines, you'll see that the other pin is debounced just fine. But when both pins are debounced the result is catastrophic. There is a weird link between the 2 objects which I cannot explain. I have reached a point at which I start doubting the compiler, so that was the moment I realized that I need to ask others.
Why is this happening and what did I do wrong here?
I found the problem.
I cannot use a static variable within a class method. These static variables are seen by all objects which caused the problem.
I moved the static variables to the private variable section
I am trying to interface with winbond external flash memory using QSPI interface : https://www.winbond.com/resource-files/w25m02gv%20revb%20070115.pdf. I am sending read Device ID command and I expect to see something like that: Read device ID waveforms
I have connected Logic analyzer and I can see that I am sending the required command but I am not getting any answer on D1 line:
enter image description here
The code that I am trying to
void QSPI_read_ID(QSPI_HandleTypeDef *hqspi){
QSPI_CommandTypeDef sCommand;
uint32_t tmp;
int len;
/* READ ID ------------------------------------------ */
sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
sCommand.Instruction = JEDEC_ID_CMD;
sCommand.AddressMode = QSPI_ADDRESS_NONE;
sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
sCommand.DataMode = QSPI_DATA_NONE;
sCommand.DummyCycles = 8;
sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
if (HAL_QSPI_Command(hqspi, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
QSPI_Flash_Error_Handler(hqspi);
}
If I change the dummy cycle number from 8 to lets say 24, I am able to read first 2 bytes of the device ID:
enter image description here
I am not sure whether I am not understanding this properly. I was under the impression that if I send the read device ID command I should invoke the D1 line to send me the ID automatically. The problem that it wont let me use more than 32 dummy cycles.
Try to set recieved size as 3 and apply recieve function
void QSPI_read_ID(QSPI_HandleTypeDef *hqspi){
QSPI_CommandTypeDef sCommand;
uint8_t tmp[3];
int len;
/* READ ID ------------------------------------------ */
sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
sCommand.Instruction = JEDEC_ID_CMD;
sCommand.AddressMode = QSPI_ADDRESS_NONE;
sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
sCommand.DataMode = QSPI_DATA_NONE;
sCommand.DummyCycles = 8;
sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
sCommand.NbData = 3;
if (HAL_QSPI_Command(hqspi, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
QSPI_Flash_Error_Handler(hqspi);
}
if (HAL_QSPI_Receive(hqspi, tmp , HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK){
QSPI_Flash_Error_Handler(hqspi);
}
}
Yes that works! I did not realise that I have to call the receive function to get the bytes ( Totally makes sense now ). Thanks.
Code here if anyone is struggling with the same problem:
void QSPI_read_ID(QSPI_HandleTypeDef *hqspi){
QSPI_CommandTypeDef sCommand;
uint8_t reg[3]; // N25Q128A13EF840E 0xEF, 0xAB, 0x21
/* READ ID ------------------------------------------ */
sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
sCommand.Instruction = JEDEC_ID_CMD;
sCommand.AddressMode = QSPI_ADDRESS_NONE;
sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
//sCommand.DataMode = QSPI_DATA_NONE;
sCommand.DataMode = QSPI_DATA_1_LINE;
sCommand.NbData = sizeof(reg);
sCommand.DummyCycles = 8;
sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
if (HAL_QSPI_Command(hqspi, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
QSPI_Flash_Error_Handler(hqspi);
}
memset(reg, 0, sizeof(reg));
if (HAL_QSPI_Receive(hqspi, ®[0], HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
puts('ERROR:HAL_QSPI_Receive');
Error_Handler();
}
HAL_Delay(10);
}
I wrote a little debugger for analysing and looging certain problems. Now I implemented a hardwarebreakpoint for detecting the access of a memory address being overwritten. When I run my debugger with a test process, then everything works fine. When I access the address, the breakpoint fires and the callstack is logged. The problem is, when I run the same against an application running multiple threads. I'm replicating the breakpoint into every thread that gets created and also the main thread. None of the functions report an error and everything looks fine, but when the address is accessed, the breakpoint never fires.
So I wonder if there is some documentation where this is described or if there are additionaly things that I have to do in case of a multithreaded application.
The function to set the breakpoint is this:
#ifndef _HARDWARE_BREAKPOINT_H
#define _HARDWARE_BREAKPOINT_H
#include "breakpoint.h"
#define MAX_HARDWARE_BREAKPOINT 4
#define REG_DR0_BIT 1
#define REG_DR1_BIT 4
#define REG_DR2_BIT 16
#define REG_DR3_BIT 64
class HardwareBreakpoint : public Breakpoint
{
public:
typedef enum
{
REG_INVALID = -1,
REG_DR0 = 0,
REG_DR1 = 1,
REG_DR2 = 2,
REG_DR3 = 3
} Register;
typedef enum
{
CODE,
READWRITE,
WRITE,
} Type;
typedef enum
{
SIZE_1,
SIZE_2,
SIZE_4,
SIZE_8,
} Size;
typedef struct
{
void *pAddress;
bool bBusy;
Type nType;
Size nSize;
Register nRegister;
} Info;
public:
HardwareBreakpoint(HANDLE hThread);
virtual ~HardwareBreakpoint(void);
/**
* Sets a hardware breakpoint. If no register is free or an error occured
* REG_INVALID is returned, otherwise the hardware register for the given breakpoint.
*/
HardwareBreakpoint::Register set(void *pAddress, Type nType, Size nSize);
void remove(void *pAddress);
void remove(Register nRegister);
inline Info const *getInfo(Register nRegister) const { return &mBreakpoint[nRegister]; }
private:
typedef Breakpoint super;
private:
Info mBreakpoint[MAX_HARDWARE_BREAKPOINT];
size_t mRegBit[MAX_HARDWARE_BREAKPOINT];
size_t mRegOffset[MAX_HARDWARE_BREAKPOINT];
};
#endif // _HARDWARE_BREAKPOINT_H
void SetBits(DWORD_PTR &dw, size_t lowBit, size_t bits, size_t newValue)
{
DWORD_PTR mask = (1 << bits) - 1;
dw = (dw & ~(mask << lowBit)) | (newValue << lowBit);
}
HardwareBreakpoint::HardwareBreakpoint(HANDLE hThread)
: super(hThread)
{
mRegBit[REG_DR0] = REG_DR0_BIT;
mRegBit[REG_DR1] = REG_DR1_BIT;
mRegBit[REG_DR2] = REG_DR2_BIT;
mRegBit[REG_DR3] = REG_DR3_BIT;
CONTEXT ct;
mRegOffset[REG_DR0] = reinterpret_cast<size_t>(&ct.Dr0) - reinterpret_cast<size_t>(&ct);
mRegOffset[REG_DR1] = reinterpret_cast<size_t>(&ct.Dr1) - reinterpret_cast<size_t>(&ct);
mRegOffset[REG_DR2] = reinterpret_cast<size_t>(&ct.Dr2) - reinterpret_cast<size_t>(&ct);
mRegOffset[REG_DR3] = reinterpret_cast<size_t>(&ct.Dr3) - reinterpret_cast<size_t>(&ct);
memset(&mBreakpoint[0], 0, sizeof(mBreakpoint));
for(int i = 0; i < MAX_HARDWARE_BREAKPOINT; i++)
mBreakpoint[i].nRegister = (Register)i;
}
HardwareBreakpoint::Register HardwareBreakpoint::set(void *pAddress, Type nType, Size nSize)
{
CONTEXT ct = {0};
super::setAddress(pAddress);
ct.ContextFlags = CONTEXT_DEBUG_REGISTERS;
if(!GetThreadContext(getThread(), &ct))
return HardwareBreakpoint::REG_INVALID;
size_t iReg = 0;
for(int i = 0; i < MAX_HARDWARE_BREAKPOINT; i++)
{
if (ct.Dr7 & mRegBit[i])
mBreakpoint[i].bBusy = true;
else
mBreakpoint[i].bBusy = false;
}
Info *reg = NULL;
// Address already used?
for(int i = 0; i < MAX_HARDWARE_BREAKPOINT; i++)
{
if(mBreakpoint[i].pAddress == pAddress)
{
iReg = i;
reg = &mBreakpoint[i];
break;
}
}
if(reg == NULL)
{
for(int i = 0; i < MAX_HARDWARE_BREAKPOINT; i++)
{
if(!mBreakpoint[i].bBusy)
{
iReg = i;
reg = &mBreakpoint[i];
break;
}
}
}
// No free register available
if(!reg)
return HardwareBreakpoint::REG_INVALID;
*(void **)(((char *)&ct)+mRegOffset[iReg]) = pAddress;
reg->bBusy = true;
ct.Dr6 = 0;
int st = 0;
if (nType == CODE)
st = 0;
if (nType == READWRITE)
st = 3;
if (nType == WRITE)
st = 1;
int le = 0;
if (nSize == SIZE_1)
le = 0;
else if (nSize == SIZE_2)
le = 1;
else if (nSize == SIZE_4)
le = 3;
else if (nSize == SIZE_8)
le = 2;
SetBits(ct.Dr7, 16 + iReg*4, 2, st);
SetBits(ct.Dr7, 18 + iReg*4, 2, le);
SetBits(ct.Dr7, iReg*2, 1, 1);
ct.ContextFlags = CONTEXT_DEBUG_REGISTERS;
if(!SetThreadContext(getThread(), &ct))
return REG_INVALID;
return reg->nRegister;
}
I'm setting the breakpoint in the main debugger loop whenever a new thread is created CREATE_THREAD_DEBUG_EVENT but looking at the sourcecode of GDB it seems not to be done there, so maybe that is to early?
So I finally found the answer to this problem.
In the debug event loop, I'm monitoring the events that windows sends me. One of those events is CREATE_THREAD_DEBUG_EVENT which I used to set the hardware breakpoint whenever a new thread was created.
The problem is, that the notification of this event comes before the thread got actually started. So Windows is setting the context for the first time AFTER this event is sent, which of course overwrites any context data that I have set before.
The solution I implemented now is, when a CREATE_THREAD_DEBUG_EVENT comes I put a software breakpoint at the start adress of the thread, so that the first instruction is my breakpoint. When I receive the breakpoint event, I restore the original code and install the hardware breakpoint, which now fires fine.
If there is a better solution, I'm all ears. :)
I have made a soft synthesizer in Visual Studio 2012 with C++, MFC and DirectX. Despite having added code to rapidly fade out the sound I am experiencing popping / clicking when stopping playback (also when starting).
I copied the DirectX code from this project: http://www.codeproject.com/Articles/7474/Sound-Generator-How-to-create-alien-sounds-using-m
I'm not sure if I'm allowed to cut and paste all the code from the Code Project. Basically I use the Player class from that project as is, the instance of this class is called m_player in my code. The Stop member function in that class calls the Stop function of LPDIRECTSOUNDBUFFER:
void Player::Stop()
{
DWORD status;
if (m_lpDSBuffer == NULL)
return;
HRESULT hres = m_lpDSBuffer->GetStatus(&status);
if (FAILED(hres))
EXCEP(DirectSoundErr::GetErrDesc(hres), "Player::Stop GetStatus");
if ((status & DSBSTATUS_PLAYING) == DSBSTATUS_PLAYING)
{
hres = m_lpDSBuffer->Stop();
if (FAILED(hres))
EXCEP(DirectSoundErr::GetErrDesc(hres), "Player::Stop Stop");
}
}
Here is the notification code (with some supporting code) in my project that fills the sound buffer. Note that the rend function always returns a double between -1 to 1, m_ev_smps = 441, m_n_evs = 3 and m_ev_sz = 882. subInit is called from OnInitDialog:
#define FD_STEP 0.0005
#define SC_NOT_PLYD 0
#define SC_PLYNG 1
#define SC_FD_OUT 2
#define SC_FD_IN 3
#define SC_STPNG 4
#define SC_STPD 5
bool CMainDlg::subInit()
// initialises various variables and the sound player
{
Player *pPlayer;
SOUNDFORMAT format;
std::vector<DWORD> events;
int t, buf_sz;
try
{
pPlayer = new Player();
pPlayer->SetHWnd(m_hWnd);
m_player = pPlayer;
m_player->Init();
format.NbBitsPerSample = 16;
format.NbChannels = 1;
format.SamplingRate = 44100;
m_ev_smps = 441;
m_n_evs = 3;
m_smps = new short[m_ev_smps];
m_smp_scale = (int)pow(2, format.NbBitsPerSample - 1);
m_max_tm = (int)((double)m_ev_smps / (double)(format.SamplingRate * 1000));
m_ev_sz = m_ev_smps * format.NbBitsPerSample/8;
buf_sz = m_ev_sz * m_n_evs;
m_player->CreateSoundBuffer(format, buf_sz, 0);
m_player->SetSoundEventListener(this);
for(t = 0; t < m_n_evs; t++)
events.push_back((int)((t + 1)*m_ev_sz - m_ev_sz * 0.95));
m_player->CreateEventReadNotification(events);
m_status = SC_NOT_PLYD;
}
catch(MATExceptions &e)
{
MessageBox(e.getAllExceptionStr().c_str(), "Error initializing the sound player");
EndDialog(IDCANCEL);
return FALSE;
}
return TRUE;
}
void CMainDlg::Stop()
// stop playing
{
m_player->Stop();
m_status = SC_STPD;
}
void CMainDlg::OnBnClickedStop()
// causes fade out
{
m_status = SC_FD_OUT;
}
void CMainDlg::OnSoundPlayerNotify(int ev_num)
// render some sound samples and check for errors
{
ScopeGuardMutex guard(&m_mutex);
int s, end, begin, elapsed;
if (m_status != SC_STPNG)
{
begin = GetTickCount();
try
{
for(s = 0; s < m_ev_smps; s++)
{
m_smps[s] = (int)(m_synth->rend() * 32768 * m_fade);
if (m_status == SC_FD_IN)
{
m_fade += FD_STEP;
if (m_fade > 1)
{
m_fade = 1;
m_status = SC_PLYNG;
}
}
else if (m_status == SC_FD_OUT)
{
m_fade -= FD_STEP;
if (m_fade < 0)
{
m_fade = 0;
m_status = SC_STPNG;
}
}
}
}
catch(MATExceptions &e)
{
OutputDebugString(e.getAllExceptionStr().c_str());
}
try
{
m_player->Write(((ev_num + 1) % m_n_evs)*m_ev_sz, (unsigned char*)m_smps, m_ev_sz);
}
catch(MATExceptions &e)
{
OutputDebugString(e.getAllExceptionStr().c_str());
}
end = GetTickCount();
elapsed = end - begin;
if(elapsed > m_max_tm)
m_warn_msg.Format(_T("Warning! compute time: %dms"), elapsed);
else
m_warn_msg.Format(_T("compute time: %dms"), elapsed);
}
if (m_status == SC_STPNG)
Stop();
}
It seems like the buffer is not always sounding out when the stop button is clicked. I don't have any specific code for waiting for the sound buffer to finish playing before the DirectX Stop is called. Other than that the sound playback is working just fine, so at least I am initialising the player correctly and notification code is working in that respect.
Try replacing 32768 with 32767. Not by any means sure this is your issue, but it could overflow the positive short int range (assuming your audio is 16-bit) and cause a "pop".
I got rid of the pops / clicks when stopping playback, by filling the buffer with zeros after the fade out. However I still get pops when re-starting playback, despite filling with zeros and then fading back in (it is frustrating).